async pipe sends 'null' value to the child component
Asked Answered
T

5

17

I want to pass a value to the child component. this value is an Observable, so I use async pipe.

<child [test]="test$ | async"></child>

test$ is just a normal observable variable, that emits values after a period of time (3000 ms), simulating an API request to the server.

this.test$=timer(3000).pipe(
      mapTo("value")      
 )

in child component, I just want to check test value

@Input() test: any;

constructor(){
    console.log("child/test", this.test); //null
    setTimeout(()=>console.log("child/test (timeout)", this.test),4000) //value

   if(this.test){
     //maintain and check `this.test`
     //this code will not run, because at this point `this.test` is null.
     //we don't know the exact time that `this.test` will have a value
     //this causes that `this.test` is wrong

      this.checked=true 
     }
  }

<div *ngIf="checked">{{test}}</div>

I don't want to change the type of test to be Observable and subscribe to it. I want to receive the final value directly. and I don't want to modify the edit component at all.

using ChangeDetectorRef to manually trigger the change detector is not

@Input() test$:Observable

constructor(){
  this.test$.subscribe(v=>this.test=v)
}

I also made this stackblitz to check the value changing among all the compoonent's hooks.

Torre answered 8/5, 2020 at 14:17 Comment(0)
F
24

Much easier solution:

(test$ | async) || defaultTestValue
Festival answered 26/1, 2022 at 16:27 Comment(0)
I
19

async pipe will return null when no value is emitted by Observable yet. So, the value of test in child component is:

  • undefined in constructor because @Input() variables are not assigned at this state
  • null after that (e.g. first onChanges hook or onInit hook`) when no value is emitted by the Observable
  • value when the Observable emit new value

Now, you should either create child component only when test variable is not null with *ngIf, or handle correctly the state of child component with nullable test (e.g. Add a progress bar when test is null). The choice is up to you.

Igal answered 8/5, 2020 at 15:17 Comment(0)
I
15

app.component.html

<ng-container *ngIf="(test$ | async) as test; else defaultTmpl">
    <child [test]="test"></child>
</ng-container>
<ng-template #defaultTmpl>Default Template</ng-template>

For more details please take a look: https://ultimatecourses.com/blog/angular-ngif-async-pipe

Insurer answered 8/5, 2020 at 14:38 Comment(4)
this is a very good idea, but actually I want to send test directly to the child component even it don't have a value yet. this makes the child component display a default template until test get it's value without the need to implement a default template myself in my parent component.Torre
Okay, another solution is to use ngOnChanges() hook to track changes in your @Input(). It would minor changes in child component however you can properly track input params. Take a look please: dev.to/nickraphael/…Insurer
@Insurer - Please note that he doesn't want to modify the child component at all.Ovi
@ConnorsFan - anyway he has to make some adjustments due to the fact that he wants to apply switching between default template and template with data in the child componentInsurer
D
4

You can use the rxjs operator startWith to set an initial value for your observable. If incase you want an custom starting value.

this.test$=timer(3000).pipe(
  mapTo("value"),
  startWith(0))
Dardan answered 16/5, 2022 at 4:7 Comment(0)
H
3

You can create variable inside your template like this:

test$ | async; let test;

then later you can check:

*ngIf='test'

if it is true then you can render your child component.

Humfrey answered 8/5, 2020 at 14:24 Comment(1)
Inside your parent component. Then you use simply ngIf to check if test is anything else than undefined or null and if so, you pass it to child component.Humfrey

© 2022 - 2024 — McMap. All rights reserved.