Skip to content

Commit 485092d

Browse files
committed
feat(challenge 64): convert registration form from reactive form to signal
1 parent 543770b commit 485092d

6 files changed

Lines changed: 392 additions & 371 deletions

File tree

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<main class="min-h-screen bg-slate-50 text-slate-900">
2+
<div class="mx-auto max-w-5xl px-6 py-12">
3+
<h1 class="mb-6 text-3xl font-semibold">Registration form</h1>
4+
<form
5+
[formRoot]="form"
6+
class="space-y-8 rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
7+
<section class="space-y-4">
8+
<h2 class="text-xl font-semibold">Profile</h2>
9+
<div class="grid gap-4 sm:grid-cols-2">
10+
<label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
11+
Name
12+
<input
13+
class="input"
14+
type="text"
15+
[formField]="form.name"
16+
aria-required="true" />
17+
<app-validation-message [fieldState]="form.name()" />
18+
</label>
19+
<label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
20+
Pseudo
21+
<input
22+
class="input"
23+
type="text"
24+
[formField]="form.pseudo"
25+
aria-required="true" />
26+
<app-validation-message [fieldState]="form.pseudo()" />
27+
</label>
28+
</div>
29+
</section>
30+
31+
<section class="space-y-4">
32+
<div class="flex items-center justify-between gap-4">
33+
<h2 class="text-xl font-semibold">Contacts</h2>
34+
<button type="button" (click)="addContact()" class="btn-secondary">
35+
Add contact
36+
</button>
37+
</div>
38+
39+
<div class="space-y-4">
40+
@for (contact of form.contacts; track $index) {
41+
<app-contact-form
42+
[formField]="contact"
43+
[index]="$index"
44+
(remove)="removeContact($index)" />
45+
}
46+
</div>
47+
48+
<!-- @if (contacts.invalid && (contacts.touched || contacts.dirty)) {
49+
<p class="hint">At least one contact is required.</p>
50+
} -->
51+
<app-validation-message [fieldState]="form.contacts()" />
52+
</section>
53+
54+
<section class="space-y-4">
55+
<div class="flex items-center justify-between gap-4">
56+
<h2 class="text-xl font-semibold">Emails</h2>
57+
<button type="button" (click)="addEmail()" class="btn-secondary">
58+
Add email
59+
</button>
60+
</div>
61+
62+
<div formArrayName="emails" class="space-y-4">
63+
@for (field of form.emails; track field; let index = $index) {
64+
<div
65+
class="rounded-lg border border-slate-200 bg-slate-50/40 p-4"
66+
data-testid="email-item">
67+
<div class="flex items-center justify-between gap-4">
68+
<h3 class="text-sm font-semibold text-slate-700">
69+
Email {{ index + 1 }}
70+
</h3>
71+
<button
72+
type="button"
73+
class="btn-danger"
74+
aria-label="Remove email {{ index + 1 }}"
75+
(click)="removeEmail($index)">
76+
Remove
77+
</button>
78+
</div>
79+
80+
<div class="mt-4 grid gap-4 sm:grid-cols-2">
81+
<label
82+
class="flex flex-col gap-1 text-sm font-medium text-slate-700">
83+
Type
84+
<select class="input" [formField]="field.type">
85+
<option value="personal">Personal</option>
86+
<option value="professional">Professional</option>
87+
<option value="other">Other</option>
88+
</select>
89+
<app-validation-message [fieldState]="field.type()" />
90+
</label>
91+
<label
92+
class="flex flex-col gap-1 text-sm font-medium text-slate-700">
93+
Email
94+
<input
95+
class="input"
96+
type="email"
97+
[formField]="field.email"
98+
aria-required="true" />
99+
<app-validation-message [fieldState]="field.email()" />
100+
</label>
101+
</div>
102+
</div>
103+
}
104+
</div>
105+
</section>
106+
107+
<div
108+
class="flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-4">
109+
<div class="text-sm text-slate-600">
110+
<span [class.text-rose-600]="form().invalid()">
111+
{{ form().invalid() ? 'Form incomplete' : 'Ready to submit' }}
112+
</span>
113+
</div>
114+
<button type="submit" class="btn-primary">Submit</button>
115+
</div>
116+
</form>
117+
118+
@if (submittedData()) {
119+
<section
120+
class="mt-6 rounded-lg border border-slate-200 bg-white p-4 shadow-sm">
121+
<h3 class="mb-2 text-lg font-semibold">Submitted data</h3>
122+
<pre
123+
class="overflow-x-auto rounded bg-slate-900 p-4 text-sm text-slate-100"
124+
>{{ submittedData() | json }}</pre
125+
>
126+
</section>
127+
}
128+
</div>
129+
</main>

0 commit comments

Comments
 (0)