Form
Textarea
Usage#
import { WdsTextareaComponent } from '@wds/angular/textarea';Examples#
Default#
field-sizing: content), from a minimum height of 64px.
import { Component } from '@angular/core';
import { WdsTextareaComponent } from '@wds/angular/textarea';
@Component({
selector: 'app-textarea-default-demo',
imports: [WdsTextareaComponent],
template: `
<div class="flex w-full max-w-sm flex-col">
<wds-textarea placeholder="Type your message here." aria-label="Message"></wds-textarea>
</div>
`,
})
export class TextareaDefaultDemo {}Field#
wds-field the label and the description become the accessible name and description of the textarea.
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldDescriptionComponent, WdsFieldLabelComponent } from '@wds/angular/field';
import { WdsTextareaComponent } from '@wds/angular/textarea';
@Component({
selector: 'app-textarea-field-demo',
imports: [WdsFieldComponent, WdsFieldLabelComponent, WdsFieldDescriptionComponent, WdsTextareaComponent],
template: `
<wds-field class="w-full max-w-sm">
<wds-field-label>Message</wds-field-label>
<wds-field-description>Enter your message below.</wds-field-description>
<wds-textarea placeholder="Type your message here."></wds-textarea>
</wds-field>
`,
})
export class TextareaFieldDemo {}Disabled#
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldLabelComponent } from '@wds/angular/field';
import { WdsTextareaComponent } from '@wds/angular/textarea';
@Component({
selector: 'app-textarea-disabled-demo',
imports: [WdsFieldComponent, WdsFieldLabelComponent, WdsTextareaComponent],
template: `
<wds-field class="w-full max-w-sm" disabled>
<wds-field-label>Message</wds-field-label>
<wds-textarea placeholder="Type your message here." disabled></wds-textarea>
</wds-field>
`,
})
export class TextareaDisabledDemo {}Invalid#
invalid on wds-field or on the textarea shows the error state.
import { Component } from '@angular/core';
import { WdsFieldComponent, WdsFieldDescriptionComponent, WdsFieldLabelComponent } from '@wds/angular/field';
import { WdsTextareaComponent } from '@wds/angular/textarea';
@Component({
selector: 'app-textarea-invalid-demo',
imports: [WdsFieldComponent, WdsFieldLabelComponent, WdsFieldDescriptionComponent, WdsTextareaComponent],
template: `
<wds-field class="w-full max-w-sm" invalid>
<wds-field-label>Message</wds-field-label>
<wds-textarea placeholder="Type your message here."></wds-textarea>
<wds-field-description>Please enter a valid message.</wds-field-description>
</wds-field>
`,
})
export class TextareaInvalidDemo {}Button#
import { Component } from '@angular/core';
import { WdsTextareaComponent } from '@wds/angular/textarea';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-textarea-button-demo',
imports: [WdsTextareaComponent, WdsButtonComponent],
template: `
<div class="flex w-full max-w-sm flex-col gap-2">
<wds-textarea placeholder="Type your message here." aria-label="Message"></wds-textarea>
<wds-button>Send message</wds-button>
</div>
`,
})
export class TextareaButtonDemo {}Forms#
Integration with Angular forms. Pick the API your app uses.
Signal forms#
WdsTextField connects the textarea to [formField]. Validators from the schema (required, minLength, maxLength) also set the matching attributes of the native textarea. wds-field-error shows the errors() of the field.
import { Component, computed, signal } from '@angular/core';
import { FormField, FormRoot, form, maxLength, minLength, required } from '@angular/forms/signals';
import { WdsButtonComponent } from '@wds/angular/button';
import { WDS_FIELD } from '@wds/angular/field';
import { WdsTextField } from '@wds/angular/forms';
import { WdsTextareaComponent } from '@wds/angular/textarea';
@Component({
selector: 'app-textarea-signal-forms-demo',
imports: [...WDS_FIELD, FormField, FormRoot, WdsButtonComponent, WdsTextareaComponent, WdsTextField],
template: `
<form class="flex w-sm max-w-full flex-col gap-4" [formRoot]="profileForm">
<wds-field [invalid]="showErrors()">
<wds-field-label>Bio</wds-field-label>
<wds-textarea placeholder="Tell us a little bit about yourself" [formField]="profileForm.bio" />
<wds-field-description>At least 10 and at most 160 characters.</wds-field-description>
<wds-field-error [errors]="showErrors() ? profileForm.bio().errors() : []" />
</wds-field>
<wds-button type="submit" class="self-start">Submit</wds-button>
<p class="m-0 font-mono text-sm text-muted-foreground">Submitted: {{ submitted() }}</p>
</form>
`,
})
export class TextareaSignalFormsDemo {
protected readonly profile = signal({ bio: '' });
protected readonly submitted = signal('—');
protected readonly profileForm = form(
this.profile,
(path) => {
required(path.bio, { message: 'Bio is required.' });
minLength(path.bio, 10, { message: 'Bio must be at least 10 characters.' });
maxLength(path.bio, 160, { message: 'Bio must not be longer than 160 characters.' });
},
{
submission: {
action: async (form) => {
this.submitted.set(JSON.stringify(form().value()));
},
},
}
);
/** Errors appear after the user leaves the field or submits the form. */
protected readonly showErrors = computed(() => this.profileForm.bio().touched() && this.profileForm.bio().invalid());
}Template-driven#
WdsTextValueAccessor makes ngModel work on wds-textarea. Angular validators (required, minlength, maxlength) work as on a native textarea; maxlength also stops typing at the limit.
import { Component, signal } from '@angular/core';
import { FormsModule, type NgForm } from '@angular/forms';
import { WdsButtonComponent } from '@wds/angular/button';
import { WDS_FIELD } from '@wds/angular/field';
import { WdsTextValueAccessor } from '@wds/angular/forms';
import { WdsTextareaComponent } from '@wds/angular/textarea';
@Component({
selector: 'app-textarea-template-driven-demo',
imports: [...WDS_FIELD, FormsModule, WdsButtonComponent, WdsTextareaComponent, WdsTextValueAccessor],
template: `
<form #profileForm="ngForm" class="flex w-sm max-w-full flex-col gap-4" (ngSubmit)="save(profileForm)">
<wds-field [invalid]="!!bio.invalid && !!bio.touched">
<wds-field-label>Bio</wds-field-label>
<wds-textarea
#bio="ngModel"
name="bio"
placeholder="Tell us a little bit about yourself"
[(ngModel)]="profile.bio"
required
minlength="10"
maxlength="160"
/>
<wds-field-description>At least 10 and at most 160 characters.</wds-field-description>
@if (bio.invalid && bio.touched) {
<wds-field-error>
@if (bio.hasError('required')) {
Bio is required.
} @else {
Bio must be at least 10 characters.
}
</wds-field-error>
}
</wds-field>
<wds-button type="submit" class="self-start">Submit</wds-button>
<p class="m-0 font-mono text-sm text-muted-foreground">Submitted: {{ submitted() }}</p>
</form>
`,
})
export class TextareaTemplateDrivenDemo {
protected readonly profile = { bio: '' };
protected readonly submitted = signal('—');
protected save(form: NgForm): void {
if (form.invalid) {
form.form.markAllAsTouched();
return;
}
this.submitted.set(JSON.stringify(form.value));
}
}Reactive forms#
WdsTextValueAccessor works with formControl and formControlName.
import { Component, signal } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { WdsButtonComponent } from '@wds/angular/button';
import { WDS_FIELD } from '@wds/angular/field';
import { WdsTextValueAccessor } from '@wds/angular/forms';
import { WdsTextareaComponent } from '@wds/angular/textarea';
@Component({
selector: 'app-textarea-reactive-forms-demo',
imports: [...WDS_FIELD, ReactiveFormsModule, WdsButtonComponent, WdsTextareaComponent, WdsTextValueAccessor],
template: `
<form class="flex w-sm max-w-full flex-col gap-4" [formGroup]="profileForm" (ngSubmit)="save()">
<wds-field [invalid]="bio.invalid && bio.touched">
<wds-field-label>Bio</wds-field-label>
<wds-textarea placeholder="Tell us a little bit about yourself" formControlName="bio" />
<wds-field-description>At least 10 and at most 160 characters.</wds-field-description>
@if (bio.invalid && bio.touched) {
<wds-field-error>
@if (bio.hasError('required')) {
Bio is required.
} @else if (bio.hasError('minlength')) {
Bio must be at least 10 characters.
} @else {
Bio must not be longer than 160 characters.
}
</wds-field-error>
}
</wds-field>
<wds-button type="submit" class="self-start">Submit</wds-button>
<p class="m-0 font-mono text-sm text-muted-foreground">Submitted: {{ submitted() }}</p>
</form>
`,
})
export class TextareaReactiveFormsDemo {
protected readonly profileForm = new FormGroup({
bio: new FormControl('', {
nonNullable: true,
validators: [Validators.required, Validators.minLength(10), Validators.maxLength(160)],
}),
});
protected readonly bio = this.profileForm.controls.bio;
protected readonly submitted = signal('—');
protected save(): void {
if (this.profileForm.invalid) {
this.profileForm.markAllAsTouched();
return;
}
this.submitted.set(JSON.stringify(this.profileForm.getRawValue()));
}
}API References#
wds-textarea#
field-sizing: content), min. 64px.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| rows | number | undefined | field-sizing). | |
| value | string | '' | |
| name | string | '' | |
| placeholder | string | '' | |
| disabled | boolean | false | |
| readonly | boolean | false | |
| required | boolean | false | |
| invalid | boolean | false | aria-invalid. |
| autocomplete | string | '' | autocomplete hint. |
| minlength.minLength | number | undefined | ||
| maxlength.maxLength | number | undefined |
Events
| Event | Description |
|---|---|
| wds-inputCustomEvent<WdsTextChangeDetail> | |
| wds-changeCustomEvent<WdsTextChangeDetail> |
Methods
| Method | Description |
|---|---|
| select() | |
| formResetCallback() | |
| formDisabledCallback(disabled: boolean) | |
| formStateRestoreCallback(state: string | File | FormData | null) | |
| checkValidity() | true when the value is valid; otherwise fires invalid on the element. |
| reportValidity() | checkValidity(), and shows the browser validation bubble when the value is invalid. |
| setCustomValidity(message: string) | input.setCustomValidity(). |
CSS parts (::part)
| Part | Description |
|---|---|
| control | <textarea>. |
Accessibility#
Screen reader#
wds-textarea renders a native <textarea>, so it is announced as a multi-line edit field. Without wds-field give it a name with aria-label on the element: it is forwarded to the native textarea.
wds-field, wds-field-label names the textarea and wds-field-description describes it. invalid sets aria-invalid="true"; disabled, readonly and required are set on the native textarea.
value is submitted under name, and required, minlength and maxlength work as native validation.
Keyboard support#
| Key | Function |
|---|---|