Skip to content

Commit 0455e95

Browse files
committed
feat(63): answer to challenge 63 custom sub form
1 parent 8fceb6e commit 0455e95

File tree

2 files changed

+160
-185
lines changed

2 files changed

+160
-185
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
2+
import {
3+
FieldState,
4+
FieldTree,
5+
FormField,
6+
required,
7+
SchemaPathTree,
8+
} from '@angular/forms/signals';
9+
10+
interface AddressGroup {
11+
street: string;
12+
zipcode: string;
13+
city: string;
14+
}
15+
16+
export const defaultAddress: AddressGroup = {
17+
street: '',
18+
zipcode: '',
19+
city: '',
20+
};
21+
22+
export const addressSchema = (item: SchemaPathTree<AddressGroup>) => {
23+
required(item.street);
24+
required(item.zipcode);
25+
required(item.city);
26+
};
27+
28+
@Component({
29+
selector: 'app-address-form',
30+
imports: [FormField],
31+
changeDetection: ChangeDetectionStrategy.OnPush,
32+
template: `
33+
<div class="grid gap-4 sm:grid-cols-2" data-testid="shipping-fields">
34+
<label
35+
class="flex flex-col gap-1 text-sm font-medium text-slate-700 sm:col-span-2">
36+
Street
37+
<input class="input" type="text" [formField]="form().street" />
38+
<span class="hint">
39+
@if (showError(form().street())) {
40+
This field is required
41+
}
42+
</span>
43+
</label>
44+
<label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
45+
ZIP code
46+
<input class="input" type="text" [formField]="form().zipcode" />
47+
<span class="hint">
48+
@if (showError(form().zipcode())) {
49+
This field is required
50+
}
51+
</span>
52+
</label>
53+
<label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
54+
City
55+
<input class="input" type="text" [formField]="form().city" />
56+
<span class="hint">
57+
@if (showError(form().city())) {
58+
This field is required
59+
}
60+
</span>
61+
</label>
62+
</div>
63+
`,
64+
styles: [
65+
`
66+
.input {
67+
@apply w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm shadow-sm outline-none transition focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200;
68+
}
69+
.hint {
70+
@apply text-xs text-rose-600;
71+
}
72+
`,
73+
],
74+
})
75+
export class AddressFormComponent {
76+
form = input.required<FieldTree<AddressGroup>>();
77+
78+
showError<T>(field: FieldState<T>): boolean {
79+
return field.invalid() && (field.touched() || field.dirty());
80+
}
81+
}

0 commit comments

Comments
 (0)