#local variable внутри *ngIf
На локальную переменную шаблона (#myVar
) можно ссылаться только внутри блока шаблона (<template>
). В случае когда необходимо сослаться извне, ссылка будет пустой. Это немного неочевидно при использовании структурных директив, таких как *ngIf
.s
<div *ngIf="true">
<p class="inside" #p> Feature </p>
<p>Class: "{{ p?.className }}"</p> <!-- Вот тут работает -->
</div>
<p>Class: "{{ p?.className }}"</p> <!-- А здесь не работает -->
Есть 2 варианта решения
@ViewChild
Используетмя декоратор @ViewChild
с сеттером. Он будет запускаться каждый раз, когда изменяется ngIf
.
export class AppComponent {
ref: ElementRef; // В шаблоне Class: "{{ ref?.nativeElement.className }}"
constructor(private cdr: ChangeDetectorRef) {}
@ViewChild('p')
set paragraph(e: ElementRef) {
this.ref = e;
// ExpressionChangedAfterItHasBeenCheckedError
this.cdr.detectChanges();
}
}
@ViewChildren
Используется декортор @ViewChildren
. Он даёт QueryList
(а-ля SelectorAll), на который следует подписаться. Список будет обновляться при каждом изменении переменной template
(ngIf включается или выключается).
export class AppComponent implements AfterViewInit, OnDestroy {
ref: ElementRef; // В шаблоне Class: "{{ ref?.nativeElement.className }}"
constructor(private cdr: ChangeDetectorRef) {}
@ViewChildren('p')
pQueryList: QueryList<ElementRef>;
/** destroy subject (pattern) */
private _destroy$ = new Subject<void>();
ngAfterViewInit(): void {
this.processChanges();
this.pQueryList.changes.pipe(
takeUntil(this._destroy$)
).subscribe(() => this.processChanges());
}
processChanges(): void {
this.ref = this.pQueryList.first;
this.cdr.detectChanges();
}
ngOnDestroy(): void {
this._destroy$.next();
this._destroy$.complete();
}
}