Reputation: 6347
I'm learning Angular with TypeScript, and a few inconsistencies with JavaScript are troubling me. Here's a function which works perfectly with pure JavaScript (it programmatically sets min
and max
attributes to an input - please don't ask me why I'd want to do that programmatically):
function change_number(input,min,max) {
if(input.value > max)
input.value = max;
else if(input.value < min)
input.value = min;
}
<input type='number' oninput='change_number(this,1,5)' placeholder='Enter a number' />
In Angular and TypeScript, the function behaves strangely: If I enter 10
into the input field, it doesn't reset to 5
, until I enter another digit, meaning the value is only read after the input
event (and not on-input - don't know if that makes sense). What's more strange is it works fine with the keydown
and keyup
events. Why this behaviour? Is it linked to the way Angular binds events to inputs? I have a feeling understanding this would help me better understand Angular's binding mechanism with NgModel
FYI, here's how I called the function in Angular (using the Ionic Framework) - I used the bound quantity
in [(ngModel)]
as the value of the input:
<ion-input type='number' (input)='change_numbers(1,5)' [(ngModel)]='quantity'></ion-input>
Upvotes: 9
Views: 3196
Reputation: 9436
To understand why it behaves like that, first you need to be familiar with two basic concepts:
[(ngModel)]="quantity"
is nothing more than sugar syntax which is translated to [ngModel]="quantity" (ngModelChange)="quantity = $event"
With this knowledge, you can understand what really happens here:
onNgModelChange
is triggered, quantity
is set to 4
. The inner model value of input
(more specifically, ngModel
directive) is still equal to undefined
.change_numbers
is called, quantity
is not changed.ngModelChange
and change_numbers
all called in a single callstack due to how angular handles events). Change detection is performed. Because quantity
(4
now) is bound to the ngModel
(undefined
now), angular detects a need to update the UI (because undefined != 4
of course).8
.onNgModelChange
is triggered, quantity
is set to 48
. The inner model value of input
(more specifically, ngModel
directive) is still equal to 4
.change_numbers
is called, quantity
is changed to 5
.quantity
(5
now) is bound to the ngModel
(4
now), angular detects a need to update the UI (because 5 != 4
of course).7
.onNgModelChange
is triggered, quantity
is set to 57
. The inner model value of input
(more specifically, ngModel
directive) is still equal to 5
.change_numbers
is called, quantity
is changed to 5
.quantity
(5
now) is bound to the ngModel
(also 5
now), angular doesn't detect a need to update the UI (because 5 == 5
of course).A solution to that might be calling detectChanges
in onNgModelChange
method. It will work, because in this moment we will update the inner model
value of ngModel
(even if it is much higher than 5
). So even if we next decrease the value to 5
, angular still will detect a need to update the UI.
I hope that explains everything. If you'll have some questions, don't hesitate to let me know. Cheers!
Upvotes: 3
Reputation: 1021
Not sure if this helps, but I found these;
how to use oninput in Angular?
oninput not working in Angular. Alternative?
They seem to talk a lot about using (input)
; if you've tried it?
Reading on google about this issue, it just seems like if you're using Angular you have to do things "The Angular Way" or oddities like what you're trying to seek an answer for occur.
As Kuiil from The Mandolorian says; "This is the way"
Upvotes: 0
Reputation: 4022
Angular framework is behaving perfectly the way it has to. It is us who are a bit confused.
Angular handles forms in a bit different manner. There are two approaches to build forms in angular -
The Key difference between them is the above Sync/async behaviour. In simple terms we can say that.
x
<ion-input type='number' (input)='change_numbers(1,5)' [(ngModel)]='quantity'></ion-input>
Above one is a Template-driven approach. Here we are using [(ngModel)]
, this updates only after the data has changed.
In order to make a reactive input field, use below code :-
in app.component.ts file
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
@Component({
selector: 'dd-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
form: FormGroup;
min = 0;
max = 5;
constructor(private fb: FormBuilder) {
this.buildForm();
}
buildForm(): void {
this.form = this.fb.group({
numberInput: ''
});
this.form.get('numberInput').valueChanges.pipe(distinctUntilChanged()).subscribe(_ => {
if (_) {
if (_ > this.max) {
this.form.get('numberInput').setValue(5);
}
if (_ < this.min) {
this.form.get('numberInput').setValue(0);
}
} else {
this.form.get('numberInput').reset('');
}
});
}
}
In app.html (Here i am posting 2 way to express it.)
<form [formGroup]="form">
<!-- <input type='number' [min]='0' [max]='5' formControlName="numberInput" /> we can also this one, more easy just set value dynamically -->
<input type='number' formControlName="numberInput" />
</form>
NOTE:- DONT FORGET TO IMPORT ReactiveFormsModule
More about forms can found in the official docs : -https://angular.io/guide/forms-overview
Upvotes: 5
Reputation: 799
Try this out, this works well, let me know if this is not what you want to achieve
html
<input type='number' (change)='change_numbers(1,5)' [(ngModel)]="quantity">
javascript
change_numbers(min,max) {
if(this.quantity > max)
this.quantity = max;
else if(this.quantity < min)
this.quantity= min;
}
Upvotes: 0