Konrad Dziurdź
Konrad Dziurdź

Reputation: 777

Angular Material MatChipList does not fire change, nor select event

I am trying to display a list of chips with predefined values to a user, and let user select some of them. My goal is selection of multiple chips by clicking, with reaction in accordance.

The problem is, that not a single event is emitted.

What I am doing wrong?

manage-roles.component.html

<mat-chip-list [selectable]="true" [multiple]="true"
  (change)="onChange($event)">
  <mat-chip *ngFor="let role of possibleRoles"
            [selectable]="true" (selectionChange)="onChipSelect($event)">
    {{role}}
  </mat-chip>
</mat-chip-list>

manage-roles.component.html

@Component({
  selector: 'hr-manage-roles',
  templateUrl: './manage-roles.component.html'
})
export class ManageRolesComponent implements AfterViewInit {
  @ViewChild(MatChipList) chipList: MatChipList;
  possibleRoles: string[] = Roles; // some const

  ngAfterViewInit(): void {
    this.chipList.chipSelectionChanges.subscribe(change => {
      console.log(change); // not fires
    })
  }

  onChange(change: MatChipListChange){
    console.log(change); // not fires
  }

  onChipSelect(change: MatChipSelectionChange) {
    console.log(change); // not fires
  }
}

package.json

  "dependencies": {
    "@angular/animations": "^5.0.0",
    "@angular/cdk": "^5.0.0",
    "@angular/common": "^5.1.0",
    "@angular/compiler": "^5.1.0",
    "@angular/core": "^5.1.0",
    "@angular/flex-layout": "^2.0.0-beta.10-4905443",
    "@angular/forms": "^5.0.0",
    "@angular/http": "^5.1.0",
    "@angular/material": "^5.0.0",
    "@angular/platform-browser": "^5.0.2",
    "@angular/platform-browser-dynamic": "^5.0.0",
    "@angular/router": "^5.0.0",
    "core-js": "^2.4.1",
    "rxjs": "^5.5.2",
    "zone.js": "^0.8.14"
  },
  "devDependencies": {
    "@angular/cli": "1.5.2",
    "@angular/compiler-cli": "^5.0.0",
    "@angular/language-service": "^5.0.0",
    "@types/jasmine": "~2.5.53",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "~3.2.0",
    "jasmine-core": "~2.6.2",
    "jasmine-spec-reporter": "~4.1.0",
    "karma": "~1.7.0",
    "karma-chrome-launcher": "~2.1.1",
    "karma-cli": "~1.0.1",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~3.2.0",
    "tslint": "~5.7.0",
    "typescript": "~2.4.2"
  }

Upvotes: 5

Views: 15045

Answers (3)

Neo Liu
Neo Liu

Reputation: 419

There are 2 solutions if you want to use (change) on mat-chip-list:

  1. Use MatChip's selectViaInteraction(), Stackblitz
<mat-chip-list (change)="onChange($event)">
  <mat-chip #chipRef="matChip" (click)="chipRef.selectViaInteraction()">
    SOME TEXT HERE
  </mat-chip>
</mat-chip-list>
  1. Use MatChip's toggleSelected(true)
<mat-chip-list (change)="onChange($event)">
  <mat-chip #chipRef="matChip" (click)="chipRef.toggleSelected(true)">
    SOME TEXT HERE
  </mat-chip>
</mat-chip-list>

For anyone who is curious about 'why' the change event is not fired in your code. Here below is the explanation:

From on MatChipList's source code, we can see the change event emitter on MatChipList will be triggered only if MatChipList received an MatChipSelectionChange with isUserInput set to true from its descendent MatChip.

784      if (event.isUserInput) {
785        this._propagateChanges();
786      }

And if you take a look at the MatChip source code you will find out that the isUserInput on MatChipSelectionChange will be set to true only when you call MatChip's toggleSelected(true) or selectViaInteraction().

369  selectViaInteraction(): void {
370    if (!this._selected) {
371      this._selected = true;
372      this._dispatchSelectionChange(true);
373      this._changeDetectorRef.markForCheck();
374    }
375  }
376
377  /** Toggles the current selected state of this chip. */
378  toggleSelected(isUserInput: boolean = false): boolean {
379    this._selected = !this.selected;
380    this._dispatchSelectionChange(isUserInput);
381    this._changeDetectorRef.markForCheck();
382    return this.selected;
383  }

Upvotes: 0

The MatChip selection has to be handled manually... so in order selectionChange emit a value you have to manually change mat-chip component selected state. You can change it in two ways:

  1. Bind a selected value to matChip component like this, you can modify that Input selected value for example in the click event function callback: ( in this example I would have a property selected on role object ):

    <mat-chip *ngFor="let role of possibleRoles" [selected]="role.selected" (selectionChange)="onChipSelect($event)" (click)="roleClicked(role.id)"> {{role}} </mat-chip>

    Doing like this in your roleClicked function you would have to calculate the new state of the roles selected property.

  2. Get the reference to the MatChip component and set Selected imperatively. I would not post an example of this because I don't recommend this options, but you can use it in some cases...

Hope this helps.

Upvotes: 2

jpgrassi
jpgrassi

Reputation: 5762

Tried using (click) instead of selectionChange event and it worked. I had several similar issues while working with material components. Anyway, if you are happy with just the data on the Chip and not the actual component, you can achieve what you want doing this:

<mat-chip-list class="mat-chip-list-stacked" [selectable]="true" [multiple]="true">
  <mat-chip *ngFor="let chip of availableColors" (click)="selectMe(chip)">
    {{chip.name}}
  </mat-chip>
</mat-chip-list>

  public selectMe(event: any) {
    console.log(event);
  }

Demo here

Upvotes: 8

Related Questions