Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 @Input from async pipe - availability in ngOnInit

Tags:

angular

On one blog I've read that:

The ngOnInit lifecycle hook is a guarantee that your bindings are readily available.

Is it true also with parameters passed using async pipe? For example:

<myComponent [myInput]="myObservableVariable | async">
 ...
</myComponent>

Will component wait for the variable to be resolved before starting up ngOnInit?

That would mean that sometimes, when data will take a while to resolve, component loading could take a long time.

like image 462
charlie_pl Avatar asked May 18 '17 10:05

charlie_pl


2 Answers

The short answer is no, the component instantiation won't be delayed and you will not get a resolved value in onInit if the observable hasn't been resolved before the first change detection cycle.

Compare the following:

  // the value will be available in onInit
  obs = Observable.from([33]);

  // the value will not be available in onInit (you'll get null)
  obs = new Observable(observer => {
    setTimeout(() => {
      observer.next(33);
    }, 1000);

    setTimeout(() => {
      observer.complete();
    }, 3000);
  });


<child-component [inputproperty]="obs"><child-component>

Here is what happens under the hood when you use async pipe:

async pipe has a method transform which is called on every change detection cycle. this method is responsible for returning the current resolved value of the observable that will be passed down to a child component. If the observable hasn't been resolved before the first change detection cycle, your child component will get null in onInit.

Then, during next digest cycle your binding will be updated as transform will return resolved values. What's interesting is that resolution of an observable can trigger the change detection cycle. You can obtain new values for the bindings in onChanges lifecycle hook.

like image 50
Max Koretskyi Avatar answered Oct 02 '22 13:10

Max Koretskyi


No, the bindings being available doesn't mean that their values have been resolved (and they may change later anyway). You can demonstrate this with a simple example:

import { Component, Input, OnInit } from '@angular/core';

import { Observable } from 'rxjs/Rx';

@Component({
  selector: 'sub-component',
  template: `<p>{{ myInput }}</p>`,
})
export class SubComponent implements OnInit {
  @Input() myInput: string;

  ngOnInit() {
    console.log('initialised with', this.myInput);
  }
}

@Component({
  selector: 'my-app',
  template: `
    <h1>Hello {{name}}</h1>
    <sub-component [myInput]="content$ | async"></sub-component>
  `
})
export class AppComponent { 
  name = 'Angular'; 
  content$ = Observable.of('Hello world').delay(5000);
}

https://plnkr.co/edit/jX7hOafuJtsWYhYrJx07?p=preview

In the console you will see initialised with null, then five seconds later the text will appear in the sub component.

If you want to know when the @Input data changes, you need to implement OnChanges.

like image 22
jonrsharpe Avatar answered Oct 02 '22 12:10

jonrsharpe