How to close an Angular Material Mat Datepicker Calendar on enter key - html

Is it possible to close the Mat Datepicker Calendar window when pressing the enter key? Right now if I click my form input field the calendar opens. I want to be able to press the enter key but close the calendar window and not have it stay open.
<form #form="ngForm" (keyup.enter)="sumbitByEnter($event)">
<mat-form-field floatLabel="never">
<input name="id" matInput placeholder="Search for student by ID number"
[(ngModel)]="model.id" minlength="9" maxlength="9" #uname="ngModel">
</mat-form-field>
<mat-form-field>
<input name="startDate" matInput [max]="model.endDate" [matDatepicker]="picker"
(click)="picker.open()" (focus)="picker?.open()" placeholder="Start Date"
[(ngModel)]="model.startDate">
<mat-datepicker-toggle matSuffix></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</form>

You can get access to your date picker via #ViewChild and to listed for global enter event and then once user press enter you can catch this event end call "close()" which will close the panel. So it will be like this in your TS file:
// getting reference to date picker
#ViewChild(MatDatepicker) private rangePicker: MatDatepicker<Date>;
globalEnter = fromEvent<KeyboardEvent>(document, 'keyup')
.pipe(
filter((event) => event.keyCode === ENTER),
tap(console.log)
)
.subscribe(() => this.rangePicker.close())
Of course import all imports (ENTER you can import from '#angular/cdk/keycodes') and also handle unsubscribtion properly etc.

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()">

Template Driven Form Mark All as touched - Angular 7

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();
});

Disabled Datepicker is validated

I have a form class with several inputs. There is a Checkbox, which enables/disables Datepicker input (Datepicker is not required) based on boolean variable (switchDate). Even if the Datepicker is disabled, it will be still validated and causing whole form to have status invalid.
My submit form button condition is: [disabled]="productForm.invalid". Unfortunately this button will be disabled without valid Datepicker field.
My question is: how to validate Datepicker field only if it is enabled?
PS I am newbie to Angular
I've tried to change submit button disabled criteria to focus on other fields errors, but this way it will pass any input in Datepicker
I was trying to find some method to check if the Datepicker field is enabled/disabled inside submit button condition, but no luck here
<input type="checkbox"
name="switcherChk"
id="switcherChk"
checked="true"
(change)="changeDatepickerVisibility()" />
<mat-form-field class="example-full-width">
<input matInput [matDatepicker]="picker"
[min]="todaysDate"
placeholder="Example text"
formControlName="userDate"
[attr.disabled]="switchDate ? '' : null"
required = false
>
<mat-datepicker-toggle matSuffix [for]="picker">
<mat-icon matDatepickerToggleIcon>keyboard_arrow_down</mat-icon>
</mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error *ngIf="productForm.controls['userDate'].invalid">
Date should be greater or equal to current date.
</mat-error>
</mat-form-field>
Resolved this way (on the Component side):
private changeUserDateValidators(): void{
var dateControl = this.productForm.get(this.UserDateField);
if (this.switchDate) {
dateControl.setValidators(Validators.required);
}
else {
dateControl.clearValidators();
}
dateControl.updateValueAndValidity();
}
Depending on what you want to achieve, you can replace Validators.required to your custom validator.

input onfocus event not detecting html element when reference them by #

Todo :
I want to open a date picker "#picker" when its input has focus but it's not working when using onfocus input event and gives an error:
error : 'picker is not defined'
<mat-form-field style="width: 100%;">
<input matInput [matDatepicker]="picker" onfocus="picker.open()"
placeholder="Choose a date">
<mat-datepicker-toggle matSuffix [for]="picker">
</mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
but when I did it by a button press it worked
<button mat-button (click)="picker.open()">show date picker</button>
how can I do this by focusing on an input
You should use Angular event binding:
<input ... (focus)="picker.open()" >
See this stackblitz for a demo.

Reset Angular Formly Form Validations on click event

I am implementing dynamic forms using the angular formly module and It's working fine. What functionality I need is Initially there should be a select box which contains multiple options, Based on selection different form fields should be displayed. As I explained I have Implemented and it's working, here what my problem is If I select option 1 and If I submit the form without filling fields, form displaying validation errors that is also cool. But when I select option 2 form fields are changing, but by default, all required fields are showing errors. How can I resist this? Please suggest me.
html
<div class="row">
<mat-form-field class="col-lg-2">
<mat-select placeholder="Form For" (selectionChange)="getSelectedFormName($event)">
<mat-option value="uf001">UF001</mat-option>
<mat-option value="uf002">UF002</mat-option>
<mat-option value="uf003">UF003</mat-option>
</mat-select>
</mat-form-field>
<div class="col-lg-4">
<button type="button" class="btn btn-default btn-one" (click)="getDynamicForm()">GET FORM</button>
</div>
</div>
<form [formGroup]="form" (ngSubmit)="submit(model)" >
<formly-form [model]="model" [fields]="fields" [form]="form" *ngIf="isFormTypeSelected" >
</formly-form>
<button type="submit" class="btn btn-success">Submit</button>
</form>
ts file
getSelectedFormName(eve) {
this.isFormSaved = false;
this.form = new FormGroup({});
this.fields=[];
this.model = {};
this.parentFormName = eve.value;
}
getDynamicForm() {
this.isFormSaved = false;
this.savedFields=[];
this.getDynamicFormBasedOnSelection(this.parentFormName);
//fields getting from api call
}
getDynamicFormBasedOnSelection(formName: string) {
this.auth.getDynamicFormBasedOnSelction(formName, this.userAgencyCode).subscribe(
(result) => {
const str = JSON.stringify(result);
this.fields = JSON.parse(str);
this.isFormTypeSelected = true;
this.addEvents(this.fields);
});
}
Here I'm providing my screens which are for better understanding
Actually form.reset() just reset the form values. You need to reset the form directive too. for eg.
<form [formGroup]='authForm' (submit)='submitForm(formDirective)' #formDirective="ngForm" class="is-mat-form">
<mat-form-field>
<input matInput placeholder="Email ID" formControlName='login'>
<mat-error *ngIf="authForm.controls.login.hasError('required')">
Email is required
</mat-error>
<mat-error *ngIf="authForm.controls.login.hasError('email')">
Please enter a valid email address
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" formControlName='password' placeholder="Password">
<mat-error *ngIf="authForm.controls.password.hasError('required')">
Password is required
</mat-error>
<mat-error *ngIf="authForm.controls.password.hasError('minlength')">
Password must be minimum 6 digit long.
</mat-error>
</mat-form-field>
and .ts file is
submitForm(formDirective: FormGroupDirective){
if (this.authForm.invalid) {
console.log('form submitted')
this.authForm.reset()
return;
}
this will reset the form values only, to reset the form error we need to reset the formdirective as well.
submitForm(formDirective: FormGroupDirective){
if (this.authForm.invalid) {
console.log('form submitted')
this.authForm.reset()
formDirective.resetForm();
return;
}