-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Expand file tree
/
Copy pathapp.component.ts
More file actions
165 lines (152 loc) · 5.09 KB
/
app.component.ts
File metadata and controls
165 lines (152 loc) · 5.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import { JsonPipe } from '@angular/common';
import { Component, signal, WritableSignal } from '@angular/core';
import {
form,
FormField,
FormRoot,
max,
min,
required,
} from '@angular/forms/signals';
type FormData = {
name: string;
lastname: string;
age: number;
note: string;
};
const initialFormData: FormData = {
name: '',
lastname: '',
age: NaN,
note: '',
};
@Component({
selector: 'app-root',
imports: [FormField, FormRoot, JsonPipe],
template: `
<div class="min-h-screen bg-gray-100 px-4 py-12 sm:px-6 lg:px-8">
<div class="mx-auto max-w-md rounded-lg bg-white p-8 shadow-md">
<h1 class="mb-6 text-3xl font-bold text-gray-900">Simple Form</h1>
<form [formRoot]="form" class="space-y-6">
<div>
<label
for="name"
class="mb-2 block text-sm font-medium text-gray-700">
Name
<span class="text-red-500">*</span>
</label>
<input
id="name"
type="text"
[formField]="form.name"
placeholder="Enter your name"
class="w-full rounded-md border border-gray-300 px-4 py-2 transition outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500"
[class.border-red-500]="
form.name().invalid() && form.name().touched()
" />
@if (form.name().invalid() && form.name().touched()) {
@for (error of form.name().errors(); track error) {
<p class="mt-1 text-sm text-red-600">{{ error.message }}</p>
}
}
</div>
<div>
<label
for="lastname"
class="mb-2 block text-sm font-medium text-gray-700">
Last Name
</label>
<input
id="lastname"
type="text"
[formField]="form.lastname"
placeholder="Enter your last name"
class="w-full rounded-md border border-gray-300 px-4 py-2 transition outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500" />
</div>
<div>
<label
for="age"
class="mb-2 block text-sm font-medium text-gray-700">
Age
</label>
<input
id="age"
type="number"
[formField]="form.age"
placeholder="Enter your age (1-99)"
class="w-full rounded-md border border-gray-300 px-4 py-2 transition outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500"
[class.border-red-500]="
form.age().invalid() && form.age().touched()
" />
@if (form.age().invalid() && form.age().touched()) {
@for (error of form.age().errors(); track error) {
<p class="mt-1 text-sm text-red-600">{{ error.message }}</p>
}
}
</div>
<div>
<label
for="note"
class="mb-2 block text-sm font-medium text-gray-700">
Note
</label>
<input
id="note"
type="text"
[formField]="form.note"
placeholder="Enter a note"
class="w-full rounded-md border border-gray-300 px-4 py-2 transition outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500" />
</div>
<div class="flex gap-4">
<button
type="submit"
[disabled]="form().invalid()"
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">
Submit
</button>
<button
type="button"
(click)="onReset()"
class="flex-1 rounded-md bg-gray-600 px-4 py-2 font-medium text-white transition hover:bg-gray-700">
Reset
</button>
</div>
</form>
@if (submittedData()) {
<div class="mt-8 rounded-lg border border-green-200 bg-green-50 p-4">
<h2 class="mb-2 text-lg font-semibold text-green-900">
Submitted Data:
</h2>
<pre
class="overflow-x-auto rounded border border-green-200 bg-white p-4 text-sm"
>{{ submittedData() | json }}</pre
>
</div>
}
</div>
</div>
`,
})
export class AppComponent {
model = signal<FormData>(initialFormData);
form = form(
this.model,
(schemaPath) => {
required(schemaPath.name, { message: 'Name is required' });
min(schemaPath.age, 1, { message: 'Age must be at least 1' });
max(schemaPath.age, 99, { message: 'Age must be at most 99' });
},
{
submission: {
action: async (f) => {
this.submittedData.set(f().value());
},
},
},
);
submittedData: WritableSignal<FormData | null> = signal(null);
onReset(): void {
this.form().reset(initialFormData);
this.submittedData.set(null);
}
}