Angular - Retrieve array of child components upon initialization

Currently, I am utilizing Angular 10 for a personal side project. Within this project, there is a specific page that, upon loading, fetches a collection of entries (in this case, primarily spells) from the server and displays one child component (<app-spell> component) per entry on that particular page. Users have the option to specify a URL parameter named name. If they do so and an entry exists where the condition spell.name === name holds true, then I aim to automatically scroll to the component associated with that specific entry.

Currently, I am facing a roadblock in gaining access to a list of these child components. My initial plan was to utilize ViewChildren to retrieve the list of child components. However, I have encountered challenges as there aren't any suitable lifecycle hooks where accurate values are available. Even when attempting to use document.querySelector (knowing in advance that there will be a component with the ID "Arawns-Changing-Scales"), I seem to be unable to obtain the necessary HTML elements:

  1. ngAfterContentInit: You cannot generally access ViewChildren within this hook. Calls to document.querySelector result in null.
  2. ngAfterViewInit: At this point, the entries have not been fetched yet, and the child components have not been initialized. As a result, ViewChildren returns an empty QueryList, and document.querySelector also returns null.
  3. ngOnInit: The child components are not yet initialized at this stage. Therefore, ViewChildren returns an empty QueryList, while document.querySelector returns null.

Following this, I have exhausted what seems to be appropriate lifecycle hooks. I have attempted the solution suggested here by placing my attempt to access ViewChildren inside a setTimeout callback. However, this yielded the same results as without using a timeout at all. Other resources I came across utilized clicks or other user-triggered events to trigger a scroll, which does not suit my requirements.

Is there a way to access HTMLElements/Components during the page loading process?

## 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.
  }
}

Answer №1

If you need to keep track of SpellComponents in Angular, you can utilize the ViewChildren annotation. This annotation provides access to a QueryList that gets populated during the ngAfterViewInit hook. In scenarios where spells are fetched asynchronously and not available in time for ngAfterViewInit, you can still monitor changes using the Observable "changes" property of QueryList.

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

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

To distinguish between different spell components, you can use the 'name' Input property in your SpellComponent class along with ElementRef for accessing DOM properties like scrollIntoView().

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

  constructor(public element: ElementRef) {}

  ngOnInit() {}
}

By subscribing to changes in QueryList, you can perform actions on specific spell components based on their names. For example, scrolling a component into view.

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

Feel free to check out a live demo showcasing this implementation here.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Retrieve the content from a specific cell within the table in the current row using Javascript

I am working with a dynamically generated table where each row has a button. My goal is to retrieve the text from a specific cell when the button in that particular row is clicked. For example: Here is an example of my table structure: <table> ...

When attempting to invoke a view model function of a component from external sources in Aurelia with TypeScript, an error message may appear stating "Property of undefined cannot be read

My expectation was that using view-model.ref would allow me to access the viewmodel of a component from outside the component and call its functions. The following code leads to an error message: "Cannot read property 'LoadForAdditionalLocations&apos ...

Using TypeORM: Implementing a @JoinTable with three columns

Seeking assistance with TypeORM and the @JoinTable and @RelationId Decorators. Any help answering my question, providing a hint, or ideally solving my issue would be greatly appreciated. I am utilizing NestJS with TypeORM to create a private API for shari ...

Use Typescript to access and utilize the value stored in local storage by using the

