I feel there are some things that are missing in Angular 2 that Angular 1 had, one of those things being the $watch
function.
OnChanges
and DoCheck
are supposed to replace the $watch
but I haven't been able to use either to get the desired result. OnChanges
can't listen to objects so that's of no use in this scenario, and while DoCheck
does the job, it causes massive lag since it seems to go bananas as soon as a change happens.
Here I've got a component which has a bunch of checkboxes, each checkbox toggle causes a value to be added to an array, rather than the default true/false
functionality.
What I need to watch for in this component is the isDisabled
state of a checkbox, so each time it changes I need to update the isChecked
state of the checkbox
.
Like I said, it works, but it has 2-3 seconds of delay on every checkbox toggle, despite only having 5 checkboxes. Preferably I wouldn't want the loop to run every time you toggle a checkbox, but instead only if the isDisabled
state of a checkbox changes.
EDIT: I was told an Observable
could do the job here, but I can't find any examples of this being applied to an array of objects like this. It also needs to trigger without calling its next()
method manually, since it has to listen for outside changes to the array that is being sent in.
EDIT 2: I created a plunkr which doesn't show the lag, probably because in my app I have other things that might have an affect on the delay as well. However, the plunkr clearly shows that there seems to be something wrong because I put a console.log(true)
within the ngDoCheck()
function and it runs a looot of times. Which can't be intended, if it is, it's really odd.
Here's the plunkr: https://plnkr.co/edit/dJVnqbrredDyWTXNDzDK?p=preview
How can I do this?
export class FormCheckboxMultipleComponent implements OnInit, DoCheck {
@Input() model: Array<any>;
@Output('modelChange') onModelChange: EventEmitter<any> = new EventEmitter();
@Output() callback: EventEmitter<any> = new EventEmitter();
constructor(private _globals: GlobalVariablesService) {
this.ns = _globals.ns;
}
ngOnInit() {
this.model = this.model || [];
}
ngDoCheck() {
for (let checkbox of this.checkboxes) {
if (checkbox.isDisabled) {
this.untoggle(checkbox);
}
}
}
findIndex(checkbox) {
return this.model.reduce(function(cur, val, index) {
if (val.name === checkbox.name && cur === -1) {
return index;
}
return cur;
}, -1);
}
addToModel(checkbox) {
this.model.push(checkbox);
}
removeFromModel(i) {
this.model.splice(i, 1);
}
toggle(checkbox) {
if (checkbox.isDisabled) {
return false;
}
let existingIndex = this.findIndex(checkbox);
if (existingIndex === -1) {
this.addToModel(checkbox);
}
else {
this.removeFromModel(existingIndex);
}
checkbox.isChecked = !checkbox.isChecked;
this.onModelChange.emit(this.model);
this.callback ? this.callback.emit() : false;
}
untoggle(checkbox) {
let existingIndex = this.findIndex(checkbox);
this.removeFromModel(existingIndex);
checkbox.isChecked = false;
this.onModelChange.emit(this.model);
}
}
That's caused by this.onModelChange.emit(this.model);
in this.untoggle(checkbox)
in ngDoCheck
. This is a perfect endless loop. this.onModelChange.emit(this.model);
causes change detection and therefore ngDoCheck()
to run and the loop begins again.
Ensure that change detection doesn't cause change detection. ngDoCheck
is called by change detection and must not invoke another change detection otherwise you get an endless loop.