docs: add signal forms playground template

(cherry picked from commit e0eb619c03)
This commit is contained in:
Ben Hong 2025-11-19 09:35:23 -05:00 committed by Jessica Janiuk
parent c2c0140532
commit ccb0264edb
3 changed files with 152 additions and 0 deletions

View file

@ -0,0 +1,5 @@
{
"type": "editor-only",
"title": "Signals forms template",
"openFiles": ["src/main.ts", "src/main.css"]
}

View file

@ -0,0 +1,70 @@
form {
display: flex;
flex-direction: column;
gap: 1rem;
max-width: 400px;
padding: 1rem;
font-family:
Inter,
system-ui,
-apple-system,
sans-serif;
}
div {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
label {
display: flex;
flex-direction: column;
gap: 0.25rem;
font-weight: 500;
}
input {
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1rem;
font-family: inherit;
}
input:focus {
outline: none;
border-color: #4285f4;
}
button {
padding: 0.75rem 1.5rem;
background-color: #4285f4;
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-family: inherit;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: #357ae8;
}
button:active {
background-color: #2a65c8;
}
.error-list {
color: red;
font-size: 0.875rem;
margin: 0.25rem 0 0 0;
padding-left: 0;
list-style-position: inside;
}
.error-list p {
margin: 0;
}

View file

@ -0,0 +1,77 @@
import {bootstrapApplication} from '@angular/platform-browser';
import {Component, signal, ChangeDetectionStrategy} from '@angular/core';
// @ts-ignore There's a known issue with type support in the playground editor for signal forms
import {form, Field, required, email, debounce, submit} from '@angular/forms/signals';
interface LoginData {
email: string;
password: string;
}
@Component({
selector: 'app-root',
template: `
<form (submit)="onSubmit($event)">
<div>
<label>
Email:
<input type="email" [field]="loginForm.email" />
</label>
@if (loginForm.email().invalid()) {
<ul class="error-list">
@for (error of loginForm.email().errors(); track $index) {
<li>{{ error.message }}</li>
}
</ul>
}
</div>
<div>
<label>
Password:
<input type="password" [field]="loginForm.password" />
</label>
@if (loginForm.password().invalid()) {
<div class="error">
@for (error of loginForm.password().errors(); track $index) {
<p>{{ error.message }}</p>
}
</div>
}
</div>
<button type="submit">Log In</button>
</form>
`,
styleUrl: 'main.css',
imports: [Field],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoginApp {
loginModel = signal<LoginData>({
email: '',
password: '',
});
loginForm = form(this.loginModel, (schemaPath) => {
debounce(schemaPath.email, 500);
required(schemaPath.email, {message: 'Email is required'});
email(schemaPath.email, {message: 'Enter a valid email address'});
debounce(schemaPath.password, 500);
required(schemaPath.password, {message: 'Password is required'});
});
onSubmit(event: Event) {
event.preventDefault();
submit(this.loginForm, async () => {
// Perform login logic here
const credentials = this.loginModel();
console.log('Logging in with:', credentials);
// e.g., await this.authService.login(credentials);
});
}
}
bootstrapApplication(LoginApp);