How to make make single checkbox select from mat-selection-list. Similar to radio button which accepts one value from group of values.
问题:
回答1:
Components >= 9.1.0
Selection list now supports a single selection mode directly. Enable it with the multiple input set to false.
<mat-selection-list [multiple]="false">...
Components < 9.1.0
You have to set SelectionModel for selectedOptions property of component reference on init.
@ViewChild(MatSelectionList, {static: true})
private selectionList: MatSelectionList;
ngOnInit() {
this.selectionList.selectedOptions = new SelectionModel<MatListOption>(false);
}
MatSelectionList api
This way you can define initially selected values too.
export declare class SelectionModel<T> {
constructor(_multiple?: boolean, initiallySelectedValues?: T[], _emitChanges?: boolean);
}
GitHub api
回答2:
What you'd need to do is:
- Listen for
selectionChange
- Clear all with
deselectAll()
method - Set it as selected again
I modified the original example from the API page, added a ViewChild for the selection list and subscribe to the selectionChange
event.
ngOnInit(){
this.shoes.selectionChange.subscribe((s: MatSelectionListChange) => {
this.shoes.deselectAll();
s.option.selected = true;
});
// WARNING: Don't attempt to do the following to select the value
// it won't trigger the value change event so it will remain unchecked
// this.shoes.selectedOptions.select(s.option);
// If interested source is here :
// https://github.com/angular/material2/blob/fa4ddd0a13461b2f846e114fd09f8f4e21b814b1/src/lib/list/selection-list.ts
}
Working sample: https://stackblitz.com/edit/angular-i3pfu2-6n5cnt
See also: https://material.angular.io/components/list/api
回答3:
To begin with, mat-selection-list
is not suitable for a single-value selection, as it veers away from its intent:
Checkboxes in a group are non-exclusive options; more than one checkbox in a group can be checked at any given time
What you're looking for is the radio-button element itself, because semantically it stands for:
A radio button is one of a group of controls representing mutually-exclusive choices
Unfortunately Angular Material does not include a mat-radio-list
component. But you can still achieve it by including a mat-radio-button
inside you mat-list-item
. This will provide best practice, as it denotes to the user that the list in view is intended for mutually-exclusive choices (unlike checkboxes that denote multiple-choice). And since the radio buttons are updating a single variable, you get exclusivity:
<mat-list role="list">
<mat-list-item role="listitem" *ngFor="let x of [0,1,2]; let i = index">
<mat-radio-button [value]="i" (change)="selection = $event.value">
Item {{x}}
</mat-radio-button>
</mat-list-item>
</mat-list>
Check a Stackblitz
回答4:
Just set the multiple option of MatSelectionList to false:
ngOnInit() {
this.links.selectedOptions._multiple = false;
}
and you're done. Also by doing this you will be able to deselect the selected checkbox.
回答5:
Bit late to the party, but here's a solution I came up with using a directive to extend the mat-selection-list
behaviour.
import { SelectionModel } from "@angular/cdk/collections";
import { Directive, Host, Input, OnChanges } from "@angular/core";
import { MatListOption, MatSelectionList } from "@angular/material/list";
@Directive({
selector: "mat-selection-list[multiple]"
})
export class MatSelectionListMultipleDirective implements OnChanges {
private matSelectionList: MatSelectionList;
@Input()
public multiple: boolean;
constructor(@Host() matSelectionList: MatSelectionList) {
this.matSelectionList = matSelectionList;
}
public ngOnChanges(): void {
if (this.multiple) {
this.matSelectionList.selectedOptions = new SelectionModel<MatListOption>(true, this.matSelectionList.selectedOptions.selected);
}
else {
let selected = this.matSelectionList.selectedOptions.selected.splice(0, 1);
this.matSelectionList.selectedOptions = new SelectionModel<MatListOption>(false, selected);
}
}
}
Which you can use like this...
<mat-selection-list [multiple]="false">
...
</mat-selection-list>
Ideally we would have a radio control instead of the checkbox, but I think this workaround is OK until the angular material team support this officially.
回答6:
In order to make single checkbox select from mat-selection-list if you are not using Angular Material v6+ here is the workaround i used : Let's say we have a list of categories checkboxes.
In your HTML :
<mat-selection-list>
<mat-list-option (selectionChange)="handleSelection($event, category)" *ngFor="let category of categories" [value]="category">
<span >{{category.name}}</span>
</mat-list-option>
</mat-selection-list>
In your .TS :
handleSelection(event, categorySelected) {
if (event.selected) {
event.source.selectionList.options.toArray().forEach(element => {
if (element.value.name!= categorySelected.name) {
element.selected = false;
}
});
}
}
The key here is to use the property selectionChange on the MatListOption and not MatSelectionList.
回答7:
From a philosophical and UX perspective, you need to use radio buttons. This is the only way to provided user-friendly "as expected" behavior.
回答8:
If you're using ngFor for each mat-checkbox, you can do something similar to following in HTML template:
<ng-container *ngFor="let address of person.addresses; let i = index">
<mat-checkbox [checked]="personCheckedIndex === i" (change)="customerCheckboxChange($event, i)"></mat-checkbox>
</ng-container>
and in TS:
public personCheckedIndex = -1;
// Called from template when checkbox value changes
personCheckboxChange(event: MatCheckboxChange, index: number) {
// This allows only one checkbox to be checked among each checkbox
this.personCheckedIndex = event.checked ? index : -1;
}
回答9:
What you need is probably a <mat-radio-group>
rather than a <mat-selection-list>
, Its a list of options but only one option can be selected. an example can be found at the Angular Material docs.
But be advised that instead of a checkbox you'll get a radio button.
回答10:
try this:
ts:
import { Component, OnInit, ViewChild} from '@angular/core';
import { MatSelectionList } from '@angular/material';
@Component({
selector : 'app-xxx',
templateUrl: './xxx.component.html'
})
export class xxxComponent implements OnInit {
public data: Object[] = [ {name : "name 1" }, {name : "name 2" }, {name : "name 3" }, {name : "name 4" } ];
@ViewChild('element_id') names_list: MatSelectionList;
ngOnInit() {
// only if the element is visible on the page, otherwise move this block and execute it until the list is displayed
setTimeout(()=>{
this.names_list.selectedOptions._multiple = false;
},0);
}
}
template:
<mat-selection-list #element_id>
<mat-list-option *ngFor="let d of data">
{{ d.name }}
</mat-list-option>
</mat-selection-list>
回答11:
you can use 'mat-checkbox' instead of 'mat-selection-list' and play with click event like this:
HTML
<div *ngFor=" ... loop">
<mat-checkbox #checkBoxes (click)="changeCheckBox()">
</mat-checkbox>
</div>
TS
@ViewChildren('checkBoxes') checkBoxes: QueryList<MatCheckbox>;
changeCheckBox() {
this.checkBoxes.filter((checkbox: MatCheckbox) =>
checkbox.checked === true
)[0].checked = false;
}
remember that all check boxes default value is unchecked.
回答12:
you can declare the [selected]
attribute of mat-list-option
for example:
<mat-selection-list #list>
<mat-list-option *ngFor="let item of myList"
[(mgModel)]="selectedOption"
[selected]="item.id === selectedOption.id"
[value]="item">
{{item.name}}
</mat-list-option>
</mat-selection-list>
by this way you reach just one selection without changing any MatSelectionList hidden property.
Also you can do it by replacing ngModel
with controller logic,
by this way you will save the last selection object in class variable.
<mat-list-option (selectionChange)="onChange($event)"`
see also Binding an Angular Material Selection List
回答13:
Starting Angular Material 9.1.0, selection list supports a single selection mode. Enable it with the multiple
input set to false
: https://material.angular.io/components/list/api#MatSelectionList