Как настроить таргетинг на элемент в шаблоне, загруженном на основе условия * ngIf

#html #angular

#HTML #angular

Вопрос:

Я пытаюсь настроить таргетинг на элемент в моем шаблоне с помощью переменной шаблона, но она продолжает возвращать значение undefined.

Когда вы переходите к определенному маршруту в моем приложении, мой компонент извлекает некоторые данные внутри forkJoin метода из rxjs. Это довольно большой объем данных, и у меня медленное соединение, поэтому задержка составляет 2 секунды, следовательно, у меня промежуточное состояние, в котором я показываю счетчик загрузки, который затем исчезает, когда возвращаются все данные.

Мой код шаблона выглядит примерно так…

 <div class="container" *ngIf="ready; else loading">
   <mat-horizontal-stepper labelPosition="bottom" #stepper>
      <mat-step label="Step 1"> Step 1 Content... </mat-step>
      <mat-step label="Step 2"> Step 2 Content... </mat-step>
      <mat-step label="Step 3"> Step 3 Content... </mat-step>
   </mat-horizontal-stepper>
</div>

<ng-template #loading>
   <mat-spinner></mat-spinner>
</ng-template>
  

Мой файл component.ts выглядит следующим образом…

 public ready = false;
public dataset1;
public dataset2;
@ViewChild('stepper') private myStepper: MatStepper;

constructor(private dataService: DataService) {}

ngOnInit() {
   this.fetch();
   console.log(this.myStepper); // returns undefined
}

ngAfterViewInit() {
   console.log(this.myStepper); // also returns undefined
}

public fetch() {
   return forkJoin(
      this.dataService.getDataSet1(),
      this.dataService.getDataset2()
   ).subscribe(res => {
      this.dataset1 = res[0];
      this.dataset2 = res[1];
      this.ready = true;
   }, error => console.log('Error : ', error));
}
  

Я хотел бы иметь возможность настроить таргетинг на шаговый процессор и получить общее количество шагов и использовать их для перехода к другому шагу на основе некоторой активности пользователя, но я не могу этого сделать, если я продолжаю получать undefined.
Как я могу это исправить?

Ответ №1:

Поскольку он находится внутри блока * ngIf, вы не сможете получить к нему доступ, пока все, к чему он привязан в качестве условия *ngIf, не станет истинным, поскольку эта директива не загружает элементы html до тех пор, пока оно не станет истинным, включая ваш шаговый механизм. Это означает, что вы могли бы проверить значение, в которое я добавил его во фрагменте ниже. Поскольку на этом этапе вы устанавливаете для своей ready переменной значение true, это означает, что этот раздел html также будет доступен. Это может быть хорошим моментом для вызова функции для запуска вашей логики.

Как упоминалось в комментарии, было бы идеально вызвать обнаружение изменений перед попыткой доступа. Это можно сделать, введя ChangeDetectorRef через конструктор.

 import { ChangeDetectorRef } from '@angular/core';
  

Убедитесь, что ваш импорт выше включает ChangeDetectorRef.

 constructor(private ref: ChangeDetectorRef,
            private dataService: DataService) {}

public fetch() {
   return forkJoin(
      this.dataService.getDataSet1(),
      this.dataService.getDataset2()
   ).subscribe(res => {
      this.dataset1 = res[0];
      this.dataset2 = res[1];
      this.ready = true;

      this.ref.detectChanges();
      console.log(this.stepper); // you will be able to read it here.

}, error => console.log('Error : ', error));
  

Эта статья помогает объяснить поведение dom с помощью *ngIf.

Что-то еще, что следует принять во внимание, это использование параметра настройки для ViewChild, это означает, что значение будет установлено только тогда, когда *ngIf имеет значение true. Это означает, что он будет доступен, как только он будет отображен в dom. Это хороший способ получить ссылку, и он будет выглядеть следующим образом. Для этого также изначально будет установлено значение undefined, пока не будет запущено обнаружение изменений или не будет запущено вручную.

 public myStepper: MatStepper;

@ViewChild('stepper') set content(content: MatStepper) {
    this.myStepper = content;
}
  

Таким образом, вы можете ссылаться на свой ViewChild и взаимодействовать с ним через this.myStepper .

Комментарии:

1. Я подозреваю, что в этот момент я все еще буду недоступен, если вы не вызовете cdRef.detectChanges() после this.ready = true; строки

2. Хороший вызов, позволяет принудительно обнаружить изменение, добавит его 🙂

3. Также есть способ перехватить эту ссылку с помощью setter на ViewChild

4. добавлен параметр настройки для получения ссылки 🙂