Im working on Angular 2.0 (Yes, a bit behind from current 2.4).
I have a checkbox list. Im trying to make that when the LAST CHECKBOX IS UNCHECKED all CHECKBOXES ARE CHECKED.
HTML
<div *ngFor="let filter of filters">
<label htmlFor="check-box-{{filter.text}}"
[ngClass]="(filter.selected)? 'active' : '' ">
<input type="checkbox"
name="check-box-{{filter.text}}"
[checked]="filter.selected"
(change)="onSelectFilter(filter)"
attr.id="check-box-{{filter.text}}">
{{filter.selected}} - ({{filter.counter}})
</label>
</div>
TS
onSelectFilter(filter: Filter){
filter.toggleSelection();
let isAnyFilterSelected = this.filters.find(filter => filter.selected);
// If no filter is selected then ALL filters are selected
if(!isAnyFilterSelected){
this.filters.forEach(filter => filter.selected = true );
}
}
I create a plunker for it.
https://plnkr.co/edit/uzF6Lk5fxRZjXBOaS9ob?p=preview
If unchecked the only checkbox with CHECKED attribute TRUE, Im expecting that ALL checkboxes would have CHECKED attribute. This does not happen.
Any ideas?
You should use ngModel
instead of binding to checked
, and use a setTimeout
.
<div *ngFor="let filter of filters">
<label htmlFor="check-box-{{filter.text}}"
[ngClass]="(filter.selected)? 'active' : '' ">
<input type="checkbox"
name="check-box-{{filter.text}}"
[ngModel]="filter.selected"
(ngModelChange)="onSelectFilter($event,filter)"
attr.id="check-box-{{filter.text}}">
{{filter.selected}} - ({{filter.counter}})
</label>
</div>
onSelectFilter(selected:boolean,filter: Filter){
filter.selected=selected;
if(this.filters.every(filter=>!filter.selected)){
setTimeout(()=>{
this.filters.forEach(filter=>filter.selected=true);
});
}
}
plunkr
"why such a fishy trick is needed ?"
Actually, because from change-detector's point of view, there is no change between the previous state and the new one.
So there is no need to update the @Input()
of the child /call the writeValue()
method of the ControlValueAccessor
(<input type="checkbox" [ngModel]="foo">
).
Using setTimeout, you first update the property to false, then delay its change back to initial state, allowing a new change-detection cycle.
Also note that events like (ngModelChange)
are not correlated to the change-detection cycle.
Without setTimeout()
:
Here, we will get the same result as in your example, while we keep foo
being true, the checkbox won't update :
The code (plunkr):
@Component({
selector: 'my-app',
template: `
<input id="foo" type="checkbox" [ngModel]="foo" (ngModelChange)="fooChange($event)"><label for="foo">{{foo}}</label>
`,
})
export class App {
filters:[];
foo=true
fooChange(newValue:boolean){
if(newValue===false)
this.foo=true; // if newValue is false, foo becomes true
else
this.foo = newValue; // otherwise, do change
}
}
What is happening under the hood :
With setTimeout()
This time we will delay resetting the value to a next tick using setTimeout
:
The code (plunkr):
@Component({
selector: 'my-app',
template: `
<input id="foo" type="checkbox" [ngModel]="foo" (ngModelChange)="fooChange($event)"><label for="foo">{{foo}}</label>
`,
})
export class App {
filters:[];
foo=true
fooChange(newValue:boolean){
this.foo=newValue; // we need to make it change !
setTimeout(()=>{
if(newValue===false)
this.foo=true; // if newValue is false, foo becomes true
else
this.foo = newValue; // otherwise, do change
})
}
}
What is happening under the hood :