Skip to content

Commit 3b2df62

Browse files
committed
feat: 64 forms with craft-ng forms
1 parent aa00d4b commit 3b2df62

5 files changed

Lines changed: 178 additions & 59 deletions

File tree

apps/forms/64-form-array/eslint.config.mjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import nx from '@nx/eslint-plugin';
22
import baseConfig from '../../../eslint.config.mjs';
3+
const craftRules = require('@craft-ng/dev-tools/eslint-rules');
34

45
export default [
56
...baseConfig,
67
...nx.configs['flat/angular'],
78
...nx.configs['flat/angular-template'],
89
{
910
files: ['**/*.ts'],
11+
plugins: {
12+
'craft-ng': craftRules,
13+
},
1014
rules: {
1115
'@angular-eslint/directive-selector': [
1216
'error',
@@ -24,6 +28,13 @@ export default [
2428
style: 'kebab-case',
2529
},
2630
],
31+
'craft-ng/brand-angular-gen-deps-required': 'error',
32+
'craft-ng/no-angular-inject': 'error',
33+
'craft-ng/prefer-craft-http-client': 'error',
34+
'craft-ng/prefer-craft-service': 'error',
35+
'craft-ng/prefer-browser-boundaries': 'error',
36+
'craft-ng/app-start-registry-match': 'error',
37+
'craft-ng/brand-angular-deps-match': 'error',
2738
},
2839
},
2940
{

apps/forms/64-form-array/src/app/app.component.ts

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { JsonPipe } from '@angular/common';
22
import { ChangeDetectionStrategy, Component } from '@angular/core';
33
import { ReactiveFormsModule } from '@angular/forms';
4-
import { FormField } from '@angular/forms/signals';
54
import {
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';
1819
import { 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>

apps/forms/64-form-array/src/app/contact-form.component.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Component, input, output } from '@angular/core';
2-
import { FormField } from '@angular/forms/signals';
3-
import { ContactField } from './app.component';
2+
import { CraftFieldDirective } from '@craft-ng/core';
3+
import { ContactFormTree } from './app.component';
44
@Component({
55
selector: 'app-contact-form',
6-
imports: [FormField],
6+
imports: [CraftFieldDirective],
77
template: `
88
<div
99
class="rounded-lg border border-slate-200 bg-slate-50/40 p-4"
@@ -22,12 +22,12 @@ import { ContactField } from './app.component';
2222
</div>
2323
2424
<div class="mt-4 grid gap-4 sm:grid-cols-2">
25-
@let firstNameField = this.field()().selectFirstname();
25+
@let firstNameField = this.field().selectFirstname();
2626
<label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
2727
First name
28-
<input class="input" type="text" [formField]="firstNameField" />
28+
<input class="input" type="text" [craftField]="firstNameField" />
2929
@for (
30-
exception of firstNameField().visibleExceptions().list;
30+
exception of firstNameField.visibleExceptions().list;
3131
track exception.code
3232
) {
3333
<span class="hint">
@@ -40,16 +40,16 @@ import { ContactField } from './app.component';
4040
</span>
4141
}
4242
</label>
43-
@let lastNameField = this.field()().selectLastname();
43+
@let lastNameField = this.field().selectLastname();
4444
<label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
4545
Last name
4646
<input
4747
class="input"
4848
type="text"
49-
[formField]="lastNameField" />
49+
[craftField]="lastNameField" />
5050
<span class="hint">
5151
@for (
52-
exception of lastNameField().visibleExceptions().list;
52+
exception of lastNameField.visibleExceptions().list;
5353
track exception.code
5454
) {
5555
@switch (exception.code) {
@@ -61,16 +61,16 @@ import { ContactField } from './app.component';
6161
}
6262
</span>
6363
</label>
64-
@let relationField = this.field()().selectRelation();
64+
@let relationField = this.field().selectRelation();
6565
<label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
6666
Relation
6767
<input
6868
class="input"
6969
type="text"
70-
[formField]="relationField" />
70+
[craftField]="relationField" />
7171
<span class="hint">
7272
@for (
73-
exception of relationField().visibleExceptions().list;
73+
exception of relationField.visibleExceptions().list;
7474
track exception.code
7575
) {
7676
@switch (exception.code) {
@@ -82,16 +82,16 @@ import { ContactField } from './app.component';
8282
}
8383
</span>
8484
</label>
85-
@let emailField = this.field()().selectEmail();
85+
@let emailField = this.field().selectEmail();
8686
<label class="flex flex-col gap-1 text-sm font-medium text-slate-700">
8787
Email
8888
<input
8989
class="input"
9090
type="email"
91-
[formField]="emailField" />
91+
[craftField]="emailField" />
9292
<span class="hint">
9393
@for (
94-
exception of emailField().visibleExceptions().list;
94+
exception of emailField.visibleExceptions().list;
9595
track exception.code
9696
) {
9797
@let code = exception.code;
@@ -112,7 +112,7 @@ import { ContactField } from './app.component';
112112
`,
113113
})
114114
export class ContactFormComponent {
115-
field = input.required<ContactField>();
115+
field = input.required<ContactFormTree>();
116116
index = input(0);
117117
remove = output<void>();
118118
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"@angular/platform-server": "21.2.2",
3333
"@angular/router": "21.2.2",
3434
"@angular/ssr": "21.2.1",
35-
"@craft-ng/core": "^0.1.3",
35+
"@craft-ng/core": "0.5.0-beta.4",
36+
"@craft-ng/dev-tools": "0.5.0-beta.0",
3637
"@ngneat/falso": "7.2.0",
3738
"@ngrx/component-store": "21.0.0",
3839
"@ngrx/operators": "21.0.0",

0 commit comments

Comments
 (0)