Frameworks
Angular
Install and use WDS in an Angular app.
Installation#
Create project#
Skip this step if you already have an Angular app.
ng new my-appInstall packages#
@wds/angular needs @wds/core, @wds/tokens and lit.
pnpm add @wds/angular @wds/core @wds/tokens litnpm install @wds/angular @wds/core @wds/tokens lityarn add @wds/angular @wds/core @wds/tokens litbun add @wds/angular @wds/core @wds/tokens litUntil the packages are in a registry, install them from files: see Local installation.
Add the tokens#
Import the CSS variables once, in the global styles.
/* src/styles.css */
@import '@wds/tokens/tokens.css';
/* Optional with SSR/SSG: hides elements until they are registered. */
wds-radio-group:not(:defined) {
visibility: hidden;
}Use components#
Import a wrapper from its entry point, such as @wds/angular/button. Wrappers have typed inputs (booleanAttribute for flags) and outputs: the wds-change event is the (wdsChange) output, and $event is its detail.
import { Component, signal } from '@angular/core';
// Every component has its own entry point, so the bundle gets only what you use.
import { WdsButtonComponent } from '@wds/angular/button';
import { WdsCardComponent } from '@wds/angular/card';
import { WDS_RADIO } from '@wds/angular/radio';
@Component({
selector: 'app-root',
imports: [WdsButtonComponent, WdsCardComponent, ...WDS_RADIO],
template: `
<wds-card heading="Plan">
<wds-radio-group [value]="plan()" (wdsChange)="plan.set($event.value)">
<wds-radio value="free">Free</wds-radio>
<wds-radio value="pro">Pro</wds-radio>
</wds-radio-group>
<wds-button slot="footer" [loading]="saving()" (click)="save()">Save</wds-button>
</wds-card>
`,
})
export class App {
protected readonly plan = signal('free');
protected readonly saving = signal(false);
protected save() {
this.saving.set(true);
}
}Forms#
@wds/angular/forms connects form controls to Angular forms. Each control page has a Forms section with Signal Forms, template-driven and Reactive Forms examples, for example Input.
Signal Forms#
The form sets disabled, required and validation on the control; wds-field-error shows the errors.
import { Component, signal } from '@angular/core';
import { FormField, form, required } from '@angular/forms/signals';
import { WDS_FIELD } from '@wds/angular/field';
import { WdsTextField } from '@wds/angular/forms';
import { WdsInputComponent } from '@wds/angular/input';
@Component({
selector: 'app-signup-form',
imports: [...WDS_FIELD, FormField, WdsInputComponent, WdsTextField],
template: `
<wds-field>
<wds-field-label>Email</wds-field-label>
<wds-input type="email" [formField]="signup.email" />
<wds-field-error [errors]="signup.email().errors()" />
</wds-field>
`,
})
export class SignupForm {
readonly model = signal({ email: '' });
readonly signup = form(this.model, (p) => required(p.email, { message: 'Enter your email.' }));
}Reactive Forms#
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { WdsCheckboxComponent } from '@wds/angular/checkbox';
import { WdsCheckedValueAccessor, WdsTextValueAccessor } from '@wds/angular/forms';
import { WdsInputComponent } from '@wds/angular/input';
@Component({
selector: 'app-signup-form',
imports: [ReactiveFormsModule, WdsCheckboxComponent, WdsInputComponent, WdsCheckedValueAccessor, WdsTextValueAccessor],
template: `
<wds-input type="email" [formControl]="email" />
<wds-checkbox [formControl]="terms">I accept the terms</wds-checkbox>
`,
})
export class SignupForm {
readonly email = new FormControl('');
readonly terms = new FormControl(false);
}Services#
WdsDialogService, WdsDrawerService and WdsSheetService open a dialog, a drawer or a sheet from a config or with a component, which gets its data through WDS_OVERLAY_DATA. All are safe with SSR and SSG.
import { Component, inject } from '@angular/core';
import { WdsDialogService } from '@wds/angular/dialog';
@Component({ /* ... */ })
export class ProjectList {
private readonly dialogs = inject(WdsDialogService);
remove() {
this.dialogs
.open<unknown, boolean>({
title: 'Delete project?',
description: 'This action cannot be undone.',
okText: 'Delete',
cancelText: 'Cancel',
okDestructive: true,
})
.closed.subscribe((ok) => ok && this.delete());
}
}