Any time I enter a number into an input that is 2 way bound to my object the value is converted to a string.
How can I force it or convert it to be a number?
It seems like it should be very simple and yet I have struggled to find an elegant solution.
I saw this solution of using a function to convert it but I don't see how that would work for 2 way data binding.
convert string to number angular 2 one way binding
<div class="row">
<mat-form-field class="custom-control">
<input type="number" matInput class="custom-control" [(ngModel)]="mpv.baseFare" required placeholder="Base Fare">
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="custom-control">
<input type="number" matInput class="custom-control" [(ngModel)]="mpv.mileageRate" required placeholder="Mileage Rate">
</mat-form-field>
</div>
mpv is an object of class VehicleType I have my VehicleType class set up as follows. The number types are completely ignored, I guess because it is at run time so just non typed javascript.
export class VehicleType {
baseFare: number;
mileageRate: number;
}
UPDATE - CURRENT HACKY SOLUTION:
onSave(vehicleType: VehicleType) {
// Hack to convert string to number
vehicleType.bags = +vehicleType.bags;
vehicleType.baseFare = +vehicleType.baseFare;
vehicleType.mileageRate = +vehicleType.mileageRate;
vehicleType.passengers = +vehicleType.passengers;
this.vehicleTypeService.updateVehicleType(vehicleType.id, vehicleType).subscribe(result => {
It turns out that the order of the attributes on a matInput mater. This issue does not occur on a standard input only a matInput.
Does not work:
<input type="number" matInput class="custom-control" [(ngModel)]="mpv.baseFare" required placeholder="Base Fare">
Works:
<input matInput type="number" class="custom-control" [(ngModel)]="mpv.baseFare" required placeholder="Base Fare">
If you want to be able to modify the value when ngModel sets a new value then you're most likely looking at creating a get and set function. Example,
<div class="row">
<mat-form-field class="custom-control">
<input type="number" matInput class="custom-control" [(ngModel)]="baseFare" required placeholder="Base Fare">
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="custom-control">
<input type="number" matInput class="custom-control" [(ngModel)]="mileageRate" required placeholder="Mileage Rate">
</mat-form-field>
</div>
get baseFare(): number {
return mpv.baseFare;
}
set baseFare(value: any) {
mpv.baseFare = pareseInt(value, 10);
}
get mileageRate(): number {
return mpv.mileageRate;
}
set mileageRate(value: any) {
mpv.mileageRate = pareseInt(value, 10);
}
That should do the job.
Update: you could just have value as any or both as any since you know the return type will be a number.
Related
I have a project in Angular in which I have created a dialog with several fields for a component. What I want is to take advantage of this dialog for other components, but the amount of data varies.
This is my dialog.component.html:
<div class="dialog-container">
<mat-form-field>
<input matInput placeholder="Name" [(ngModel)]="data.name">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Phone" [(ngModel)]="data.phoneNumber">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Email" [(ngModel)]="data.email">
</mat-form-field>
</div>
I want to know if it is possible to create only one mat-form component and display as many as necessary, passing it the number of fields that I want to generate.
This is what I want the dialog.component.html to contain, to achieve the same as above (I don't know if this is possible):
<div class="dialog-container">
<mat-form-field>
<input matInput placeholder={{place}} [(ngModel)]={{data}}>
</mat-form-field>
</div>
From only one mat-form-field is it possible to show the same result as the first example?
You could create a custom directive :
import { Directive, Input } from '#angular/core';
import { NgForOf } from '#angular/common';
#Directive({
selector: '[ngFor][ngForRepeat]'
})
export class NgForRepeat<T> extends NgForOf<T> {
#Input() set ngForRepeat(repeat: number) {
this.ngForOf = new Array(repeat >= 0 ? repeat : 0);
}
}
And use it as follow in you template :
<ng-container *ngForRepeat="3">
<mat-form-field>
<input matInput placeholder={{place}} [(ngModel)]={{data}}>
</mat-form-field>
</ng-container>
So I'm working on an Angular app and I have these two date input fields:
<div class="col-lg-3">
<div> {{ dataInizioLabel }} </div>
<input required type="datetime-local" name="dataInizio" id="dateInput"
[ngModel]="serverEvent.dataInizio | date:'yyyy-MM-ddTHH:mm'"
(ngModelChange)="manageDateChange($event, dataInizio)" #dataInizio="ngModel">
<small class="text-danger" *ngIf="dataInizio.errors">Campo obbligatorio</small>
</div>
<div class="col-lg-3">
<div> {{ dataFineLabel }} </div>
<input required type="datetime-local" name="dataFine" [ngModel]="serverEvent.dataFine | date:'yyyy-MM-ddTHH:mm'" id="dataFine"
(ngModelChange)="manageDateChange($event, dataFine)" #dataFine="ngModel">
<small class="text-danger" *ngIf="dataFine.errors">Campo obbligatorio</small>
</div>
manageDateChange() JS (incomplete):
manageDateChange(date, inputField) {
if (date) {
this.serverEvent[inputField.name] = new Date(date);
}
let inizio = new Date(date.val())
console.log(inizio)
}
I can't find a way to make so the dataInizioLabel uptades the dataFineLabel field with his same date when it gets changed. For example, if dataInizioLaber gets set to 24/04/2022, I'd need dataFineLabel to self update to the same date.
Also, console says date.val() can't work and gives an error, so I don't think that's the right way to get the date value from the input field.
You might want to try to use a two-way binding on your ngModel like this:
[(ngModel)]="serverEvent.dataInizio | date:'yyyy-MM-ddTHH:mm'"
This question already has an answer here:
Regular expression works on regex101.com, but not on prod
(1 answer)
Closed 2 years ago.
Currently working on regex / pattern in angular where i need to restrict the user to input only one minus and digit as per the below example.
-10
-10.00
10
This is what i have tried so far.
<input style="width: 65px;" ng-pattern="/^-?[0-9]\d*(\.\d+)?$/" class="form-control form-control-sm bg-light" type="number" [(ngModel)]="col.value" required>
I checked this pattern in regex.com this was working here but not in Angular can some one please let
me know what I am doing wrong here.
It seems that ng-patter works with angularjs and not angular so you should try angular patternValidator directive instead angular docs
You need to escape for javascript and regex. For me it worked like this.
<input style="width: 65px;"
pattern="^-?[0-9]\\d*(\\.\\d+)?$"
class="form-control form-control-sm bg-light" type="number" [(ngModel)]="myProperty" required>
Since in angular html is not actual html but will be transformed to javascript so you need to escape regex special escape sequences again so that JS could understand its part of regex. If you dont escape :
html is as follows and wont work
<input style="width: 65px;" pattern="/^-?[0-9]\d*(\.\d+)?$/" class="form-control form-control-sm bg-light" type="number" [(ngModel)]="col.value" required>
In dom it is as follows
<input class="form-control form-control-sm bg-light" ng-pattern="/^-?[0-9]d*(.d+)?$/" required="" style="width: 65px;" type="number" ng-reflect-required="">
For managing a simple input like in your case you can use template ref to check validity
import { Component, ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'my-app',
template:
`<input style="width: 65px;" #ref (keyup)="trigger(ref)"
pattern="^-?[0-9]\\d*(\\.\\d+)?$"
class="form-control form-control-sm bg-light" type="number" [(ngModel)]="myProperty" required>
<h1>{{ myProperty }}</h1>`,
})
export class AppComponent {
myProperty = 222;
#ViewChild('ref') ref: ElementRef;
trigger(e) {
console.log(this.ref.nativeElement.validity.valid);
}
}
Simple version without template ref
myProperty = 22;
showError = false;
onkeydecimalCheck(e){
const value = e.target.value;
console.log(value);
var regex = new RegExp(/^-?[0-9]+(\.\d+)?$/);
const flag = regex.test(value) ;
if (!flag) {
this.showError = true;
} else {
this.showError = false;
}
}
<input style="width: 65px;" type="number" (keyup)="onkeydecimalCheck($event)"
[value]="myProperty" required>
<span *ngIf="showError">Invalid data</span>
We can display some error. The issue with number field is that if you type + at the start eventhough it displays + if you check event.target.value will be empty
I am using angular material date picker in one of my component's of angular project. This component has two tabs. Using *ngIf I am showing only one at a time based on what user has clicked.In one tab user selects a date and if navigate away to other tab of same component and comes back to previous one, I need to retain the selected date.
This is what I am doing in HTML side:
<mat-form-field class="dropdownWidth">
<input #dateInput matInput [matDatepickerFilter]="myFilter" [matDatepicker]="picker"
placeholder="Choose a date"
[value]="datePickerDate"
[(ngModel)]="datePickerDate"
(dateChange)="addDateEvent($event)"
[disabled]="selectedOperator.length === 0 && userDateRange.length === 0">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
And in TS file:
addDateEvent(event) {
this.datePickerEvent = event;
this.datePickerDate = new Date(`${event.value['_i'].month + 1}-${event.value['_i'].date}-${event.value['_i'].year}`);
this.formatDate = moment(event.value).format('YYYY-MM-DD');
}
But when I navigate back, date value is not retained. Any suggestions how can I achieve this?
Here is sample stackblitz
It does not work because you are not storing a selected value. So create a variable in typescript:
yourDate: any;
HTML:
<p> YourDate {{ yourDate | date }} </p>
<mat-form-field>
<input matInput [(ngModel)]="yourDate" [matDatepicker]="picker" placeholder="Choose a date">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
It is possible to see the whole code at stackblitz
In your example you are not using any bindings. Try to use [(ngModel)], so that it will take and hold the selected value.
Do like this, it will work:
<mat-form-field>
<input matInput [(ngModel)]="date" [matDatepicker]="picker" placeholder="Choose a date">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
I'm working with Angular and Typescript and I'm wondering how come that if I add a value of string for example "hahah" to a field which should accept numbers only and it's binded to property of type number also wont fire or trigger any error, insted of that value of that field will be zero! I mean that is fine for me, it's better to result as a zero than to result as a some strange value, but I'm just curious how is that?
Here is how my typescript class looks:
export class Article {
public id: string;
public price: number;
public price2: number;
}
Here is my template .html file:
<div class="form-group">
<label class="control-label dash-control-label col-sm-3" for="">Price:</label>
<div class="col-sm-3">
<input type="text" class="form-control dash-form-control" id="" placeholder="" name="price" [(ngModel)]="article.price">
</div>
<label class="control-label dash-control-label col-sm-3" for="">Price 2:</label>
<div class="col-sm-3">
<input type="text" class="form-control dash-form-control" id="" placeholder="" name="price2" [(ngModel)]="article.price2">
</div>
</div>
As you can see guys inputs are binded to [(ngModel)]="article.price" and to
[(ngModel)]="article.price2" and they looks like this when app is runned:
But everything is fine if I type something like this:
And when I do post, In my database is stored value : ZERO (0) !
How come?
Why there is no error like I'm trying to add a string to a number or whatever?
Thanks
Yeah!
Typescript changing the value depends by the property type without throwing error.
like
add(a:number , bnumber )
{
console.log(a+b);
}
add("1",2);// result is 3
So if you pass string add("aa" +"aa"); it return 0. because the function parameters (number n,number n2) is type. So it's converting a number (if it's not a number then it's assuming as 0)
Suggestion:
the better way is you can use type="number" instead of type="text" in your input element to avoiding to type any string value in the input box
<div class="form-group">
<label class="control-label dash-control-label col-sm-3" for="">Price:</label>
<div class="col-sm-3">
<input type="number" class="form-control dash-form-control" id="" placeholder="" name="price" [(ngModel)]="article.price">
</div>
<label class="control-label dash-control-label col-sm-3" for="">Price 2:</label>
<div class="col-sm-3">
<input type="number" class="form-control dash-form-control" id="" placeholder="" name="price2" [(ngModel)]="article.price2">
</div>
</div>
Typescript is a superset of javascript, it add static type checking.
So all the code you write in typescript will be transpiled in javascript At runtime which have no type safety.
Typescript mostly acts on compile time in this example we have no information on what the user will type so impossible to check it when compiling.
Typescript transpiling
If you have a code like this
function add(a: number, b: number) {
return a + b;
}
add('de', 'de');//compile error
typescript can know that you are calling the function with bad params, that won't compile.
you can play with it at https://www.typescriptlang.org/play/index.html
On runtime we will just have
function add(a, b) {
return a + b;
}
so if a library call it with bad params you cant check it or throw an error.
A way to solve it
That can be solved by Typescript if they had type guard on the transpiled version like this:
export function add(x: number, y: number): number {
if (typeof x !== 'number' || typeof y !== 'number') {
throw new TypeError('x and y need to be numbers');
}
return x + y;
}
for primitive type that can be simple but for classes it's more complex.
There's an issue for that https://github.com/Microsoft/TypeScript/issues/1573
But they claim it's against there Design Goals.