11import { JsonPipe } from '@angular/common' ;
22import { ChangeDetectionStrategy , Component } from '@angular/core' ;
33import { ReactiveFormsModule } from '@angular/forms' ;
4- import { FormField } from '@angular/forms/signals' ;
54import {
65 cEmail ,
76 cMinLength ,
7+ CraftFieldDirective ,
88 cRequired ,
99 insertForm ,
1010 insertFormAttributes ,
@@ -14,6 +14,7 @@ import {
1414 mutation ,
1515 state ,
1616 ValidatedFormValue ,
17+ type TargetFormField ,
1718} from '@craft-ng/core' ;
1819import { ContactFormComponent } from './contact-form.component' ;
1920
@@ -36,26 +37,25 @@ type Registration = {
3637 emails : Email [ ] ;
3738} ;
3839
39- // 😅 I may add some helpers
40- export type ContactField = ReturnType <
41- ReturnType <
42- ReturnType <
43- ReturnType <
44- InstanceType < typeof AppComponent > [ 'registration' ] [ 'form' ]
45- > [ 'selectContacts' ]
46- >
47- > [ 'items' ]
48- > [ number ] ;
40+ export type ContactFormTree = TargetFormField <
41+ InstanceType < typeof AppComponent > [ 'registration' ] [ 'form' ] ,
42+ 'selectContacts.selectContact'
43+ > ;
4944
5045@Component ( {
5146 selector : 'app-root' ,
52- imports : [ ReactiveFormsModule , JsonPipe , ContactFormComponent , FormField ] ,
47+ imports : [
48+ ReactiveFormsModule ,
49+ JsonPipe ,
50+ ContactFormComponent ,
51+ CraftFieldDirective ,
52+ ] ,
5353 changeDetection : ChangeDetectionStrategy . OnPush ,
5454 template : `
5555 <main class="min-h-screen bg-slate-50 text-slate-900">
5656 <div class="mx-auto max-w-5xl px-6 py-12">
5757 <h1 class="mb-6 text-3xl font-semibold">Registration form</h1>
58- @let form = registration.form() ;
58+ @let form = registration.form;
5959 <form
6060 class="space-y-8 rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
6161 <section class="space-y-4">
@@ -65,17 +65,17 @@ export type ContactField = ReturnType<
6565 <label
6666 class="flex flex-col gap-1 text-sm font-medium text-slate-700">
6767 Name
68- <input class="input" type="text" [formField ]="nameField" />
69- <span class="hint">
70- @for( exception of nameField().visibleExceptions().list; track exception.code) {
68+ <input class="input" type="text" [craftField ]="nameField" />
69+ @if(nameField.visibleFirstLeftFailedValidation(); as exception) {
70+ <span class="hint">
7171 @switch(exception.code) {
7272 @case('required') {
7373 Name is required
7474 }
7575 @default never;
7676 }
77- }
78- </span>
77+ </span>
78+ }
7979 </label>
8080 @let pseudoField = form.selectPseudo();
8181 <label
@@ -84,9 +84,9 @@ export type ContactField = ReturnType<
8484 <input
8585 class="input"
8686 type="text"
87- [formField ]="pseudoField" />
87+ [craftField ]="pseudoField" />
8888 <span class="hint">
89- @for( exception of pseudoField() .visibleExceptions().list; track exception.code) {
89+ @for( exception of pseudoField.visibleExceptions().list; track exception.code) {
9090 @switch(exception.code) {
9191 @case('required') {
9292 This field is required
@@ -105,21 +105,21 @@ export type ContactField = ReturnType<
105105 <h2 class="text-xl font-semibold">Contacts</h2>
106106 <button
107107 type="button"
108- (click)="contacts() .add()"
108+ (click)="contacts.add()"
109109 class="btn-secondary">
110110 Add contact
111111 </button>
112112 </div>
113113
114114 <div class="space-y-4">
115- @for (contact of contacts() .items(); track $index) {
115+ @for (contact of contacts.items(); track $index) {
116116 <app-contact-form
117117 [field]="contact"
118118 [index]="$index"
119- (remove)="contacts() .remove($index)"></app-contact-form>
119+ (remove)="contacts.remove($index)"></app-contact-form>
120120 }
121121 </div>
122- @for( exception of contacts() .visibleExceptions().list; track exception.code) {
122+ @for( exception of contacts.visibleExceptions().list; track exception.code) {
123123 @let code = exception.code;
124124 @switch(code) {
125125 @case('minLength') {
@@ -134,13 +134,13 @@ export type ContactField = ReturnType<
134134 @let emails = form.selectEmails();
135135 <div class="flex items-center justify-between gap-4">
136136 <h2 class="text-xl font-semibold">Emails</h2>
137- <button type="button" (click)="emails() .add()" class="btn-secondary">
137+ <button type="button" (click)="emails.add()" class="btn-secondary">
138138 Add email
139139 </button>
140140 </div>
141141
142142 <div class="space-y-4">
143- @for (email of emails() .items(); track $index) {
143+ @for (email of emails.items(); track $index) {
144144 <div
145145 class="rounded-lg border border-slate-200 bg-slate-50/40 p-4"
146146 data-testid="email-item">
@@ -152,23 +152,23 @@ export type ContactField = ReturnType<
152152 type="button"
153153 class="btn-danger"
154154 aria-label="Remove email {{ $index + 1 }}"
155- (click)="emails() .remove($index)">
155+ (click)="emails.remove($index)">
156156 Remove
157157 </button>
158158 </div>
159159
160160 <div
161161 class="mt-4 grid gap-4 sm:grid-cols-2">
162- @let relativeField = email() .selectType();
162+ @let relativeField = email.selectType();
163163 <label
164164 class="flex flex-col gap-1 text-sm font-medium text-slate-700">
165165 Type
166- <select class="input" [formField ]="relativeField">
166+ <select class="input" [craftField ]="relativeField">
167167 <option value="personal">Personal</option>
168168 <option value="professional">Professional</option>
169169 <option value="other">Other</option>
170170 </select>
171- @for(exceptions of relativeField() .visibleExceptions().list; track exceptions.code) {
171+ @for(exceptions of relativeField.visibleExceptions().list; track exceptions.code) {
172172 @switch(exceptions.code) {
173173 @case('required') {
174174 <span class="hint">This field is required</span>
@@ -177,15 +177,15 @@ export type ContactField = ReturnType<
177177 }
178178 }
179179 </label>
180- @let emailField = email() .selectEmail();
180+ @let emailField = email.selectEmail();
181181 <label
182182 class="flex flex-col gap-1 text-sm font-medium text-slate-700">
183183 Email
184184 <input
185185 class="input"
186186 type="email"
187- [formField ]="emailField" />
188- @for(exceptions of emailField() .visibleExceptions().list; track exceptions.code) {
187+ [craftField ]="emailField" />
188+ @for(exceptions of emailField.visibleExceptions().list; track exceptions.code) {
189189 @let code= exceptions.code;
190190 @switch(code) {
191191 @case('required') {
@@ -207,15 +207,15 @@ export type ContactField = ReturnType<
207207 <div
208208 class="flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-4">
209209 <div class="text-sm text-slate-600">
210- <span [class.text-rose-600]="registration.form() .invalid()">
211- {{ registration.form() .invalid() ? 'Form incomplete' : 'Ready to submit' }}
210+ <span [class.text-rose-600]="registration.form.invalid()">
211+ {{ registration.form.invalid() ? 'Form incomplete' : 'Ready to submit' }}
212212 </span>
213213 </div>
214- <button type='button' (click)="registration.form() .submit()" class="btn-primary">Submit</button>
214+ <button type='button' (click)="registration.form.submit()" class="btn-primary">Submit</button>
215215 </div>
216216 </form>
217217
218- @if (save.safeValue ()) {
218+ @if (save.hasValue ()) {
219219 <section
220220 class="mt-6 rounded-lg border border-slate-200 bg-white p-4 shadow-sm">
221221 <h3 class="mb-2 text-lg font-semibold">Submitted data</h3>
0 commit comments