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:
ngAfterContentInit
: You cannot generally accessViewChildren
within this hook. Calls todocument.querySelector
result in null.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 emptyQueryList
, anddocument.querySelector
also returns null.ngOnInit
: The child components are not yet initialized at this stage. Therefore,ViewChildren
returns an emptyQueryList
, whiledocument.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.
}
}