Rasik
Rasik

Reputation: 2420

Child component not listening to the changes in the parent form array

From a parent component, I am passing the form array to the child component in this way.

<form class="form-horizontal" [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <hello [myForm]="myForm"></hello>
</form>

In the parent component .ts I have fetched the data from API and bind the result to the form array.

Form array is defined as:

this.myForm = this.fb.group({
  frequency: [2, Validators.required],
  memberid: this.fb.array([]),
});

In the ngOnInit() i have called the the function callApi():

async callApi() {

    const fTitle = this.myForm.controls.memberid as FormArray;
    const response = await fetch(this.url);
    const json = await response.json();
    
    let arr = [];
    json.map((item) => arr.push(item.id));
    arr.forEach((item) => fTitle.push(this.fb.control(item)));
    console.log(this.myForm.value);

}

ngOnInit(){
    this.callApi();
}

In the child component, I am trying to filter the memberList with an memberid that is passed in the form group.

export class HelloComponent {
  public memberScheduleNotifications = [];
  public memberList = [
    { id: 2, name: 'a' },
    { id: 1, name: 'b' },
    { id: 3, name: 'c' },
    { id: 4, name: 'd' },
  ];
  @Input() myForm: FormGroup;
  ngOnInit() {
    this.memberFilter();
  }

  memberFilter() {
    const member = this.myForm.controls.memberid as FormArray;
    this.memberScheduleNotifications = member.value || [];
    this.memberList = this.memberList.map((x) =>
      this.memberScheduleNotifications.find((y) => y == x.id)
        ? { ...x, selected: true }
        : { ...x }
    );
  }
}

Component

@Component({
  selector: 'hello',
  template: `
  <div class="form-group">
      <p>child component</p>
      <pre> {{memberList | json}}</pre>
  </div>      
     `,
  styles: [`h1 { font-family: Lato; }`],
})

Here the member list is not filtered with the member id passed from the parent component. The view is displayed without the filter being applied.

The child component not listening to the changes in the parent form array, which is updated through the API call.

What I am doing wrong here?

Working demo in stackblitz

Upvotes: 1

Views: 4146

Answers (3)

You can add this little hack in hello.component.html:

<ng-container *ngIf="myForm.valueChanges | async"></ng-container>

This async pipe will trigger change detection

Upvotes: 0

Nehal
Nehal

Reputation: 13317

Because you using changeDetection: ChangeDetectionStrategy.OnPush, Angular is not running running change detection cycle, thus the latest data is not appearing in your app.component.html or in child component.

I was not able to make ChangeDetectionRef work with aync/await, but I was curious if I can find a workaround to provide you a workable solution. I made it work with Angular's 'HttpClient'. I also added an *ngIf check, so that child component doesn't load until data has been fetched from API. I also had to use ngOnChanges instead of ngOnInit in child component, to get the latest myForm value from parent.

Hope this helps you to get on right track.

Stackbliz Demo

  • removed use of ChangeDetectionRef and OnChanges, after OP mentioned use of ChangeDetectionStrategy.OnPush not necessary.

app.component.html:

    this.http.get(this.url).subscribe((data: Todo[]) => {
      this.arrHttp = [];
      const fTitle = this.myForm.controls.memberid as FormArray;
      data.map((item) => this.arrHttp.push(item.id));
      this.arrHttp.forEach((item) => fTitle.push(this.fb.control(item)));
      this.ref.detectChanges();
    });

app.component.html:

<hello *ngIf="arrHttp && arrHttp.length" [myForm]="myForm"></hello>

hello.component.ts:

  ngOnChanges(changes: SimpleChanges) {
    if (changes.myForm && changes.myForm.currentValue) {
      this.myForm = changes.myForm.currentValue;
      this.memberFilter();
    }
  }

  memberFilter() {
    const member = this.myForm.controls.memberid as FormArray;
    this.memberScheduleNotifications = member.value || [];
    this.memberList = this.memberList.map((x) =>
      this.memberScheduleNotifications.find((y) => y == x.id)
        ? { ...x, selected: true }
        : { ...x }
    );
  }

Result:

enter image description here

enter image description here

Upvotes: 0

Diego Bascans
Diego Bascans

Reputation: 1159

Your child component is not detecting the changes because you are pushing the memberlist into the FormArray, so for Angular is like you are not updating the reference you are sending to the child component.

To Angular detect the change in your child component you can:

  • Reassign the form, to detect a change in the input and refresh the component.
  • Forcing it with NgZone or ChangeDetectorRef

Upvotes: 2

Related Questions