Angular Material matInput
September 14, 2018
Angular Material provides MatInput
Directive to create <input>
and <textarea>
element. To use MatInput
we need to import MatInputModule
. MatInput
provides errorStateMatcher
property to assign ErrorStateMatcher
object to control when to show validation error. Inputs are used with MatFormField
that applies common underline, floating label and hint messages etc. We use <mat-label>
, <mat-hint>
, <mat-error>
with <input matInput>
and <textarea matInput>
inside <mat-form-field>
.
On this page we will create input text, color, date, time, number and textarea using reactive form as well as template-driven form.
Contents
Import MatInputModule
To work with Angular Material<input>
and <textarea>
, we need to import MatInputModule
in application module.
import { MatInputModule } from '@angular/material/input'; @NgModule({ imports: [ ------ MatInputModule ], ------ }) export class AppModule { }
Use MatInput to create Inputs
Angular Material usesMatInput
Directive to create <input>
and <textarea>
inside a <mat-form-field>
. MatInput
Directive selector is matInput
. It is used with color, date, datetime-local, email, month, number, password, search, tel, text, time, url, week input types and textarea. MatInput
has following properties.
errorStateMatcher: This is
ErrorStateMatcher
object to control when error messages are shown.
readonly: This is Boolean value to know if element is readonly.
type: It gives input type of the element.
errorState: This is Boolean value to know if control is in error state or not.
Find the sample code snippet to create
<input>
and <textarea>
.
1. Find the code snippet to create
<input>
using matInput
selector.
<mat-form-field> <input matInput placeholder="Amount" [formControl]="amount" (change)="onAmountChange()"> </mat-form-field>
amount = new FormControl(); onAmountChange() { console.log(this.amount.value); }
<textarea>
using matInput
selector.
<mat-form-field> <textarea matInput placeholder="Comment" [formControl]="commentFC" (change)="onCommentChange()"> </textarea> </mat-form-field>
commentFC = new FormControl(); onCommentChange() { console.log(this.commentFC.value); }
Using <mat-form-field>
<input matInput>
and <textarea matInput>
are used inside <mat-form-field>
. It applies common underline, floating label and hint messages etc. <mat-form-field>
is used with <input matInput>
as given below.
1. Using <mat-label>
<mat-form-field> <mat-label>Enter City</mat-label> <input matInput placeholder="City"> </mat-form-field>
<mat-form-field> <mat-label>Enter City</mat-label> <input matInput placeholder="City"> <mat-hint>Native place city</mat-hint> </mat-form-field>
Suppose we have a
FormControl
object as following.
myCity = new FormControl('', Validators.required);
<mat-form-field> <mat-label>Enter City</mat-label> <input matInput placeholder="City" [formControl]="myCity"> <mat-hint>Native place city</mat-hint> <mat-error *ngIf="myCity.hasError('required')"> City is required. </mat-error> </mat-form-field>
<mat-form-field> <mat-label>Enter Amount</mat-label> <input matInput placeholder="Amount" [formControl]="amount"> <mat-hint>Indian Rupee</mat-hint> <span matPrefix>INR </span> <span matSuffix>.00</span> <mat-error *ngIf="amount.hasError('required')"> Amount is required. </mat-error> </mat-form-field>
Suppose we have a
FormControl
object as following.
commentFC = new FormControl('', [ Validators.required, Validators.maxLength(30) ]);
<mat-form-field> <mat-label>Write Your Comment</mat-label> <textarea matInput placeholder="Comment" [formControl]="commentFC" #comment></textarea> <mat-hint align="end">{{comment.value?.length || 0}}/30</mat-hint> <mat-error *ngIf="commentFC.hasError('required')"> Comment is required. </mat-error> <mat-error *ngIf="commentFC.hasError('maxlength')"> Max length is 30. </mat-error> </mat-form-field>
Validation of Input Text
We will validate input text here. We will validate for required, maximum value and minimum value of a number usingValidators
and FormControl
. Find the code snippet of TS file.
amount = new FormControl('', [ Validators.required, Validators.min(100), Validators.max(200) ]); onAmountChange() { console.log(this.amount.value); console.log(this.amount.valid); }
<mat-form-field> <mat-label>Enter Amount</mat-label> <input matInput placeholder="Amount" [formControl]="amount" (change)="onAmountChange()"> <mat-hint>Indian Rupee</mat-hint> <span matPrefix>INR </span> <span matSuffix>.00</span> <mat-error *ngIf="amount.hasError('required')"> Amount is required. </mat-error> <mat-error *ngIf="amount.hasError('min')"> Minimum Amount is 100. </mat-error> <mat-error *ngIf="amount.hasError('max')"> Maximum Amount is 200. </mat-error> </mat-form-field>
Validation of Textarea
We will validate input<textarea>
here. We will validate for required, maximum length using Validators
and FormControl
. Find the code snippet of TS file.
commentFC = new FormControl('', [ Validators.required, Validators.maxLength(30) ]); onCommentChange() { console.log(this.commentFC.value); console.log(this.commentFC.valid); }
<mat-form-field> <textarea matInput placeholder="Comment" [formControl]="commentFC" (change)="onCommentChange()"> </textarea> <mat-hint align="end">{{commentFC.value?.length || 0}}/30</mat-hint> <mat-error *ngIf="commentFC.hasError('required')"> Comment is required. </mat-error> <mat-error *ngIf="commentFC.hasError('maxlength')"> Max length is 30. </mat-error> </mat-form-field>
Find the print screen of the output for input and textarea.
Using Custom ErrorStateMatcher
Angular Material providesErrorStateMatcher
to control when to show error. It has isErrorState()
method that accepts FormControl
and FormGroupDirective
or NgForm
as arguments and returns Boolean. To create custom ErrorStateMatcher
class, we need to implement ErrorStateMatcher
and override isErrorState()
. If isErrorState()
returns true
, error will be shown and if false
then error will not be shown. To use it with <input>
and <textarea>
input, MatInput
Directive provides errorStateMatcher
property. We need to assign the object of custom ErrorStateMatcher
to errorStateMatcher
property. Find our custom ErrorStateMatcher
.
custom-error-state-matcher.ts
import { ErrorStateMatcher } from '@angular/material/core'; import { FormControl, FormGroupDirective, NgForm } from '@angular/forms'; export class CustomErrorStateMatcher implements ErrorStateMatcher { isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { const isSubmitted = form && form.submitted; return (control.invalid && (control.dirty || control.touched || isSubmitted)); } }
ErrorStateMatcher
can be used for a <input>
and <textarea>
using errorStateMatcher
property or globally for every <input>
and <textarea>
in the application.
a. To use
errorStateMatcher
property, we will create an instance of CustomErrorStateMatcher
in TS file.
esMatcher = new CustomErrorStateMatcher();
esMatcher
object with errorStateMatcher
property.
<mat-form-field> <mat-label>Enter Amount</mat-label> <input matInput placeholder="Amount" [formControl]="amount" [errorStateMatcher]="esMatcher" (change)="onAmountChange()"> <mat-hint>Indian Rupee</mat-hint> <span matPrefix>INR </span> <span matSuffix>.00</span> <mat-error *ngIf="amount.hasError('required')"> Amount is required. </mat-error> <mat-error *ngIf="amount.hasError('min')"> Minimum Amount is 100. </mat-error> <mat-error *ngIf="amount.hasError('max')"> Maximum Amount is 200. </mat-error> </mat-form-field> <br/> <mat-form-field> <textarea matInput placeholder="Comment" [formControl]="commentFC" [errorStateMatcher]="esMatcher" (change)="onCommentChange()"> </textarea> <mat-hint align="end">{{commentFC.value?.length || 0}}/30</mat-hint> <mat-error *ngIf="commentFC.hasError('required')"> Comment is required. </mat-error> <mat-error *ngIf="commentFC.hasError('maxlength')"> Max length is 30. </mat-error> </mat-form-field>
ErrorStateMatcher
globally, configure provider in application module as following.
providers: [ {provide: ErrorStateMatcher, useClass: CustomErrorStateMatcher} ]
CustomErrorStateMatcher
will be applied to all <input>
and <textarea>
element in the application. Angular material also provides ShowOnDirtyErrorStateMatcher
that matches when a control is invalid and dirty.
Reactive Form Example with matInput
Find the complete code to create inputs using reactive form.reactive-form.component.html
<h4>1. Input Text validation</h4> <mat-form-field> <mat-label>Enter Amount</mat-label> <input matInput placeholder="Amount" [formControl]="amount" [errorStateMatcher]="esMatcher" (change)="onAmountChange()"> <mat-hint>Indian Rupee</mat-hint> <span matPrefix>INR </span> <span matSuffix>.00</span> <mat-error *ngIf="amount.hasError('required')"> Amount is required. </mat-error> <mat-error *ngIf="amount.hasError('min')"> Minimum Amount is 100. </mat-error> <mat-error *ngIf="amount.hasError('max')"> Maximum Amount is 200. </mat-error> </mat-form-field> <h4>2. Textarea validation</h4> <mat-form-field> <textarea matInput placeholder="Comment" [formControl]="commentFC" [errorStateMatcher]="esMatcher" (change)="onCommentChange()"> </textarea> <mat-hint align="end">{{commentFC.value?.length || 0}}/30</mat-hint> <mat-error *ngIf="commentFC.hasError('required')"> Comment is required. </mat-error> <mat-error *ngIf="commentFC.hasError('maxlength')"> Max length is 30. </mat-error> </mat-form-field> <h4>3. Textarea autosize</h4> <mat-form-field> <textarea matInput placeholder="Description" [formControl]="descFC" cdkTextareaAutosize cdkAutosizeMinRows="2" cdkAutosizeMaxRows="5" #autosize="cdkTextareaAutosize" (change)="onDescChange()"> </textarea> <mat-error *ngIf="descFC.hasError('required')"> Description is required. </mat-error> </mat-form-field> <h4>4. resizeToFitContent() and Reset() test</h4> <div> <mat-form-field [style.fontSize]="fontSize.value"> <textarea matInput placeholder="Content" [formControl]="contentFC" cdkTextareaAutosize cdkAutosizeMinRows="2" cdkAutosizeMaxRows="5" #cfcAutosize="cdkTextareaAutosize"> </textarea> </mat-form-field> </div> <div> <button mat-raised-button (click)="resetTextAreaSize()">Reset Textarea Size</button> </div> <br/> <div> <mat-form-field> <mat-label>Select Font size</mat-label> <mat-select #fontSize value="15px" (selectionChange)="resizeTextArea()"> <mat-option value="10px">10px</mat-option> <mat-option value="15px">15px</mat-option> <mat-option value="20px">20px</mat-option> </mat-select> </mat-form-field> </div> <h3>5. Person Reactive Form</h3> <form [formGroup]="personForm" (ngSubmit)="onFormSubmit()"> <div> <mat-form-field> <input matInput placeholder="Username" formControlName="username" [errorStateMatcher]="esMatcher"> <mat-error *ngIf="username.hasError('required')"> Username is required. </mat-error> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="password" formControlName="password" placeholder="Password" [errorStateMatcher]="esMatcher"> <mat-error *ngIf="password.hasError('required')"> Password is required. </mat-error> </mat-form-field> </div> <div> <mat-form-field> <textarea matInput placeholder="Address" formControlName="address" cdkTextareaAutosize cdkAutosizeMinRows="2" cdkAutosizeMaxRows="4" [errorStateMatcher]="esMatcher"> </textarea> <mat-error *ngIf="address.hasError('required')"> Comment is required. </mat-error> <mat-error *ngIf="address.hasError('maxlength')"> Max length is 100. </mat-error> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="color" formControlName="favColor" placeholder="Favorite color"> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="date" formControlName="dob" placeholder="Date of birth"> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="time" formControlName="tob" placeholder="Time of birth"> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="number" formControlName="age" placeholder="Age"> </mat-form-field> </div> <div> <button mat-raised-button>Submit</button> </div> </form>
import { Component, OnInit, ViewChild, NgZone } from '@angular/core'; import { FormControl, Validators, FormBuilder } from '@angular/forms'; import { CdkTextareaAutosize } from '@angular/cdk/text-field'; import { take } from 'rxjs/operators'; import { CustomErrorStateMatcher } from './custom-error-state-matcher'; import { PersonService } from './person.service'; @Component({ selector: 'app-reactive', templateUrl: './reactive-form.component.html' }) export class ReactiveFormComponent implements OnInit { constructor(private ngZone: NgZone, private formBuilder: FormBuilder, private personService: PersonService) {} ngOnInit() { } esMatcher = new CustomErrorStateMatcher(); //Input text validation amount = new FormControl('', [ Validators.required, Validators.min(100), Validators.max(200) ]); onAmountChange() { console.log(this.amount.value); console.log(this.amount.valid); } //Textarea validation commentFC = new FormControl('', [ Validators.required, Validators.maxLength(30) ]); onCommentChange() { console.log(this.commentFC.value); console.log(this.commentFC.valid); } //Textarea autosize descFC = new FormControl('', [ Validators.required ]); @ViewChild('autosize') txtAreaAutosize: CdkTextareaAutosize; onDescChange() { console.log("enabled: "+ this.txtAreaAutosize.enabled); console.log("minRows: "+ this.txtAreaAutosize.minRows); console.log("maxRows: "+ this.txtAreaAutosize.maxRows); console.log("Description: "+ this.descFC.value); } //resizeToFitContent() and Reset() test contentFC = new FormControl(); @ViewChild('cfcAutosize') contentFCAutosize: CdkTextareaAutosize; resizeTextArea() { this.ngZone.onStable.pipe(take(1)) .subscribe(() => this.contentFCAutosize.resizeToFitContent(true)); } resetTextAreaSize() { this.contentFCAutosize.reset(); } //Create a form personForm = this.formBuilder.group({ username: ['', Validators.required], password: ['', Validators.required], address: ['', [Validators.required, Validators.maxLength(100)]], favColor: '#e66465', dob: '', tob: '', age: '' }); onFormSubmit() { this.personService.savePerson(this.personForm.value); } get username() { return this.personForm.get('username'); } get password() { return this.personForm.get('password'); } get address() { return this.personForm.get('address'); } }
export interface Person { username: string; password: string; address: string; favColor: string; dob: string; tob: string; age: number; }
import { Injectable } from '@angular/core'; import { Person } from './person'; @Injectable({ providedIn: 'root' }) export class PersonService { savePerson(person: Person) { console.log(person); } }
Find the print screen of the output.
Template-Driven Form Example with matInput
Find the complete code to create inputs using template-driven form.template-driven-form.component.html
<h3>6. Person Template-Driven Form</h3> <form #personForm="ngForm" (ngSubmit)="onFormSubmit(personForm)"> <div> <mat-form-field> <input matInput placeholder="Username" name="username" required ngModel #name="ngModel" [errorStateMatcher]="esMatcher"> <mat-error *ngIf="name.hasError('required')"> Username is required. </mat-error> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="password" name="password" ngModel required placeholder="Password" #pwd="ngModel" [errorStateMatcher]="esMatcher"> <mat-error *ngIf="pwd.hasError('required')"> Password is required. </mat-error> </mat-form-field> </div> <div> <mat-form-field> <textarea matInput placeholder="Address" name="address" required maxlength="100" ngModel #address="ngModel" cdkTextareaAutosize cdkAutosizeMinRows="2" cdkAutosizeMaxRows="4" [errorStateMatcher]="esMatcher"> </textarea> <mat-error *ngIf="address.hasError('required')"> Comment is required. </mat-error> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="color" name="favColor" [ngModel]="'#e66465'" placeholder="Favorite color"> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="date" name="dob" ngModel placeholder="Date of birth"> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="time" name="tob" ngModel placeholder="Time of birth"> </mat-form-field> </div> <div> <mat-form-field> <input matInput type="number" name="age" ngModel placeholder="Age"> </mat-form-field> </div> <div> <button mat-raised-button>Submit</button> </div> </form>
import { Component, OnInit } from '@angular/core'; import { CustomErrorStateMatcher } from './custom-error-state-matcher'; import { PersonService } from './person.service'; @Component({ selector: 'app-template-driven', templateUrl: './template-driven-form.component.html' }) export class TemplateDrivenFormComponent implements OnInit { constructor(private personService: PersonService) {} ngOnInit() { } esMatcher = new CustomErrorStateMatcher(); onFormSubmit(form) { this.personService.savePerson(form.value); } }
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <app-reactive></app-reactive> <app-template-driven></app-template-driven> ` }) export class AppComponent { }
import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatButtonModule } from '@angular/material/button'; import { ErrorStateMatcher } from '@angular/material/core'; import { CustomErrorStateMatcher } from './custom-error-state-matcher'; import { AppComponent } from './app.component'; import { ReactiveFormComponent } from './reactive-form.component'; import { TemplateDrivenFormComponent } from './template-driven-form.component'; @NgModule({ declarations: [ AppComponent, ReactiveFormComponent, TemplateDrivenFormComponent ], imports: [ BrowserModule, BrowserAnimationsModule, FormsModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatButtonModule ], providers: [ // {provide: ErrorStateMatcher, useClass: CustomErrorStateMatcher} ], bootstrap: [AppComponent] }) export class AppModule { }
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
References
Angular Material InputCdkTextareaAutosize Directive