Reputation: 28338
Since upgrading to angular 2.0.0-rc2 I noticed that calling this.onModelChange.emit(this.model)
from inside the ngOnInit
or ngDoCheck
functions causes this error:
Expression has changed after it was checked
I've looked through the changelog of the new release but I can't find anything related to this.
This is a big issue if you want to set a default value when you first init the component. If I don't emit the value on init then the value isn't available in the outer scope which is no good.
How can I fix this problem?
import {Component, Input, Output, EventEmitter, ElementRef, OnInit, DoCheck, OnDestroy, Renderer} from '@angular/core';
import {GlobalVariablesService} from '../../core/global-variables.service';
import {IsTouchedDirective} from '../../core/directives/is-touched.directive';
@Component({
selector: 'form-select',
templateUrl: './form-select.component.html',
styleUrls: ['./form-select.component.css'],
inputs: [
'options',
'callback',
'model',
'modelProperty',
'required',
'optionsLabel',
'disabled',
'label',
'placeholder'
],
directives: [
IsTouchedDirective
]
})
export class FormSelectComponent implements OnInit, DoCheck, OnDestroy {
@Input() model: any;
@Output('modelChange') onModelChange: EventEmitter<any> = new EventEmitter();
@Output() callback: EventEmitter<any> = new EventEmitter();
public isOpen: boolean = false;
public previousModel: any = null;
constructor(private _globals: GlobalVariablesService, private _elementRef: ElementRef, private _renderer: Renderer) {
this.ns = _globals.ns;
this.eventHandler = _renderer.listenGlobal('document', ('ontouchstart' in window ? 'touchend' : 'click'), (e) => {
// If the clicked element is not the component element or any of its children, close the select
if (this._elementRef.nativeElement !== e.target && !this._elementRef.nativeElement.contains(e.target)) {
this.close();
}
});
}
ngOnInit() {
// If no model is set and the select shouldn't be allowed empty, set the model to the first option
if (!this.model && this.required) {
this.model = this.options[0];
}
// If it should be allowed empty, set it to null which will add an empty class in the markup
else if (!this.required) {
this.model = null;
}
this.onModelChange.emit(this.model);
}
ngDoCheck() {
// Check if the model changes and emit it if it does
if (this.previousModel !== this.model) {
this.onModelChange.emit(this.model);
}
this.previousModel = this.model;
}
ngOnDestroy() {
this.eventHandler();
}
toggle() {
if (this.disabled) {
this.isOpen = false;
return false;
}
this.isOpen = !this.isOpen;
}
close() {
this.isOpen = false;
}
select(option) {
this.model = option;
this.isDirty = true;
this.close();
this.onModelChange.emit(this.model);
this.callback ? this.callback.emit() : false;
}
isSelected(option) {
if (this.model) {
return option[this.modelProperty] === this.model[this.modelProperty];
}
else {
return false;
}
}
}
Upvotes: 1
Views: 786
Reputation: 4013
It seems angular 2.0.0-rc2 has fixed a bug with event emitter which is causing this issue
`var emitter = new EventEmitter()
emitter.emit(data)`
Above line rc1 will emit asynchronously so your changes would be detected in next cycle so you won't get this error.
Same line In rc2 will emit synchronously so changes will happen in same cycle so you will get mentioned error.
EventEmitter has an optional argument to make emitting async. You can modify above code as below to works as it was working in rc1
var emitter = new EventEmitter(true)
emitter.emit(data)
Upvotes: 3