11import { JsonPipe } from '@angular/common' ;
22import { Component , signal , WritableSignal } from '@angular/core' ;
3+
34import {
4- FormControl ,
5- FormGroup ,
6- ReactiveFormsModule ,
7- Validators ,
8- } from '@angular/forms' ;
5+ form ,
6+ FormField ,
7+ FormRoot ,
8+ max ,
9+ min ,
10+ required ,
11+ } from '@angular/forms/signals' ;
12+
13+ type FormData = {
14+ name : string ;
15+ lastname : string ;
16+ age : number ;
17+ note : string ;
18+ } ;
19+
20+ const initialFormData : FormData = {
21+ name : '' ,
22+ lastname : '' ,
23+ age : NaN ,
24+ note : '' ,
25+ } ;
926
1027@Component ( {
1128 selector : 'app-root' ,
12- imports : [ ReactiveFormsModule , JsonPipe ] ,
29+ imports : [ FormField , FormRoot , JsonPipe ] ,
1330 template : `
1431 <div class="min-h-screen bg-gray-100 px-4 py-12 sm:px-6 lg:px-8">
1532 <div class="mx-auto max-w-md rounded-lg bg-white p-8 shadow-md">
1633 <h1 class="mb-6 text-3xl font-bold text-gray-900">Simple Form</h1>
1734
18- <form [formGroup ]="form" (ngSubmit)="onSubmit() " class="space-y-6">
35+ <form [formRoot ]="form" class="space-y-6">
1936 <div>
2037 <label
2138 for="name"
@@ -26,14 +43,16 @@ import {
2643 <input
2744 id="name"
2845 type="text"
29- formControlName=" name"
46+ [formField]="form. name"
3047 placeholder="Enter your name"
31- 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"
48+ 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"
3249 [class.border-red-500]="
33- form.controls. name.invalid && ! form.controls. name.untouched
50+ form.name() .invalid() && form.name().touched()
3451 " />
35- @if (form.controls.name.invalid && !form.controls.name.untouched) {
36- <p class="mt-1 text-sm text-red-600">Name is required</p>
52+ @if (form.name().invalid() && form.name().touched()) {
53+ @for (error of form.name().errors(); track error) {
54+ <p class="mt-1 text-sm text-red-600">{{ error.message }}</p>
55+ }
3756 }
3857 </div>
3958
@@ -46,9 +65,9 @@ import {
4665 <input
4766 id="lastname"
4867 type="text"
49- formControlName=" lastname"
68+ [formField]="form. lastname"
5069 placeholder="Enter your last name"
51- 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" />
70+ 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" />
5271 </div>
5372
5473 <div>
@@ -60,23 +79,16 @@ import {
6079 <input
6180 id="age"
6281 type="number"
63- formControlName=" age"
82+ [formField]="form. age"
6483 placeholder="Enter your age (1-99)"
65- min="1"
66- max="99"
67- 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"
84+ 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"
6885 [class.border-red-500]="
69- form.controls. age.invalid && ! form.controls. age.untouched
86+ form.age() .invalid() && form.age().touched()
7087 " />
71- @if (form.controls.age.invalid && !form.controls.age.untouched) {
72- <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
78- }
79- </p>
88+ @if (form.age().invalid() && form.age().touched()) {
89+ @for (error of form.age().errors(); track error) {
90+ <p class="mt-1 text-sm text-red-600">{{ error.message }}</p>
91+ }
8092 }
8193 </div>
8294
@@ -89,15 +101,15 @@ import {
89101 <input
90102 id="note"
91103 type="text"
92- formControlName=" note"
104+ [formField]="form. note"
93105 placeholder="Enter a note"
94- 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" />
106+ 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" />
95107 </div>
96108
97109 <div class="flex gap-4">
98110 <button
99111 type="submit"
100- [disabled]="form.invalid"
112+ [disabled]="form() .invalid() "
101113 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">
102114 Submit
103115 </button>
@@ -126,35 +138,28 @@ import {
126138 ` ,
127139} )
128140export 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 } ) ,
140- } ) ;
141+ model = signal < FormData > ( initialFormData ) ;
141142
142- submittedData : WritableSignal < {
143- name : string ;
144- lastname : string ;
145- age : number | null ;
146- note : string ;
147- } | null > = signal ( null ) ;
143+ form = form (
144+ this . model ,
145+ ( schemaPath ) => {
146+ required ( schemaPath . name , { message : 'Name is required' } ) ;
147+ min ( schemaPath . age , 1 , { message : 'Age must be at least 1' } ) ;
148+ max ( schemaPath . age , 99 , { message : 'Age must be at most 99' } ) ;
149+ } ,
150+ {
151+ submission : {
152+ action : async ( f ) => {
153+ this . submittedData . set ( f ( ) . value ( ) ) ;
154+ } ,
155+ } ,
156+ } ,
157+ ) ;
148158
149- onSubmit ( ) : void {
150- if ( this . form . valid ) {
151- this . submittedData . set ( this . form . getRawValue ( ) ) ;
152- console . log ( 'Form submitted:' , this . submittedData ) ;
153- }
154- }
159+ submittedData : WritableSignal < FormData | null > = signal ( null ) ;
155160
156161 onReset ( ) : void {
157- this . form . reset ( ) ;
162+ this . form ( ) . reset ( initialFormData ) ;
158163 this . submittedData . set ( null ) ;
159164 }
160165}
0 commit comments