Skip to content

Commit 88a0f54

Browse files
committed
feat(): migrate to signal form
1 parent b0d8f03 commit 88a0f54

File tree

1 file changed

+47
-47
lines changed

1 file changed

+47
-47
lines changed
Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
import { JsonPipe } from '@angular/common';
22
import { Component, signal, WritableSignal } from '@angular/core';
3-
import {
4-
FormControl,
5-
FormGroup,
6-
ReactiveFormsModule,
7-
Validators,
8-
} from '@angular/forms';
3+
import { Field, form, max, min, required } from '@angular/forms/signals';
4+
5+
interface IFormData {
6+
name: string;
7+
lastname: string;
8+
age: number;
9+
note: string;
10+
}
11+
12+
const defaultValue = {
13+
name: '',
14+
lastname: '',
15+
age: null!,
16+
note: '',
17+
};
918

1019
@Component({
1120
selector: 'app-root',
12-
imports: [ReactiveFormsModule, JsonPipe],
21+
imports: [JsonPipe, Field],
1322
template: `
1423
<div class="min-h-screen bg-gray-100 px-4 py-12 sm:px-6 lg:px-8">
1524
<div class="mx-auto max-w-md rounded-lg bg-white p-8 shadow-md">
1625
<h1 class="mb-6 text-3xl font-bold text-gray-900">Simple Form</h1>
1726
18-
<form [formGroup]="form" (ngSubmit)="onSubmit()" class="space-y-6">
27+
<form (submit)="onSubmit($event)" class="space-y-6">
1928
<div>
2029
<label
2130
for="name"
@@ -26,14 +35,16 @@ import {
2635
<input
2736
id="name"
2837
type="text"
29-
formControlName="name"
38+
[field]="form.name"
3039
placeholder="Enter your name"
3140
class="w-full rounded-md border border-gray-300 px-4 py-2 outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500"
3241
[class.border-red-500]="
33-
form.controls.name.invalid && !form.controls.name.untouched
42+
form.name().invalid() && form.name().touched()
3443
" />
35-
@if (form.controls.name.invalid && !form.controls.name.untouched) {
36-
<p class="mt-1 text-sm text-red-600">Name is required</p>
44+
@if (form.name().invalid() && form.name().touched()) {
45+
@for (error of form.name().errors(); track $index) {
46+
<p class="mt-1 text-sm text-red-600">{{ error.message }}</p>
47+
}
3748
}
3849
</div>
3950
@@ -46,7 +57,7 @@ import {
4657
<input
4758
id="lastname"
4859
type="text"
49-
formControlName="lastname"
60+
[field]="form.lastname"
5061
placeholder="Enter your last name"
5162
class="w-full rounded-md border border-gray-300 px-4 py-2 outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500" />
5263
</div>
@@ -60,21 +71,16 @@ import {
6071
<input
6172
id="age"
6273
type="number"
63-
formControlName="age"
74+
[field]="form.age"
6475
placeholder="Enter your age (1-99)"
65-
min="1"
66-
max="99"
6776
class="w-full rounded-md border border-gray-300 px-4 py-2 outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500"
6877
[class.border-red-500]="
69-
form.controls.age.invalid && !form.controls.age.untouched
78+
form.age().invalid() && form.age().touched()
7079
" />
71-
@if (form.controls.age.invalid && !form.controls.age.untouched) {
80+
@if (form.age().invalid() && form.age().touched()) {
7281
<p class="mt-1 text-sm text-red-600">
73-
@if (form.controls.age.hasError('min')) {
74-
Age must be at least 1
75-
}
76-
@if (form.controls.age.hasError('max')) {
77-
Age must be at most 99
82+
@for (error of form.age().errors(); track $index) {
83+
<p class="mt-1 text-sm text-red-600">{{ error.message }}</p>
7884
}
7985
</p>
8086
}
@@ -89,15 +95,15 @@ import {
8995
<input
9096
id="note"
9197
type="text"
92-
formControlName="note"
98+
[field]="form.note"
9399
placeholder="Enter a note"
94100
class="w-full rounded-md border border-gray-300 px-4 py-2 outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500" />
95101
</div>
96102
97103
<div class="flex gap-4">
98104
<button
99105
type="submit"
100-
[disabled]="form.invalid"
106+
[disabled]="form().invalid()"
101107
class="flex-1 rounded-md bg-blue-600 px-4 py-2 font-medium text-white transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:bg-gray-400">
102108
Submit
103109
</button>
@@ -126,35 +132,29 @@ import {
126132
`,
127133
})
128134
export class AppComponent {
129-
form = new FormGroup({
130-
name: new FormControl('', {
131-
validators: Validators.required,
132-
nonNullable: true,
133-
}),
134-
lastname: new FormControl('', { nonNullable: true }),
135-
age: new FormControl<number | null>(null, [
136-
Validators.min(1),
137-
Validators.max(99),
138-
]),
139-
note: new FormControl('', { nonNullable: true }),
135+
model = signal<IFormData>(defaultValue);
136+
137+
form = form(this.model, (schemaPath) => {
138+
(required(schemaPath.name, { message: 'Name is required' }),
139+
min(schemaPath.age, 1, { message: 'Age must be at least 1' }),
140+
max(schemaPath.age, 99, { message: 'Age must be at most 99' }));
140141
});
141142

142-
submittedData: WritableSignal<{
143-
name: string;
144-
lastname: string;
145-
age: number | null;
146-
note: string;
147-
} | null> = signal(null);
143+
submittedData: WritableSignal<IFormData | null> = signal(null);
148144

149-
onSubmit(): void {
150-
if (this.form.valid) {
151-
this.submittedData.set(this.form.getRawValue());
152-
console.log('Form submitted:', this.submittedData);
145+
onSubmit(e: Event): void {
146+
e.preventDefault();
147+
if (this.form().valid()) {
148+
this.submittedData.set({
149+
...this.form().value(),
150+
age: +this.form().value().age,
151+
});
152+
console.log('Form submitted:', this.submittedData());
153153
}
154154
}
155155

156156
onReset(): void {
157-
this.form.reset();
157+
this.form().reset(defaultValue);
158158
this.submittedData.set(null);
159159
}
160160
}

0 commit comments

Comments
 (0)