Trying to retrieve the language setting from localHost and implement it in a translation pipe as shown below: transform(value: string): string {... localStorage.setItem("language", JSON.stringify("en")); let language = JSON.parse(loca ...

Converting a cast method into a function in Typescript

With some experimenting on WebRTC, I found that the ondatachannel callback takes a function, but I'm wondering if it's possible to use a method of a Typescript class instead. Can someone please assist me with this? export class MainComponent imp ...

Is AgGrid's onGridReady function compatible with Angular's lifecycle hooks?

When setting the data source for the grid, I initialize it like this: @Input() id: number; public gridOptions: GridOptions = { ... onGridReady: (event) => this.onGridReady(event) }; public onGridReady(params): void { const dataSource = { get ...

Steps to deactivate an angular material component on version 2.0.0-beta.5

Recent updates have led to an error in my code: Error at /Users/asaylor/Desktop/RevenueIQ/website/aot/node_modules/@angular/material/typings/index.ngfactory.ts:4236:30: Property 'disabled' does not exist on type 'MdCheckbox' I am enc ...

Storing the ngFor output in a variable with Angular 2

I've been exploring how to achieve this functionality using AngularJS and was successful with the following code: item in (filteredList.sup = (nozzles | rangeFilter: { flow: calc.q.tempFlow, pressure: calc.pressure } | orderBy: 'pressao')) ...

After migrating my project from Angular 2 to Angular 5, the typings are completely chaotic

Recently, I encountered issues while compiling my project after updating to angular 5. The compilation process seems to be failing due to some errors in typing. Initially, the project utilized typings, but I attempted to switch to the modern approach by us ...

What causes a merge conflict to occur within a React and Typescript project?

I stumbled upon this code snippet in our React/Typescript - JSX/TSX project with an unresolved Git merge conflict. Interestingly, the code successfully compiles and runs in the browser! Upon transpilation, I noticed that the top element is recognized as t ...

Add an additional boolean attribute called `_ro` as a suffix to a property

Is it possible to add an additional property using a property decorator? The current approach I am taking seems to be incorrect. const RoProp = () => { return <T>(target: T, memberName: keyof T) => { const roPropName = `${String(memberNa ...

When utilizing the useRef hook in Material-UI, an error may arise stating: "The property 'value' is not found on the type 'never'."

Currently, I am utilizing material UI to construct a login and registration page. In the process, I am leveraging the useRef hook to retrieve a reference instance for a TextFiled, and using xxxRef.current.value to access the input value. Despite being abl ...

Utilizing a Map with Angular's ngFor

Currently, I am working with Ionic 3, which utilizes Angular and TypeScript. My goal is to use ngFor with a Map type in my project. Below is what I have implemented so far: interface IShared_Position { lat: number; lng: number; time: number; ...

What are the benefits of using index signature `{[key: string]: any}` over the `object` type declaration?

As I delve into learning TypeScript, I frequently encounter the use of index signatures in function parameters. For example, export function template(resources: {[key: string]: any}) Given that the value type is any, what is the utility of this type? Is t ...

Include [op.and] in the Sequelize query object

I want to construct my Sequelize query object based on input parameters. It functions well for database keys, as shown in the example below: let query = { where: { id: 5 } } if (inputName) { query['where']['name'] = { nam ...

Send a Date Object through an Event Emitter to be used in a Date Picker

I created a personalized Date Picker Child Component, and when the onDateChange event occurs, I intend to send an event to the parent component. @Output() selectedDateChange = new EventEmitter<Date>(); onDateChange($event) { this.selectedDateCha ...

Using jQuery functionality on rendered results in Angular

Currently, I'm working on truncating a string based on its character count and then adding an ellipsis if the number of characters exceeds 21. I've included a truncate class in the HTML div using jQuery. Everything seems to be functioning correc ...

Avoid loading global stylesheet in lazy loaded module in Angular 8

I'm facing an issue with conflicting style rules in my project. The global styles.scss file has some specific style rules set, and all code is contained within app.module. When attempting to create a lazy-loaded module, I discovered that certain Angul ...

Comparing Twitter Bootstrap and PrimeNg: The Ultimate Guide for UI Development in Angular 2

After using twitter bootstrap for a while, I transitioned to Angular 2 and began exploring different CSS libraries. I discovered options like ngSemantic, Angular2 Material, ng-bootstrap, and ng2-bootstrap, but primeNg caught my eye with its wide range of c ...

The functionality of d3 transition is currently non-existent

I've encountered an issue with my D3 code. const hexagon = this.hexagonSVG.append('path') .attr('id', 'active') .attr('d', lineGenerator(<any>hexagonData)) .attr('stroke', 'url(#gradi ...