Angular - 在页面加载时访问 child 组件列表

Angular - Access list of child components on page load

我正在使用 Angular 10 作为我的 side-project。我有一个页面,在页面加载时,从服务器检索条目列表(在本例中特别是 spells),并在该页面上为每个条目呈现 1 child-component(<app-spell> 组件) .用户可以(这是可选的)指定一个名为 name 的 url 参数,如果他这样做并且有一个条目是真的 spell.name === name 那么我想滚动到组件该条目。

我一直在努力寻找可以访问这些 children 列表的地方。 Ì 计划使用 ViewChildren 来获取 child-component 的列表。但是,似乎没有任何适当的生命周期挂钩具有准确的值。此外,即使在尝试使用 document.querySelector 时(我提前知道我将拥有一个 ID 为“Arawns-Changing-Scales”的组件),我似乎也无法获得所需的 HTML 元素:

  1. ngAfterContentInit :一般情况下,您不能在此挂钩中访问 ViewChildrendocument.querySelector-调用 return 为空。
  2. ngAfterViewInit:此时尚未获取条目,也未初始化 child-component。因此 ViewChildren return 是一个空的 QueryList。 document.querySelector` returns null
  3. ngOnInit: child-component尚未初始化。因此 ViewChildren return 是一个空的 QueryListdocument.querySelector returns null

然后我就没有合适的生命周期钩子了。我已经尝试了建议 并将我尝试访问我的 ViewChildren 到 setTimeout 回调中,这只是 returned 与根本不使用超时相同。我发现的其他问题是使用点击或其他 user-triggered 事件来触发滚动,但我不是这种情况。

在加载页面期间,我在哪里可以获得某种 HTMLElements/Components?

## spells.component.html

        <!-- Spells -->
        <div *ngFor="let spell of spells; let i = index">
            <app-spell
            #spellElements
            [id]="spell.name.replaceAll(' ', '-').replace('\'', '')"
            [index] = "i"
            [cardData] = "spell"
            *ngIf="hasSelectedClasses(spell)"
            ></app-spell>
        </div>
## spells.component.ts

@Component({
  selector: 'app-spells',
  templateUrl: './spells.component.html',
  styleUrls: ['./spells.component.scss']
})
export class SpellsComponent implements OnInit, AfterContentInit, AfterViewInit {
  panelIsOpenArray: boolean[];
  spells: SpellObject[];

  @ViewChildren("spellElements") spellElements: QueryList<any>;

  constructor(
    private spellService: SpellService,
    public routingService: RoutingService,
    private route: ActivatedRoute,
  ) { }

  ngOnInit(): void {
    this.spellService.list().pipe(first()).subscribe(
      (spells: SpellObject[]) => {
        this.spells = spells;
        this.panelIsOpenArray = [];
        for (let spell of spells){
          this.panelIsOpenArray.push(true);
        };

        console.log("After init");
        this.scrollToSpellInUrl();
      },
      error => this.routingService.routeToErrorPage(error)
    );
  }
  ngAfterViewInit(): void{
    setTimeout(_=> {
      console.log("After view init in timeout");
      this.scrollToSpellInUrl()
    });

    console.log("After view init normal")
    this.scrollToSpellInUrl();
  }

  ngAfterContentInit(): void{
    console.log("After content init");
    this.scrollToSpellInUrl();
  }

  scrollToSpellInUrl(){
    console.log(this.spellElements);
    console.log(document.getElementById("Arawns-Changing-Scales");
    console.log(document.querySelector("#Arawns-Changing-Scales"));
    return;
  }

  hasSelectedClasses(spell: SpellObject): boolean{
    //For all intents and purposes, based on some other data and this
    //data this function returns sometimes true sometimes false, more 
    //is not relevant for this question.
  }
}

您可以使用首先填充在 ngAfterViewInit 挂钩中的 ViewChildren annotation to watch for SpellComponents. The annotation exposes a QueryList。在您的情况下,您正在以异步方式获取法术,并且您无法及时获取 ngAfterViewInit 挂钩。然而,Querylist 有一个 changes 属性,它是一个 Observable,它会在任何时候在模板中添加、删除或移动 SpellComponent 时发出。

  @ViewChildren(SpellComponent)
  public spellsComponents: QueryList<SpellComponent>;

  public ngAfterViewInit() {
    this.spellsComponents.changes.subscribe(queryList => {});
  }

在每次发出时,我们都可以在 queryList 参数中进行搜索。但在这是我的 SpellComponent 之前:

export class SpellComponent implements OnInit {
  @Input() name: string;

  constructor(public element: ElementRef) {}

  ngOnInit() {}
}

我们将使用名称 Input 来标识我们的拼写组件。我注入 ElementRef as it will able us to use the element.scrollIntoView 方法。

this.spellsComponents.changes.subscribe(queryList => {
  const target = queryList.find(cmp => cmp.name === "foo");
  target.element.nativeElement.scrollIntoView();
});

完成。这是一个现场演示 https://stackblitz.com/edit/angular-mmjzwj-jffmyr?file=app.module.ts