I have the below screen with 4 buttons I am using to complete a tutorial on passing data between components (Sharing Data Between Angular Components - Four Methods).
I am having trouble with the @ViewChild section. There is a routerlink below the four button which populates on each button click the relevant components.
When I click the @ViewChild button, the Component shows but the message is not populated, it is empty.
When I click the @ViewChild button a second time the message is populated as expected.
I'm sure this is not the intended outcome of @ViewChild. So I need to know why it is doing this and how to correct it so the message is visible on the first click.
The tutorial says: 'we need to implement the AfterViewInit lifecycle hook to receive the data from the child' which I have done.
Below are my Parent and Child code.
Parent
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChildComponent } from './child/child.component';
@Component({
selector: 'app-data-view-child',
templateUrl: './data-view-child.component.html',
styleUrls: ['./data-view-child.component.scss']
})
export class DataViewChildComponent implements AfterViewInit {
@ViewChild(ChildComponent, { static: false }) child;
constructor() { }
message: string;
ngAfterViewInit() {
this.message = this.child.message
}
}
Child
import { Component } from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent {
message = 'Hola Mundo!';
constructor() { }
}
Parent Template
<div class="parent-card">
<h1>I am the Parent component</h1>
<h2 class="tac">Message: {{ message }}</h2>
<app-child></app-child>
</div>
Please let me know why the message is not available on the first click.
the issue here is a violation of unidirectional data flow which is preventing change detection from triggering properly. Unidirectional data flow means that data needs to flow up or down your component tree during change detection. Instantiating the component tree is data flowing down, but then trying to pull data out of children during instantiation means you're violating the principle by pulling it back up. Angular does not allow this as it could create an infinite change detection loop where the parent triggers child, child triggers parent, and so on.
If you're running in dev mode, you'll see an error like "expression changed after checked"... this means that something in your child has caused a change in your parent during your parents change detection cycle.
you could do a lot of hacky things like this:
ngAfterViewInit() {
setTimeout(() => {
this.message = this.child.message;
});
}
which will solve your issue as the timeout will trigger another round of change detection in the parent.
but the issue here is that you should not be trying to do this and shows poor architecture. Data should come to parents in response to events, not instantiation
demo blitz: .https://stackblitz.com/edit/angular-zzc7r6?file=src/app/app.component.ts