50

I am creating a Toolbar with a selection list (checkboxes with each list item) using Angular Material 2. I just cannot figure out how to set the checkboxes before the list is displayed and then get the selected items following user interaction.

I am trying the control within a Form thinking I may need this to bind to ngModel, but this doesn't seem to help. My html so far is:

<form
  novalidate
  #areaSelectForm="ngForm">

<div>
    <mat-selection-list 
                        #areasList="ngModel"
                        [(ngModel)]="model"
                        id="areaListControl"
                        name="areaListControl"
                        (ngModelChange)="onAreaListControlChanged($event)">
        <mat-list-option *ngFor="let tta of taskTypeAreas" (click)="onCheckboxClick($event)" [value]="tta">
            {{tta}}
        </mat-list-option>
    </mat-selection-list>
</div>

</form>

This must be a well trodden path but the documentation is difficult to interpret and I cannot seem to find any suitable examples.

Any guidance very welcome please.

Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
TDC
  • 1,869
  • 3
  • 25
  • 40

3 Answers3

102

As of version 5.0.0, angular material now supports ngModel for selection list.

So the code can be simplified to

<mat-selection-list #list [(ngModel)]="selectedOptions" (ngModelChange)="onNgModelChange($event)">
    <mat-list-option *ngFor="let tta of taskTypeAreas" [value]="tta.name">
        {{tta.name}}
    </mat-list-option>
</mat-selection-list>

The release also exposes an ngModelChange event for selection list. Here is the updated stack blitz


(Original answer before Angular 5.0.0)

