4
votes

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"
  }
3
Interesting.. if you just open their examples and add the (selectionChange) event it doesn't fire. run.stackblitz.com/api/angular/v1. - jpgrassi

3 Answers

5
votes

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

2
votes

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.

0
votes

Two Solutions:

  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 the old implementation. 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  }