I am facing a situation I don't understand. I have a parent component (app.component in the example) that gets its data from an API as an observable
This data is then passed down to the child (hello.component) using the async pipe.
That child then receives the input, but when ngOnInit runs in the child, the input is null
.
I don't understand why, and I don't know how to make it so that the input is the actual returned value from the API instead. The call to detectChanges()
in app.component was a desperate attempt to trigger change detection in the child but that doesn't re-run ngOnInit so it's kinda moot. I left it there because that's how the actual code I'm working with looked like.
I know this code is terrible.I didn't write it. Unfortunately, the component I'm working with is like that and I can't refactor it because, you guessed it, there are no unit tests. I'm working on cleaning it all up, but for now I have to reuse that component the way it is.
Stackblitz: https://stackblitz.com/edit/angular-ivy-rw1xte?devtoolsheight=33&file=src/app/hello.component.ts
// app.component.ts
import { ChangeDetectorRef, Component, VERSION } from "@angular/core";
import { interval, Observable, of } from "rxjs";
import { mapTo, tap } from "rxjs/operators";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
public name$: Observable<any> = of({});
constructor(private cdRef: ChangeDetectorRef) {}
public ngOnInit() {
this.name$ = interval(3000).pipe(
mapTo(() => {
first: "Eunice";
}),
tap(() => this.cdRef.detectChanges())
);
}
}
<!-- app.component.html -->
<hello [name]="name$ | async"></hello>
<p>
Start editing to see some magic happen :)
</p>
// hello.component.ts
import { Component, Input } from "@angular/core";
@Component({
selector: "hello",
template: `
<div *ngIf="name">
<h1>Hello {{ this.name.first }}!</h1>
<h1>{{ greetings }}</h1>
</div>
`,
styles: [
`
h1 {
font-family: Lato;
}
`
]
})
export class HelloComponent {
@Input() name: { first?: string }
public greetings: string = "";
public firstName: string = "";
public async ngOnInit() {
console.log('name:',this.name); // name: null
if (this.name) {
// this conditional is always false because this.name is always null
// and so this never runs.
console.log("got name");
this.firstName = this.name.first || "fallback";
this.greetings = await new Promise(resolve =>
resolve("how do you do, ${firstName}?")
);
}
}
}