components icon indicating copy to clipboard operation
components copied to clipboard

mat-selection-list list-options is not updated when formControl for mat-selection-list is updated with setValue

Open EmilienRamos opened this issue 6 years ago • 3 comments

What is the expected behavior?

Checkboxes should update

What is the current behavior?

Checkboxes do not update.

What are the steps to reproduce?

editor.componenet.html

<mat-selection-list formControlName="role">
    <mat-list-option checkboxPosition="before" *ngFor="let role of roles" [value]="role.id">{{ role.attributes.name }}</mat-list-option>
</mat-selection-list>

editor.component.ts (onInit)

this.form = this.fb.group({
    role: [[...this.user.attributes.role], Validators.required],
});

this.user.attributes.role is an array of string.

I tried editor.component.ts

ngAfterContentInit() {
    this.form.patchValue({
        'role': [...this.user.attributes.role]
    });
}

I also tried editor.component.ts

ngAfterContentInit() {
    setTimeout(() => {
      this.form.patchValue({
        'role': [...this.user.attributes.role]
      });
      console.log(this.form.value.role);
    }, 1000);
}

Note that I'm getting roles with an observable : editor.component.ts

combineLatest(
      // Others observables ...
      this.jsonApi.all('roles').pipe(map(document => document.data)),
    ).subscribe(([firms, langs, roles]) => {
      // Others observables ...
      this.roles = roles;
    });

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular 7.x.x Material 7.x.x

Is there anything else we should know?

I know there's a ticket about this, but it's closed and no one replies

EmilienRamos avatar Mar 14 '19 11:03 EmilienRamos

Same, If I change the values bound, the checkboxes won't update (none is selected). Even though the array is updated correctly If I proceed to check another checkbox. I tried using the compareWith function and using objects and a simple number[] too, did not work.

SzabKel avatar Jul 16 '19 06:07 SzabKel

I could trick the component though, by introducing an extra boolean variable showBoundMatSelectionList, which is true by default and I added an *ngIf="showBoundMatSelectionList" for the mat-selection-list component. This way I can force Angular to destroy the component before I do the patchValue call using the injected protected ref: ChangeDetectorRef and reload it afterwards.

this.showBoundMatSelectionList = false;
// now notify angular to check for updates
this.ref.detectChanges();
// do your thing
// patchValues ..etc
this.showBoundMatSelectionList = true;
this.ref.detectChanges();

With this, the mat-selection-list is forced to reload the checkbox values from the supplied array. Does not matter which binding you use.

Check out: https://stackoverflow.com/questions/47496610/destroying-and-reloading-a-child-component

SzabKel avatar Jul 16 '19 07:07 SzabKel

@EmilienRamos Thank you for mentioning it's still an issue, I was getting mad at my code.

Ended up using template condition to remove the mat-selection-list from template, and add it back when the option list is updated. It's the only way to keep values checked.

I lost hours over this and I wanted to bring something constructive to help fixing this issue. At some point I was tweaking a "very" old stackblitz (angular 6) from a similar issue that uses *ngFor and [(ngModel)]. It does not reproduce the issue but maybe it's interesting to know and check. If you remove the trackBy then the issue is showing which makes sense regarding the way objects are compared without it.

Anyway here's a stackblitz reproducing the issue using a fresh link from material list documentation. Hope this helps.

Elvynia avatar Nov 09 '25 17:11 Elvynia