codewarrior
codewarrior

Reputation: 61

Unable to share Observable Subject service between grandchildren components in Angular 5

I have an application where I'm using the angular cdk stepper and I'm trying to share data between a common grandchild component in both of the child step components.

The first child passes the data to populate the grandchild, but the problem seems to be that the stepper initializes both components at the same time, and since the first child has to make a service call to populate the grandchild, the second child's child is initialized with incomplete data.

parent.component.html:

<app-custom-stepper>
<cdk-step [stepControl]="frmStep1" #step1="cdkStep">
<ng-template cdkStepLabel>Step1</ng-template>
    <form #frmStep1="ngForm">
        <child1 [myData]="myData"></child1>
    </form>
    <button cdkStepperPrevious>Prev</button>
    <button cdkStepperNext>Next</button>
</cdk-step>
<cdk-step cdkStep #step2="cdkStep">
    <form #frmStep2="ngForm">
        <child2></child>
    </form>
    <button cdkStepperPrevious>Prev</button>
    <button cdkStepperNext>Next</button>
</cdk-step>

parent.service.ts

export class ParentService {
  myData:Subject<any> = new Subject<any>();

  setMyData(data: any) {
     this.myData.next(data);
  }

  getMyData(): Observable<any> {
     return this.myData.asObservable();
  }
}

child1.component.html:

<sharedGrandchild [myData]="myData"></sharedGrandchild>

child2.component.html

<sharedGrandchild></sharedGrandchild>

grandchild.component.ts:

@Input() myData:MyData;
ngOnInit() {
  if(this.myData) {
    //call service that populates data, this only happens on the page
    //step of the stepper
    this.parentService.setMyData(response);
  } else {
    this.parentService.getMyData()
      .subscribe( myData => {
        this.myData = myData 
      }
   };

Things I've tried:

Using BehaviorSubjects and Subjects (like in the above code) Using a switch to delay initialization of the second child component Trying to use other lifecycle hooks to initialize the subscribe for the second childs component.

The issue is timing, that request the first child fires via the grandchild takes too long to return, so the grandchild present in the second child is subscribing to an object that is only partially present. This never seems to get updated. So the first child on the page loads fine since it's hitting the service directly, the second child on the second page of the stepper has incomplete data due to being initialized at the same time.

Upvotes: 0

Views: 188

Answers (1)

Sunil
Sunil

Reputation: 11243

You should follow these steps to ensure the data is being shared asynchronously-

1.Use BehaviorSubject to store the last set values.

   myData:Subject<any> = new BehaviorSubject<any>(null);

2.Read and set the data in function ngAfterViewInit instead of ngOnInit

@Input() myData:MyData;
ngAfterViewInit() {
  if(this.myData) {
    //call service that populates data, this only happens on the page
    //step of the stepper
    this.parentService.setMyData(response);
  } else {
    this.parentService.getMyData()
      .subscribe( myData => {
        this.myData = myData 
      }
   };

3.Ensure that ParentService is provided in the common module or Main Module or root module.

Upvotes: 1

Related Questions