Структурная директива Angular с HostBinding
В проекте нужна структурная директива, которая по условию изменяет состояние элемента, на который накинута.
Покажу на примере простенькой директивы shimmer:
@Directive({
selector: '[shimmer]'
})
export class ShimmerDirective {
private _shimmer = false;
@HostBinding('class.shimmer')
public get shimmerBinding(): boolean {
return this._shimmer;
}
@Input()
public set shimmer(value: boolean ) {
this._shimmer = value;
}
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {
this.viewContainer.createEmbeddedView(this.templateRef);
}
}
Использую эту директиву вот так:
<button (click)="loading=!loading">
переключить
</button>
{{ loading }}
<p *shimmer="loading">проверка шиммера</p>
По идее, после нажатия на кнопку "переключить", на элемент p должен навеситься класс shimmer, но этого не происходит:
Выбрана именно структурная директива не просто так, нужна дополнительная обрабатываемая логика, так что способ изменить структурную директиву на атрибутивную не подходит
Ответы (1 шт):
Любая структурная директива - это просто замаскированный ng-template, то есть ваша разметка эквивалентна вот такой:
<button (click)="loading=!loading">
переключить
</button>
{{ loading }}
<ng-template [shimmer]="loading">
<p>проверка шиммера</p>
</ng-template>
Легко видеть, что любые ваши хост-биндинги тут будут применяться именно к ng-template, а не к тому элементу на который вы "вешаете" структурную директиву.
Теперь что делать нужно в таком случае. А нужно получить сохранить ViewRef и добавить класс напрямую в rootNodes через Renderer2:
private viewRef: EmbeddedViewRef;
constructor(
templateRef: TemplateRef<any>,
viewContainer: ViewContainerRef,
private renderer: Renderer2,
) {
this.viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
}
@Input()
public set shimmer(value: boolean ) {
this._shimmer = value;
for (const node of this.viewRef.rootNodes) {
if (value) {
this.renderer.addClass(node, 'shimmer');
} else {
this.renderer.removeClass(node, 'shimmer');
}
}
}
Ну и всё-таки рассмотрите возможность сделать свою директиву обычной, а не структурной. Помните, что даже обычная директива имеет доступ к элементу на который её навесили через ElementRef.

