Angular Material Textarea

By Arvind Rai, April 14, 2024
This page will walk through Angular Material textarea example. Angular Material provides MatInput Directive to create <input> and <textarea> element with a MatFormField. To use MatInput we need to import MatInputModule in application module. MatInput provides errorStateMatcher property to assign ErrorStateMatcher object to control when to show validation error.
Angular Component Dev Kit (CDK) provides CdkTextareaAutosize Directive to automatically resize a textarea to fit its content. It provides cdkTextareaAutosize property to enable autosizing, cdkAutosizeMinRows property to define minimum amount of rows and cdkAutosizeMaxRows property to define maximum amount of rows in the textarea for autosizing. CdkTextareaAutosize also provides resizeToFitContent() method to resize the text area to fit its content and reset() method to reset the textarea to original size.
On this page we will create Angular Material textarea and validate it. We will use CdkTextareaAutosize Directive for autosizing and will create reactive and template-driven forms with Angular Material textarea.

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 Textarea

Angular Material uses MatInput Directive to create <input> and <textarea> inside a <mat-form-field>. MatInput Directive selector is matInput. Find the code snippet to create a <textarea> using matInput selector.
<mat-form-field>
  <textarea matInput 
     placeholder="Comment" 
     [formControl]="commentFC" 
     (change)="onCommentChange()">
  </textarea>
</mat-form-field> 
Find the code snippet for TS.
commentFC = new FormControl();
onCommentChange() {
  console.log(this.commentFC.value);
} 
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.

Validation

To apply validation on <textarea> input, we can use Angular Validators as usual. Find the code snippet of TS file.
commentFC = new FormControl('', [
  Validators.required, 
  Validators.maxLength(30)
]); 
Find the code snippet to create <textarea> in HTML template.
<mat-form-field>
  <textarea matInput 
      placeholder="Comment" 
      [formControl]="commentFC" 
      (change)="onCommentChange()">
  </textarea>
  <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> 
Error messages are shown using mat-error and mat-form-field associates error messages with matInput. By default errors are shown in the state of invalid control, touched or form submitted. We can use custom ErrorStateMatcher to change default behavior when to show error.

Using Custom ErrorStateMatcher

Angular Material provides ErrorStateMatcher 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));
    }
} 
Our custom 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(); 
Now use esMatcher object with errorStateMatcher property.
<mat-form-field>
 <textarea matInput 
	placeholder="Comment" 
	[formControl]="commentFC" 
	[errorStateMatcher]="esMatcher"
	(change)="onCommentChange()">
 </textarea>
 <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> 
b. To use custom ErrorStateMatcher globally, configure provider in application module as following.
providers: [
  {provide: ErrorStateMatcher, useClass: CustomErrorStateMatcher}
] 
Now 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.

Using CdkTextareaAutosize

CdkTextareaAutosize Directive is used to automatically resize a textarea to fit its content. Component Dev Kit (CDK) provides high quality predefined behavior for the components. CDK allows us to add common interaction patterns with minimal effort.
Find the CdkTextareaAutosize properties to use with textarea.
1. cdkTextareaAutosize
It defines whether autosizing is enabled or not. cdkTextareaAutosize is used with <textarea> element. It is declared in CdkTextareaAutosize Directive as following.
@Input('cdkTextareaAutosize')
get enabled(): boolean { return this._enabled; } 
We can use enabled property in TS file to know if autosize is enabled or not.
2. cdkAutosizeMinRows
It defines minimum amount of rows in the textarea for autosizing. cdkAutosizeMinRows is used with <textarea> element. It is declared in CdkTextareaAutosize Directive as following.
@Input('cdkAutosizeMinRows')
get minRows(): number { return this._minRows; } 
We can use minRows property in TS file to get configured autosize minimum rows.
3. cdkAutosizeMaxRows
It defines maximum amount of rows in the textarea for autosizing. cdkAutosizeMaxRows is used with <textarea> element. It is declared in CdkTextareaAutosize Directive as following.
@Input('cdkAutosizeMaxRows')
get maxRows(): number { return this._maxRows; } 
We can use maxRows property in TS file to get configured autosize maximum rows.

