Template Driven Form Mark All as touched - Angular 7 - html

I am using template driven forms for validations. And I would like to Mark all fields as touched when the user blur on the last required field. currently I am only able to do this by passing the form and individually doing each field. From research I see there is a way to do MarkAllAsTocuhed but it's throwing an error. Is there a better/correct way to do this with Angular 7. I also tried looping through the controls but since it's an object that also does not work.
.HTML
<form #myForm="ngForm">
<mat-form-field class="input-field">
<input #field1="ngModel" name="name1"
[(ngModel)]="fieldOne" type="text" matInput placeholder="Field 1" required>
</mat-form-field>
<mat-form-field class="input-field">
<input #field2="ngModel" name="name2"
[(ngModel)]="fieldTwo" type="text" matInput placeholder="Field 2" required>
</mat-form-field>
<mat-form-field class="input-field">
<input #field2="ngModel" name="name3"
[(ngModel)]="fieldThree" type="text" matInput placeholder="Field 3" required>
</mat-form-field>
<mat-form-field class="input-field">
<input #field3="ngModel" name="name4"
[(ngModel)]="fieldFour" type="text" matInput placeholder="Field 4"
(blur)="touchAllFields(myForm.form)" required>
</mat-form-field>
</form>
.TS
touchAllFields(form){
//Working Version
form.controls.name1.markAsTouched();
form.controls.name2.markAsTouched();
form.controls.name3.markAsTouched();
form.controls.name4.markAsTouched();
form.controls.name5.markAsTouched();
form.controls.name6.markAsTouched();
form.controls.name7.markAsTouched();
form.controls.name8.markAsTouched();
//Failed Attempt
form.controls.markAllAsTouched(); //typeError: form.controls.markAllAsTouched is not a function
//Failed Attempt
for(var i = 0; i < form.controls.length; i++){
form.controls[i].markAllAsTouched(); //Failed since its an object and not an array
}
}

ngForm itself has no markAllAsTouched method since it's not a FormGroup or FormArray. However, NgForm has an instance of a formGroup accessible via .form or .control - source.
This has been available since Angular2 until the current latest version (Angular10)
So the correct answer to your questions should be as follows:
form.form.markAllAsTouched();
or
form.control.markAllAsTouched();

You can try this:
Found out that Object.keys can handle this.
Object.keys(this.form.controls).forEach(key => {
this.form.get(key).markAsTouched();
});
For Angular 8+, you can use:
Object.keys(this.form.controls).forEach(key => {
this.form.controls[key].markAsTouched();
});

Related

How do I add a listener to a search form in Angular material?

I'm working with Angular material and I have a search form. As shown in the following code, I have a button and after a click event on the button, the words written in the search form are given to the searchProductByName method as parameters. I'd like to substitute the button with a listener that, after having written something in the form and having clicked on enter (instead of the button), grabs the things written in the search form and passes them to the searchProductByName method as parameters. Is there a way to do so?
<form class="search-form" >
<mat-form-field class="example-full-width" appearance="fill">
<mat-label >Search</mat-label>
<input #input type="text" ngModel name="name" class="form-control" id="name"
aria-label="Search"
matInput
>
<button (click)="searchProductByName(input.value)" routerLink="/products/searchProduct/by_name" routerLinkActive="active">Go</button>
</mat-form-field>
</form>
Easy way to do that in template is add keyup event to the input:
<form class="search-form" >
<input #input type="text" ngModel name="name" class="form-control" id="name"
(keyup.enter)="searchProductByName(input.value)"
aria-label="Search"
matInput
>
<button (click)="searchProductByName(input.value)" routerLink="/products/searchProduct/by_name" routerLinkActive="active">Go</button>
</mat-form-field>
</form>
Or you can get your input element in component with ViewChild and make listener there.
If the searchPruductByName is a method that is using http request to search from backend, for example - I would suggest to also implement debounce'ing the search as well. Otherwise it would search with each keystroke. A nice elegant way would be to use a debounce decorator. https://medium.com/time-machine/how-to-implement-debounce-decorator-2e963b70cd27
<form class="search-form" >
<input #input type="text" ngModel name="name" class="form-control" id="name"
(input)="searchProductByName($event); onInput($event)"
aria-label="Search"
matInput>
</mat-form-field>
</form>
onInput(event: any) {
console.log(event.target.value);
}
To navigate to link on pressing "Enter" add to constructor Router and then use router to navigate.
constructor(router: Router) {}
onNavigate() {
// other stuff
this.router.navigateByUrl('/products/searchProduct/by_name')
}
<input (keyup.enter)="searchProductByName(input.value); onNavigate()">

