2 way binding between 2 components works only on first letter

665 views Asked by At

I am getting a very strange error that i have researched and I am having trouble figuring out. Most all of the items that I have found similar are about a year old before final changes and do not work now.

Error Message

Expression has changed after it was checked. Previous value: 'Angular2'. Current value: 'Angular2x'.

For some reason it is trowing an exception when it 2 way binds to the property.

I think that the error is happening after the following code is ran.

  ngDoCheck(){
    this.bindingPropertyChange.emit(this.bindingProperty)
  }

I have created a Plunker to reproduce the error. Plunker here

Does anyone know why this is happening? If so, can you please explain why this exception is being thrown?

2

There are 2 answers

2
Milad On BEST ANSWER

ngDoCheck is your problem, here is why :

ngDoCheck would fire after something has changed inside your model.

After the letter you typed in, angular starts checking everywhere (the whole app ) for any relevant changes, and Angular is running change detection only once after a change, and there shouldn't be any update after that.( one way data flow, one check only ).

Let's say change detection is ran ( ngDoCheck ) successfully and updated the UI accordingly, BUT in the middle of the checking , you're suddenly making another change.

In Dev mode, Angular2 runs another changeDetection after the first one to make sure nothing has changed after the first one, and because you emitted that event and changed the model again silently, angular is angry and throws error.

However, in Prod mode , Angular won't run the second check so you won't see this error in production , but your model update ( emit ) won't bet reflected in UI.( to test this, simply add ngCore.enableProdMode(); to your main.ts file and the error goes away).

Here is couple of ways to fix that :

1- Change your strategy, like In my First Plunker , you can simply use object bindings and bypass the issue.

2- Postpone your change with a setTimeout , which will run another change detection and makes angular happy :

ngDoCheck(){
    setTimeout(()=>{
      this.bindingPropertyChange.emit(this.bindingProperty)
    });

  }

Here is the plunker

3- Do not use ngDoCheck and emit your change in another cycle using (input) or (keyup) or similar events, which again ,makes Angular to run another change detection :

NOTE I'd suggest using (input) over keyup or keydown or similar and the reason is if you use keyup/keydown , the update won't be seamless, because if you press a key and hold it there the update won't happen until you release the key , but with (input) , it happens straight away and like you said in comment if a user were to copy paste with mouse they are not using a key up and it will not update the model.

<input [(ngModel)]="bindingProperty" (input)="emitUp()" class="form-control"/>

emitUp(){
    this.bindingPropertyChange.emit(this.bindingProperty)
  }

Here is the Plunker

0
micronyks On

I'm not the right person to say anything about ngDoCheck but it seems there is a problem with ngDoCheck. Somebody else can suggest that and can edit my answer to say anything about ngDoCheck.

But as of now you can use keyup event and bind a function with it as shown below,

DEMO : https://plnkr.co/edit/hSrPuWNyJd6Az8sHXh1T?p=preview

<input [(ngModel)]="bindingProperty" (keyup)="check()" class="form-control"/>

check(){
    this.bindingPropertyChange.emit(this.bindingProperty)
}