It appears mat-selection-list does not currently support ngModel (https://github.com/angular/material2/pull/7456), but it looks like it will be supported in the near future. In the meantime you can use a reference variable #list to grab the selected options.

// component.html
<mat-selection-list #list>
    <mat-list-option *ngFor="let tta of taskTypeAreas" [selected]="tta.selected" 
        (click)="onAreaListControlChanged(list)" [value]="tta.name">
        {{tta.name}}
    </mat-list-option>
</mat-selection-list>

Then pass in the reference variable to your onAreaListControlChanged(list) method so you can parse out the selected options.

// component.ts
onAreaListControlChanged(list){
    this.selectedOptions = list.selectedOptions.selected.map(item => item.value);
}

To select the checkboxes on load, you can use the [selected] property of each <mat-list-option>.

<mat-list-option ... [selected]="tta.selected" ...>

To do this you'll need to add another property to your array.

// component.ts
taskTypeAreas: {
    name: string;
    selected: boolean;
}[] = [
    {
        name: 'Area 1',
        selected: false
    },
    {
        name: 'Area 2',
        selected: false
    },
    {
        name: 'Area 3',
        selected: true
    },
];

This will make Area 3 be selected on load. Here is a stackblitz demoing this.


Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
LLai
  • 13,128
  • 3
  • 41
  • 45
  • You're a star LLai. I will refactor my code and give it a go. Thanks. – TDC Nov 17 '17 at 15:21
  • Your suggested approach worked a treat. Thank you very much. – TDC Nov 18 '17 at 07:43
  • @TDC glad I could help! – LLai Nov 18 '17 at 15:46
  • Tkx. I could not get the value of the initial list when an item was selected... I did not know I needed to bind the value by myself `[value]="tta.name"` to the `mat-list-option`s – M'sieur Toph' Dec 08 '17 at 09:36
  • 9
    this really should be part of the documentation – SpaceMonkey Jan 19 '18 at 00:18
  • @LLai Thank you! but can you maybe show in your stackblitz how i can use the selected options to trigger functions or change variables? – Budi Mar 28 '18 at 19:19
  • @Budi in the updated answer, you can use the `ngModelChange` event to run some logic when the selected options change.That event is ran whenever an option is selected/deselected – LLai Mar 28 '18 at 19:50
  • @LLai Thanks for the fast answere. but how i do this? sorry i am absolutley low skilled in programming. – Budi Mar 28 '18 at 20:03
  • @Budi I've added a comment to this [file](https://stackblitz.com/edit/material-selection-list-5-0-0-wlnjd9?file=app/app.component.ts). This is where you can call additional methods when the options change – LLai Mar 28 '18 at 20:22
  • @LLai Thanks but this was one of the only things that i know before :P I just dont know how i can add here if statements. i tryed with: `if (event.name === "Area 1") {console.log('Place here a function')}` but he dont fire this console.log when i select Area 1... – Budi Mar 28 '18 at 20:31
  • @Budi event is an array. You could loop through it and put your conditional in there – LLai Mar 28 '18 at 21:38
  • @LLai So `if (event.indexOf('Background Image') >= 0)` is the correct angular way? This looks to me dirty... – Budi Mar 28 '18 at 21:53
  • How do we handle reference variable while loop over ? – AnoopGoudar Apr 02 '18 at 11:15
  • Agree with @Spacemonkey – Prashant Pimpale Aug 01 '18 at 09:41
  • 2
    This is a very good answer, I made an edit to move your Angular 5 answer to the top as it is more relevant. My first thought when I started reading it was, "oh no, its not supported".. wait.. it is :D Hehehe +1 – Piotr Kula Aug 02 '18 at 08:43
  • I did the same for x lists with y items. The principal is exactly the same, but the model can be bound to selectedOptions[xList] and the selectedOptions is of type {} instead of []. This way, each list gets an entry in the object with as key the name of the list and as value an array with the selected values in that list. – Valentin Grégoire Aug 27 '18 at 13:11
  • Like seriously this answer is a life saver. I spent the couple of days looking for similar things until I found this one. – Hristo Petev Nov 26 '18 at 20:49
  • amazing answer..kudos – Shubham Arya Sep 27 '19 at 02:56
  • Can anyone please explain here... -------------------------------------------------------------------------- -------------------------------------------------------------------------- What are the methods that I can call on event to fetch the value and state of the checkbox? – Shubham Arya Sep 27 '19 at 02:57
20

@LLai's answer is correct, but you might have noticed that Angular material selection does not work when we use object as a mat-select-option [value]

To fix this, Angular material provides [compareWith] input.

@Input() compareWith: (o1: any, o2: any) => boolean

Function used for comparing an option against the selected value when determining which options should appear as selected. The first argument is the value of an options. The second one is a value from the selected value. A boolean must be returned.

For example,

list-selection.component.ts

export class ListSelectionExample {

  selectedOptions = [{name: 'Boots', id:1}];
  compareFunction = (o1: any, o2: any)=> o1.id===o2.id;

  typesOfShoes: {name: string, id: number }[] = [
    {name: 'Boots', id: 1}, 
    {name: 'Clogs', id: 2},
    {name: 'Loafers', id: 3 },
    {name: 'Moccasins', id: 4},
    {name: 'Sneakers', id:5}
  ];
}

list-selection.component.html

<mat-selection-list [(ngModel)]="selectedOptions" [compareWith]="compareFunction">
  <mat-list-option *ngFor="let shoe of typesOfShoes" [value]="shoe">
    {{shoe.name}}
  </mat-list-option>
</mat-selection-list>

<p>
  Options selected: {{selectedOptions | json}}
</p>

Find here the live Stackblitz example

Ankit Prajapati
  • 2,670
  • 2
  • 12
  • 22
  • Thanks for much for point out the compareWith thing. I've been all over looking for solutions and either no one wants a default selection or they aren't using complex objects – SleekPanther Oct 07 '20 at 23:33
13

You can use the selectionChange event emitter to trigger a controller function.

<mat-selection-list 
   id="areaListControl"
   name="areaListControl"
   (selectionChange)="onChange($event)"
>
    <mat-list-option 
       *ngFor="let tta of taskTypeAreas" 
       [selected]="tta.selected"
       [value]="tta"
    >
        {{tta}}
    </mat-list-option>
</mat-selection-list>

And in the controller:

onChange(change: MatSelectionListChange) {
   console.log(change.option.value, change.option.selected);
}
strttn
  • 2,320
  • 1
  • 18
  • 17
  • 1
    You should be able to use ViewChildren to access the matListOption instances and then loop through them to check what you need to. Note that the instances won't be available until after the view has loaded you won't be able to do this in onInit. – strttn May 11 '20 at 13:59