The value [value] is not assigned in input

I have a little problem using [value] in my inputs.
This is my component .html
<div class="form-group">
<mat-form-field class="example-full-width" style="padding-left: 15px;">
<input matInput [value]='value_2_' [(ngModel)]="form.name_1" placeholder="name_1"
name="name_pro_1" required>
</mat-form-field>
</div>
Everything looks good, but when I run the program, the value is shown in the corresponding input, however, being a value required by the input, it is still in red, it is solved until I add a letter to the value or remove something from the input.
I managed to solve the error by deleting
name = "name_pro_1"
but since I use NgModel I cannot remove the name because I get other errors. Like:
core.js:4352 ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form
control must be defined as 'standalone' in ngModelOptions.
Example 1: <input [(ngModel)]="person.firstName" name="first">
Example 2: <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone: true}">
I think the problem is [(ngModel)]="form.name_1". Try to replace that with
[(ngModel)]="testValue" and define a variable in the class with the same name and it will work.
I can't replicate the problem, but using [(ngModel)] doesn't make using [value] unnecessary? You can set the form.name_1 to the value you want on the component.ts file.
Finally i resolved the issue, that it's my solution:
First thing first, i delete the [(ngModel)] in the input of component.html, then i used (change) to call the function when user select a SKU
<div class="form-group input-sku" >
<mat-form-field class="example-full-width">
<mat-label>SKU</mat-label>
<input type="text" placeholder="Search.." id="sku_select" aria-label="number"
matInput [formControl]="myControl" [matAutocomplete]="auto"
[(ngModel)]="selectedValue" (change)="changedata_1($event)" >
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
<mat-option *ngFor="let data of filter_products | async" [value]="data.sku">{{data.sku}}</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
Input who recived the value:
<div class="form-group inputs-name">
<mat-form-field class="example-full-width">
<mat-label>Name</mat-label>
<input type="text" matInput id="nombre_1" required [value]="name_1" name="name_1" placeholder="Name" >
</mat-form-field>
</div>
And in my component.ts
changedata_1(e) {
this.nombre_1 = e.target.value;
this.DataToSave.nombre_1 = this.nombre_1;}
private _filter(value: string): Products_BD[] {
const filterValue = value.toLowerCase();
const data_ = this.DatasProducts.filter(DataProducts => DataProducts.sku.toLowerCase().includes(filterValue));
for(let element of data_){
const sku1= elemento.sku;
const name1 = element.name;
this.name_1 = name1;
this.DataToSave.sku_1 = sku1;
this.DataToSave.name_1 = name1;
}
return this.DataProducts.filter(DataProducts => DataProducts.sku.toLowerCase().includes(filterValue));}
this.filter_products = this.myControl.valueChanges.pipe(
startWith(''),
map(value => this._filter(value))
);
I hope this solution helps! :)

Angular Input Placeholder on the top

I am new in Angular and I have to create an input field like that:
Everything is ok besides the Vorname placeholder on the top.
How can I put it there?
I am using Angular Materials.
<div class='grid-container-left'>
<mat-form-field appearance="outline">
<input matInput placeholder={{angeforderteVerfueger.request.vorname}} readonly>
</mat-form-field>
</div>
Thank you in advance!
Answer:
The below provided solutions works, just have to add floatLabel value with always.
<mat-form-field appearance="outline" floatLabel="always">
<mat-label>Vorname</mat-label>
<input matInput [placeholder]="angeforderteVerfueger.request.vorname" readonly>
</mat-form-field>
Try to add a label before your input:
<mat-label>Vorname</mat-label>
What you are doing is binding a property using string interpolation which is the wrong syntax. Try this
<div class='grid-container-left'>
<mat-form-field appearance="outline">
<mat-label>Vorname</mat-label>
<input matInput [placeholder]="angeforderteVerfueger.request.vorname" readonly>
</mat-form-field>
</div>

