Issue
I have the following input
<input type="number" [ngModel]="inputValue" (ngModelChange)="handleChange($event)"/>
And i am trying to force the input to stay less or equal to handred. using the method handleChange
export class AppComponent {
inputValue: number = 0;
handleChange($event: any) {
if ($event > 100) {
this.inputValue = 100;
}
}
}
And it only works the first time I enter the first input that is higher than a 100, but after that i doesn't.
My understanding of it is that the DOM doesn't update when there no value updates.
I can solve this problem using other methods using @ViewChild for example, but I am more intersted in knowing how this works and how angular handles this specific use case.
Thanks!
Solution
The solution is to introduce two-way binding, so your property and the html value are synchronized. You also need to force change detection before setting the property to 100.
Stackblitz: https://stackblitz.com/edit/angular-ivy-upykps?file=src/app/app.component.ts
Both of these steps are necessary for the change detector to detect that the property has changed every time, only then will it update the html value with your property value.
Two way binding is accomplished with the banana-in-a-box [(🍌)]
notation.
Change detection is forced with the ChangeDetectorRef
injectable service.
<input type="number" [(ngModel)]="inputValue" (ngModelChange)="handleChange($event)"/>
inputValue: number = 0;
constructor(private cd: ChangeDetectorRef){};
handleChange($event: any) {
this.cd.detectChanges();
if ($event > 100) {
this.inputValue = 100;
}
}
I know this is a bit unintuitive but it has to do with how the change detector detects changes. If you really want to understand it, I suggest adding a debugger
line at the start of your function and walking through the steps.
I'll explain what was happening the way you had it set up:
The html value is set to 0 via data binding and the change detector notes that the property is currently 0. On every click event, change detection is run after the ngModelChange
callback. So, the first time you set the property to 100, the change detector sees - oh yes, the property used to be 0, now it is 100, a change has occurred - it sets a flag that causes the html to be updated with the new value, and notes that the property is currently 100.
The problem is you never changed inputValue
away from 100 - so every time you set it to 100 - the change detector sees no change. It will never update the html again.
Using two-way binding, the property is updated whenever the html value is changed - this happens before ngModelChange
. But this does not update the change detector's state! It will work when we go from less than 100 to greater than 100, but if we continue passing more numbers that are larger than 100, it won't be reset. That's because change detection is run after ngModelChange
, and we actually overwrite the larger value to 100 before the change detector can see the larger value. We get the same problem - the change detector sees 100 every time and does not attempt to update the html.
You need the forced change detection so the change detector can note down that the property changed to this larger value. Then after setting the property to 100, it will see - ah yes, the property used to be this larger value, now it is 100 - it sets the "changed" flag which triggers a DOM update.
An example when the user changes the html from 100 to 101:
The change detector notes the current state on every change detection, but NOT during data binding. DOM updates are only triggered when the change detector finds a change.
Initial State
ACTUAL
html value: 100
property: 100
CHANGE DETECTOR
property: 100
User changes html value to 101
ACTUAL
html value: 101
property: 100
CHANGE DETECTOR
property: 100
Round bracket data binding updates property
ACTUAL
html value: 101
property: 101
CHANGE DETECTOR
property: 100
handleChange calls ChangeDetectorRef.detectChanges()
ACTUAL
html value: 101
property: 101
CHANGE DETECTOR
property: 100 - CHANGED!
**This is the important part
the change detector sees that the property changed to 101**
Square bracket data binding attempts to update html because of a change
It's already correct though
ACTUAL
html value: 101
property: 101
CHANGE DETECTOR
property: 101
handleChange changes property to 100
ACTUAL
html value: 101
property: 100
CHANGE DETECTOR
property: 101
change detector detects changes because click event occurred
ACTUAL
html value: 101
property: 100
CHANGE DETECTOR
property: 101 - CHANGED!
**If this was still at 100, no updates would be made.
There would be a mismatch between property and html value**
Square bracket data binding updates html because of a change
ACTUAL
html value: 100
property: 100
CHANGE DETECTOR
property: 100
Answered By - Chris Hamilton
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.