Now find the sample example of textarea with autosize configurations.
<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> 
Find the TS file code snippet.
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);
} 
4. resizeToFitContent()
resizeToFitContent() method of CdkTextareaAutosize is used to resize the text area to fit its content. It accepts Boolean value. By passing true we can force a height calculation. By default, height calculation is performed only when value changed since the last call.
5. reset()
reset() method of CdkTextareaAutosize resets the textarea to original size. When we configure cdkAutosizeMinRows and cdkAutosizeMaxRows in textarea, it is auto resized. On calling reset(), textarea resizes to its original size.

Find the HTML code snippet to test resizeToFitContent() and reset().
<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-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> 
Find the TS file code snippet.
constructor(private ngZone: NgZone) {} 
contentFC = new FormControl();

@ViewChild('cfcAutosize') 
contentFCAutosize: CdkTextareaAutosize;

resizeTextArea() {
  this.ngZone.onStable.pipe(take(1))
	.subscribe(() => this.contentFCAutosize.resizeToFitContent(true));
}
resetTextAreaSize() {
  this.contentFCAutosize.reset();
} 
NgZone is an injectable service for executing work inside or outside of the Angular zone.

Create readonly Textarea

To create a readonly textarea, use readonly property with <textarea> element.
<mat-form-field>
  <textarea matInput readonly
  cdkTextareaAutosize
  cdkAutosizeMinRows="2"
  cdkAutosizeMaxRows="5">
    Text Line 1
    Text Line 2
    Text Line 3
  </textarea>
</mat-form-field> 

Reactive Form Example using Textarea

Find the complete code to create textarea using reactive form.
reactive-form.component.html
<h4>1. Textarea validation</h4>
<div>
  <mat-form-field>
    <textarea matInput 
        placeholder="Comment" 
        [formControl]="commentFC" 
        [errorStateMatcher]="esMatcher"
        (change)="onCommentChange()">
    </textarea>
    <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>
</div>
<h4>2. Textarea autosize</h4>
<div>
  <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>
</div>

<h4>3. 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>

<h4>4. Person Reactive Form</h4>
<form [formGroup]="personForm" (ngSubmit)="onFormSubmit()">
  <div>
    <mat-form-field>
      <input matInput placeholder="Name" 
      formControlName="name" 
      [errorStateMatcher]="esMatcher">
      <mat-error *ngIf="name.hasError('required')">
        Name 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>   
      <button mat-raised-button>Submit</button>
  </div>  
</form> 
reactive-form.component.ts
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() {
  } 
  //Textarea validation
  esMatcher = new CustomErrorStateMatcher();  

  commentFC = new FormControl('', [
    Validators.required, 
    Validators.maxLength(30)
  ]);
  onCommentChange() {
    console.log(this.commentFC.value);
  }

  //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({
    name: ['', Validators.required],
    address: ['', [Validators.required, Validators.maxLength(100)]]
  });
  onFormSubmit() {
    this.personService.savePerson(this.personForm.value);
  }
  get name() {
    return this.personForm.get('name');
  }  
  get address() {
    return this.personForm.get('address');
  }    
} 
person.ts
export interface Person {
  name: string;
  address: string;
} 
person.service.ts
import { Injectable } from '@angular/core';
import { Person } from './person';

@Injectable({
  providedIn: 'root'
})
export class PersonService {
  savePerson(person: Person) {
     console.log(person);  
  }
} 

Template-Driven Form Example using Textarea

Find the complete code to create textarea using template-driven form.
template-driven-form.component.html
<h4>5. Person Template-Driven Form</h4>
<form #personForm="ngForm" (ngSubmit)="onFormSubmit(personForm)">  
  <div>
    <mat-form-field>
      <input matInput placeholder="Name" 
      name="name" 
      required
      ngModel
      #name="ngModel"
      [errorStateMatcher]="esMatcher">
      <mat-error *ngIf="name.hasError('required')">
        Name 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>   
      <button mat-raised-button>Submit</button>
  </div>  
</form> 
template-driven-form.component.ts
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);
  } 
} 
app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <app-reactive></app-reactive>
    <app-template-driven></app-template-driven>
  ` 
})
export class AppComponent {
} 
app.module.ts
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 { } 
styles.css
@import "~@angular/material/prebuilt-themes/indigo-pink.css"; 
Find the print screen of the output.
Angular Material Textarea

References

Angular Material Input
CdkTextareaAutosize Directive

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us