0
votes

I am using @ViewChild in parent component and the parent component depends on child component properties. Now the child component gets data from a REST call which is done in ngoninit. In parent component i have used ngAfterViewInit so that i can get hold of the data of child component, but it seems that ngAfterViewInit gets called when first initialization takes place and any subsequent changes to view are not looked into(Called once after the first ngAfterContentChecked()). something like below

child Component

    @Component({
      selector: "academicyear-detail",
    }) 
    export class AcademicYearDetail  {
      currentYear: string;
      ngOnInit() {
        //do a rest call
        //assign when data arrives
        currentYear = data.currentAcademicYear;
      }
    }

Parent Component

      @Component({
         selector: "testProject",
         templateUrl: "app/partials/Main.html",
         directives: [AcademicYearDetail] 
      })
      export class AppComponent { 
        @ViewChild(AcademicYearDetail) acadYearDetail:AcademicYearDetail;
        ngAfterViewInit() {
          this.getChildProperty();
        }
        getChildProperty() {
          console.log(this.acadYearDetail.currentYear);
        }
      }

Now the question is

  1. Is there a way to check and wait for child component to be fully rendered?
  2. Is it better to take care of such scenarios in the parent component, child data load and subsequent logic in parent? But in this way we end up totally duplicating child component data load in all parent components
  3. Is there any better way to do this
3

3 Answers

1
votes

You should make use of an Output emitter on the child component.

   @Component({
      selector: "academicyear-detail",
    }) 
    export class AcademicYearDetail  {

     @Ouput()
     currentYear = new EventEmitter<string>();

     currentYear: string;
     ngOnInit() {
        //do a rest call
        //assign when data arrives
        this.currentYear.emit(currentAcademicYear);
    }

Then in the template for the parent component set the variable.

<academicyear-detail (currentYear)="acadYearDetail.currentYear = $event"></academicyear-detail>
1
votes

You should move logic like api calls into a service and store your resulting data inside it using a rxjs/Subject or rxjs/BehaviorSubject.

Or if you want to go even further store your data in a redux store like @ngrx/store together with @ngrx/effects which has a great performance benefit due to immutable data and the use of ChangeDetectionStrategy.OnPush. Also your application state is definitly at any point of time which reduces side effects and improves testability.

This will keep your logic away from components and the service can be used in any component which need the shared data.

More about services to share application data can be found in the tutorial section of the angular docs.

0
votes

You could probably do it more easily if you let the parent component get the superset of your data. Master-Detail pattern essentially means that your children component just receives the data it needs and the parent component can cache your full data set till you need it.

To do this you can make the rest call in your parent.

@Component({
         selector: "testProject",
         templateUrl: "app/partials/Main.html",
         directives: [AcademicYearDetail] 
       })
export class AppComponent { 
  constructor(private http: HttpClient) { }
  ngOnInit() {
     //do a get for everything
  }      
}

In your child component you just need to bind.

 @Component({
      selector: "academicyear-detail",
    }) 
    export class AcademicYearDetail  {
     currentYear: string;
     @Input() data;
     ngOnChanges() {
      //bind data to be used where ever in child.
    }