How to disable a text area or mat-form-field in the same HTML (Angular 9)

I am using Angular 9 with angular Material Reactive Forms. I want to disable the fields, but without using the disabled status on the formControl, just only using HTML instead of programmatically in ts.
My Html:
<form name="nameForm" [formGroup]="nameForm" fxLayout="column">
<mat-form-field fxFlex="50" class="m-4">
<input matInput name="startTime" formControlName="startTime" placeholder="{{'DATE.START' | translate}}" type="date">
</mat-form-field>
<mat-form-field fxFlex="30" class="m-4">
<input matInput name="period" formControlName="period" placeholder="{{'TITLE.PERIOD' | translate}}" type="number" step="0.1">
</mat-form-field>
<mat-form-field fxFlex="20" class="m-4">
<input matInput name="range" formControlName="range" placeholder="{{'TITLE.RANGE' | translate}}" type="number" step="0.1">
</mat-form-field>
</form>
My ts:
ngOnInit(): void {
this.nameForm = this.createNameForm();
... some code ...
}
private createNameForm() {
return this.formBuilder.group({
startTime: new FormControl(this.startTime, []),
period: new FormControl(this.period, []),
range: new FormControl(this.range, []),
});
}
... some code ...
I came across this doubt because some questions like this one (How to disable a text area or mat-form-field) has programmatic answers, and I would like to know if it can be solved using exclusively the Html. I know [disabled]="true" does not work, but maybe with a different directive?.
I hope it's clear enough and thanks in advance.
I think that the disabled directive is not valid for the input (it would work with a select or text-area for example ) but to disable the input we can use the readonly directive
[readonly]="true"
[readonly]="method()"
[readonly]="variable"
Although, if you need to disable it, another solution would be
[attr.disabled]="true"
[attr.disabled]="method()"
[attr.disabled]="variable"
I've tried both and they work :)
I have tried both the [disabled] = 'true' and [readonly] = 'true' for the textarea.
Only [readonly] property works well in all the cases in angular.

Default type=email validation being ignored

I'm building an Angular Material registration form. One of the input elements is type=email.
The expected behavior is, on submit, if the email is not in proper email format the form will not be valid and I will get a default browser error, seen here:
Code pen to test desired behavior
However, that's not what's happening at all. I'm using Material components to build out the form:
<form class="form" [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<mat-form-field class="form-full">
<input matInput placeholder="First Name" style="width:350px" formControlName="firstName" required />
</mat-form-field>
<mat-form-field class="form-full">
<input matInput placeholder="Last Name" style="width:350px" formControlName="lastName" required />
</mat-form-field>
<mat-form-field class="form-full">
<input matInput placeholder="Email" style="width:350px" type="email" formControlName="email" required />
</mat-form-field>
<mat-form-field class="form-full">
<input matInput placeholder="Password" style="width:350px" type="password" formControlName="password" required />
</mat-form-field>
<mat-form-field class="form-full">
<input matInput placeholder="Confirm Password" style="width:350px" type="password" formControlName="confirmPassword" required />
</mat-form-field>
<mat-card-actions>
<button mat-raised-button mat-button color="primary" type="submit">Register</button>
</mat-card-actions>
</form>
My register.component.ts is very basic right now, as I just started building out the component. I bind a form group to my registration form and console out some values in the onSubmit.
export class RegisterComponent {
registerForm;
constructor(private fb: FormBuilder) {
this.registerForm = fb.group({
firstName: '',
lastName: '',
email: '',
password: '',
confirmPassword: '',
})
}
onSubmit() {
console.log(this.registerForm.value);
console.log(this.registerForm.valid);
}
}
I expect registrationForm.valid to be false and a browser error to display when the submit button is pressed, however that is not happening. Of course, I can validate this myself - but not getting desired behavior is driving me nuts.
Here are the results:
Any thoughts?
You should be able to use the EmailValidator Directive, just add email attribute to your input syntax.
<input matInput placeholder="Email" style="width:350px" type="email" formControlName="email" email required />
https://angular.io/api/forms/EmailValidator