SBB
SBB

Reputation: 8970

Angular2 Async issue with child components

I have a parent component which is loading data from a database. This data comes from my service which returns a promise.

Component:

ngOnInit() {
    this._massEmpService.fetchFilterFields()
        .then(results => {
            this.fields = {
                areas: results['areas']['options'],
                silos: results['silos']['options'],
                departments: results['departments']['options'],
                locations: results['locations']['options'],
                segments: results['segments']['options'],
                roles: results['roles']['options']
            };
        });
}

Service:

fetchFilterFields() {
    return new Promise((resolve, reject) => {
        this._http.get(this.baseUrl + '/fetchImportFields', { "headers": this.headers })
            .map((result: Response) => result.json())
            .subscribe((results) => resolve(results.data));
    });
};

I am trying to pass my this.fields data to a child component but when I try and use the data in the child component, it doesn't appear to have access to it.

Child Component:

@Input() fields: any;

ngAfterViewInit() {
    console.log(this.fields); // No data
}

Parent HTML:

<app-import [fields]="fields"></app-import>

I believe this is an async issue but I thought using the afterViewInit may have solved it. Any thoughts?

Upvotes: 1

Views: 718

Answers (2)

Nehal
Nehal

Reputation: 13307

My suggestion would be to use shared service with Subject and Observable so that the child component variable can subscribe to the data stream.

With the data binding way, child component is loaded with parent, so when Promise returns data, child component is not aware of it, hence it needs to be reinitialized to get the data. Observable and subscription erases that problem.

I have created this Plunker demo that returns promise data to a child component( with added extra 2000ms of delay). If it works for you, you can easily implement it in you code :)

parent.component.ts:

constructor(private appState: AppState){ }

  ngOnInit(){
    this.getData();
  }

  getData(): void {
    this.appState
        .fetchFilterFields()
        .then(data => {
          // console.log(data)
          this.appState.setData(data);
        });
} 

service.ts:

  private headers = new Headers({'Content-Type': 'application/json'});
  private apiUrl = 'api/data';

  // Observable string source
  private dataStringSource = new Subject<string>();

  // Observable string stream
  dataString$ = this.dataStringSource.asObservable();

  constructor(private http: Http) { }

  public setData(value) {
    this.dataStringSource.next(value);
  }

  fetchFilterFields() {
    console.log(this.apiUrl);
    return this.http.get(this.apiUrl)
               .delay(2000)
               .toPromise()
               .then(response => response.json().data)
               .catch(this.handleError);
  }

  private handleError(error: any): Promise<any> {
    console.error('An error occurred', error); // for demo purposes only
    return Promise.reject(error.message || error);
  }

child.component.ts:

fields: any;

constructor(private appState: AppState){
    // this.mylist = this.appState.get('mylist');

    this.appState.dataString$.subscribe(
      data => {
        // console.log("Subs to child" + data);
        this.fields = data; 
      });

}

Upvotes: 1

Kai
Kai

Reputation: 2599

Use ngOnChanges(), which is called every time the value of an input property changes.

Upvotes: 0

Related Questions