I have a list of items with checkboxes, and I would like to disable the ability to check off those boxes after a user has checked off 10 items from the list.
I am new to the world of Angular, so I am not clear why my use of [disabled]
is not working:
<mat-selection-list appScrollable #filteredCountriesSelectionList >
<mat-list-option appOffsetTop *ngFor="let country of filteredCountries" [value]="country.name" checkboxPosition="before" [disabled]="filteredCountries.length > 10">
{{country.name}} {{country.iso3}} <span>{{country.emoji}}</span>
</mat-list-option>
</mat-selection-list>
I attempted something like this in the past:
<mat-selection-list appScrollable #filteredCountriesSelectionList [disabled]="filteredCountries.length >= 10">
<mat-list-option (selectedChange)="onChange($event, country)" appOffsetTop *ngFor="let country of filteredCountries" [value]="country.name" checkboxPosition="before" [disabled]="filteredCountries.length > 10">
{{country.name}} {{country.iso3}} <span>{{country.emoji}}</span>
</mat-list-option>
</mat-selection-list>
And then in my component class file:
filteredCountries = [];
onChange(selected: boolean, country: string) {
this.filteredCountries.push(country);
}
Since the work I have done above only served to completely disable the ability to click on checkboxes, I decided to console log event
and selectedOptions
to get the appropriate type and was able to get it at least for one of the parameters.
So instead what I landed on that is getting closer is removing [disabled]
entirely I believe and doing it like so:
<mat-selection-list (selectionChange)="disableMaxSelection($event, filteredCountriesSelectionList.selectedOptions)" appScrollable #filteredCountriesSelectionList [disabled]="filteredCountries.length >= 10">
<mat-list-option (selectedChange)="onChange($event, country)" appOffsetTop *ngFor="let country of filteredCountries" [value]="country.name" checkboxPosition="before" [disabled]="filteredCountries.length > 10">
{{country.name}} {{country.iso3}} <span>{{country.emoji}}</span>
</mat-list-option>
</mat-selection-list>
So now I am depending on a selectionChange
called disableMaxSelection()
like so:
disableMaxSelection(event: MatSelectionListChange, selectedOptions) {}
And now I think I got it, but I might need some help with the logic here, something like comparing with current selected filter and if item is not in filter, don't disable? Not sure.
I have tried repeatedly to implement the use of [disabled]=""
but it disables my checkboxes every single time. And my data is not an array of strings its an array of objects.
CodePudding user response:
I don't see any event specified in your element, try adding selectedChange
onto your mat-list-option
, also, you should add the disabled
in the element container rather than the option.
I have taken an example from the offical docs:
Template HTML
<mat-selection-list [disabled]="shoesSelected.length > 3"> <!--disable goes here-->
<mat-list-option
(selectedChange)="onChange($event, shoe)" <!-- listen to this event -->
*ngFor="let shoe of typesOfShoes">
{{ shoe }}
</mat-list-option>
</mat-selection-list>
Component
...
@Component({...})
export class YourComponent {
typesOfShoes: string[] = ['Boots', 'Clogs', 'Loafers', 'Moccasins', 'Sneakers'];
shoesSelected: string[] = [];
onChange(selected: boolean, shoe: string) {
// ... do something with selected
this.shoesSelected.push(shoe);
}
}
CodePudding user response:
You need check if the length>10 and is not selected.
You can play with two properties:
- options: a QueryList
- selectedOptions.selected.length: how many items are you selected
So, if you write some like
<mat-selection-list #shoes>
<mat-list-option *ngFor="let shoe of typesOfShoes;let i=index"
[disabled]="shoes.selectedOptions.selected.length>2 &&
!isSelected(i,shoes.options)
">
{{shoe}}
</mat-list-option>
</mat-selection-list>
Your function isSelected like
isSelected(index:number,options:QueryList<MatListOption>)
{
if (!options)
return;
const option= options.find((x,i)=>i==index)
return option && option.selected
}
See how you can find the option of a QueryList by index using the "second argument of find"
And you get it
NOTE: You can use some like
getValue(options:QueryList<MatListOption>)
{
if (!options)
return null;
return this.typesOfShoes.filter((x,index)=>this.isSelected(index,options));
}
To get the values selected
See a stackblitz
Personal Opinion To control a series of check-boxes I feel is easer use a FormArray of FormControls or a simple [(ngModel)] using an array than fight with QueryLists
Update another way to control
e.g. Imagine we has a variable
selected:string[]=[]
And some like
<div *ngFor="let shoe of typesOfShoes;let i=index">
<mat-checkbox
[checked]="selected.indexOf(shoe)!=-1"
(change)="change(shoe,$event.checked)"
[disabled]="selected.length>2 && selected.indexOf(shoe)==-1"
>{{shoe}}</mat-checkbox
>
</div>
The function change
change(value:string,checked:boolean)
{
const oldValue=this.selected || []
if (!checked)
this.selected=this.typesOfShoes.filter(
x=>oldValue.indexOf(x)!=-1 && x!=value)
else
this.selected=this.typesOfShoes.filter(
x=>oldValue.indexOf(x)!=-1 || x==value)
}
You get it again
CodePudding user response:
So here is what worked for me, with great help.
<mat-selection-list (selectionChange)="onSelection($event)" appScrollable #filteredCountriesSelectionList [disabled]="filteredCountries.length >= 10">
<mat-list-option [disabled]="disableCountry(country.name)" appOffsetTop *ngFor="let country of filteredCountries | orderBy:'name'" [value]="country.name" checkboxPosition="before" [attr.id]="'scrollTo' country.name.split('')[0].toUpperCase()">
{{country.name}} {{country.iso3}} <span>{{country.emoji}}</span>
</mat-list-option>
</mat-selection-list>
And the component class file:
get isMaxCountries() {
return this.selectedCountries?.length >= 10;
}
disableCountry(country: string) {
if (this.isMaxCountries) {
const countryNotSelected = this.selectedCountries.indexOf(country) === -1;
return countryNotSelected;
}
}
onSelection(event: MatSelectionListChange) {
this.selectedCountries = event.source._value;
}