Skip to content

Commit 536c709

Browse files
committed
fixup! docs(forms): add initial signal forms examples
1 parent 3e61454 commit 536c709

15 files changed

+106
-99
lines changed

packages/forms/signals/examples/04-conditional-validation.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ Use this pattern when a validation rule for one field depends on the state of an
2727
## Key Concepts
2828

2929
- **`applyWhen()`:** A function used within a schema to apply a validator conditionally.
30-
- **`valid` signal:** A signal on the `FieldState` that is `true` only when the field and all its descendants are valid.
31-
- **`(ngSubmit)`:** An event binding on a `<form>` element that triggers a method when the form is submitted.
30+
- **`submit()`:** An async helper function that manages the form's submission state and should be called from the submit event handler.
3231

3332
## Example Files
3433

@@ -40,7 +39,7 @@ This file defines the component's logic, including a schema that uses `applyWhen
4039

4140
```typescript
4241
import { Component, signal, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
43-
import { form, schema, applyWhen, required } from '@angular/forms/signals';
42+
import { form, schema, applyWhen, required, submit } from '@angular/forms/signals';
4443
import { JsonPipe } from '@angular/common';
4544

4645
export interface ContactForm {
@@ -73,10 +72,10 @@ export class ContactFormComponent {
7372

7473
contactForm = form(this.contactModel, contactSchema);
7574

76-
handleSubmit() {
77-
if (this.contactForm().valid()) {
75+
async handleSubmit() {
76+
await submit(this.contactForm, async () => {
7877
this.submitted.emit(this.contactForm().value());
79-
}
78+
});
8079
}
8180
}
8281
```
@@ -86,7 +85,7 @@ export class ContactFormComponent {
8685
This file provides the template for the form, which includes a checkbox that controls the validation of another field.
8786

8887
```html
89-
<form (ngSubmit)="handleSubmit()">
88+
<form (submit)="handleSubmit(); $event.preventDefault()">
9089
<div>
9190
<label>
9291
<input type="checkbox" [control]="contactForm.subscribe" />
@@ -112,8 +111,8 @@ This file provides the template for the form, which includes a checkbox that con
112111

113112
## Usage Notes
114113

115-
- The submit button is disabled based on the form's `valid` signal (`[disabled]="!contactForm().valid()"`).
116-
- The `handleSubmit` method checks `this.contactForm().valid()` as a safeguard before emitting the data.
114+
- The native `(submit)` event on the `<form>` element is bound to the `handleSubmit` method. It's important to call `$event.preventDefault()` to prevent a full page reload.
115+
- The `handleSubmit` method uses the `submit()` helper to manage the submission process.
117116

118117
## How to Use This Example
119118

packages/forms/signals/examples/05-dynamic-field-state-hidden.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Use this pattern when you need to show or hide a form control based on another c
2424
## Key Concepts
2525

2626
- **`hidden()`:** A function used within a schema to define the logic that determines whether a field should be hidden. It takes a `FieldPath` and a predicate function.
27-
- **Predicate Function:** The function passed to `hidden` that returns `true` when the field should be hidden.
27+
- **`submit()`:** An async helper function that manages the form's submission state and should be called from the submit event handler.
2828
- **`@if` block:** Used in the template to conditionally render the form control based on the field's `hidden` signal.
2929

3030
## Example Files
@@ -37,7 +37,7 @@ This file defines the component's logic, including a schema that uses the `hidde
3737

3838
```typescript
3939
import { Component, signal, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
40-
import { form, schema, hidden, required } from '@angular/forms/signals';
40+
import { form, schema, hidden, required, submit } from '@angular/forms/signals';
4141
import { JsonPipe } from '@angular/common';
4242

4343
export interface ContactForm {
@@ -70,10 +70,10 @@ export class ContactFormComponent {
7070

7171
contactForm = form(this.contactModel, contactSchema);
7272

73-
handleSubmit() {
74-
if (this.contactForm().valid()) {
73+
async handleSubmit() {
74+
await submit(this.contactForm, async () => {
7575
this.submitted.emit(this.contactForm().value());
76-
}
76+
});
7777
}
7878
}
7979
```
@@ -83,7 +83,7 @@ export class ContactFormComponent {
8383
This file provides the template for the form, using an `@if` block to conditionally render a field based on its `hidden` signal.
8484

8585
```html
86-
<form (ngSubmit)="handleSubmit()">
86+
<form (submit)="handleSubmit(); $event.preventDefault()">
8787
<div>
8888
<label>Reason for Contact:</label>
8989
<select [control]="contactForm.reason">

packages/forms/signals/examples/06-conditionally-disabling-fields.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Use this pattern whenever you need to enable or disable a form control based on
2424
## Key Concepts
2525

2626
- **`disabled()`:** A function used within a schema to define the logic that determines whether a field should be disabled. It takes a `FieldPath` and a predicate function.
27-
- **Predicate Function:** The function passed to `disabled` that returns `true` when the field should be disabled.
27+
- **`submit()`:** An async helper function that manages the form's submission state and should be called from the submit event handler.
2828
- **`disabled` signal:** A signal on the `FieldState` that reflects the field's disabled status, which can be used for attribute binding in the template.
2929

3030
## Example Files
@@ -37,7 +37,7 @@ This file defines the component's logic, including a schema that uses the `disab
3737

3838
```typescript
3939
import { Component, signal, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
40-
import { form, schema, disabled, required } from '@angular/forms/signals';
40+
import { form, schema, disabled, required, submit } from '@angular/forms/signals';
4141
import { JsonPipe } from '@angular/common';
4242

4343
export interface PromoForm {
@@ -70,10 +70,10 @@ export class PromoFormComponent {
7070

7171
promoForm = form(this.promoModel, promoSchema);
7272

73-
handleSubmit() {
74-
if (this.promoForm().valid()) {
73+
async handleSubmit() {
74+
await submit(this.promoForm, async () => {
7575
this.submitted.emit(this.promoForm().value());
76-
}
76+
});
7777
}
7878
}
7979
```
@@ -83,7 +83,7 @@ export class PromoFormComponent {
8383
This file provides the template for the form, binding an input's `disabled` attribute to its corresponding field's `disabled` signal.
8484

8585
```html
86-
<form (ngSubmit)="handleSubmit()">
86+
<form (submit)="handleSubmit(); $event.preventDefault()">
8787
<div>
8888
<label>
8989
<input type="checkbox" [control]="promoForm.hasPromoCode" />

packages/forms/signals/examples/07-dynamic-arrays.md

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ Use this pattern when you have a form where the user can add or remove multiple
2626
## Key Concepts
2727

2828
- **`applyEach()`:** A function used within a schema to apply a sub-schema to each element of an array field.
29-
- **`valid` signal:** A signal on the `FieldState` that is `true` only when the field and all its descendants are valid.
30-
- **`(ngSubmit)`:** An event binding on a `<form>` element that triggers a method when the form is submitted.
29+
- **`submit()`:** An async helper function that manages the form's submission state and should be called from the submit event handler.
3130

3231
## Example Files
3332

@@ -39,7 +38,7 @@ This file defines the component's logic, including methods to dynamically add an
3938

4039
```typescript
4140
import { Component, signal, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
42-
import { form, schema, applyEach, validate } from '@angular/forms/signals';
41+
import { form, schema, applyEach, validate, submit } from '@angular/forms/signals';
4342
import { JsonPipe } from '@angular/common';
4443

4544
export interface Question {
@@ -53,11 +52,11 @@ export interface SurveyForm {
5352
}
5453

5554
const questionSchema = schema<Question>((question) => {
56-
validate(question.text, ({value}) => (value === '' ? { required: true } : null));
55+
validate(question.text, ({value}) => (value() === '' ? { required: true } : null));
5756
});
5857

5958
const surveySchema = schema<SurveyForm>((survey) => {
60-
validate(survey.title, ({value}) => (value === '' ? { required: true } : null));
59+
validate(survey.title, ({value}) => (value() === '' ? { required: true } : null));
6160
applyEach(survey.questions, questionSchema);
6261
});
6362

@@ -81,21 +80,21 @@ export class SurveyFormComponent {
8180
addQuestion() {
8281
this.surveyModel.update(survey => {
8382
survey.questions.push({ text: '', required: false });
84-
return survey;
83+
return { ...survey };
8584
});
8685
}
8786

8887
removeQuestion(index: number) {
8988
this.surveyModel.update(survey => {
9089
survey.questions.splice(index, 1);
91-
return survey;
90+
return { ...survey };
9291
});
9392
}
9493

95-
handleSubmit() {
96-
if (this.surveyForm().valid()) {
94+
async handleSubmit() {
95+
await submit(this.surveyForm, async () => {
9796
this.submitted.emit(this.surveyForm().value());
98-
}
97+
});
9998
}
10099
}
101100
```
@@ -105,7 +104,7 @@ export class SurveyFormComponent {
105104
This file provides the template for the form, using an `@for` block to render the fields for each question in the array.
106105

107106
```html
108-
<form (ngSubmit)="handleSubmit()">
107+
<form (submit)="handleSubmit(); $event.preventDefault()">
109108
<div>
110109
<label>
111110
Survey Title:

packages/forms/signals/examples/08-custom-form-control.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Use this pattern when you have a form input that is more complex than a standard
2727

2828
- **`FormValueControl<T>`:** An interface that custom form control components can implement to integrate with signal forms.
2929
- **`model()`:** A function from `@angular/core` that creates a two-way bound signal input, used here to implement the `value` property of the `FormValueControl` interface.
30-
- **`(ngSubmit)`:** An event binding on a `<form>` element that triggers a method when the form is submitted.
30+
- **`submit()`:** An async helper function that manages the form's submission state and should be called from the submit event handler.
3131

3232
## Example Files
3333

@@ -70,7 +70,7 @@ This file defines the parent form component that consumes the custom numeric ste
7070

7171
```typescript
7272
import { Component, signal, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
73-
import { form } from '@angular/forms/signals';
73+
import { form, submit } from '@angular/forms/signals';
7474
import { JsonPipe } from '@angular/common';
7575
import { NumericStepperComponent } from './numeric-stepper.component';
7676

@@ -96,8 +96,10 @@ export class ProductFormComponent {
9696

9797
productForm = form(this.productModel);
9898

99-
handleSubmit() {
100-
this.submitted.emit(this.productForm().value());
99+
async handleSubmit() {
100+
await submit(this.productForm, async () => {
101+
this.submitted.emit(this.productForm().value());
102+
});
101103
}
102104
}
103105
```
@@ -107,7 +109,7 @@ export class ProductFormComponent {
107109
This file provides the template for the parent form, showing how the `[control]` directive is used on the custom component.
108110

109111
```html
110-
<form (ngSubmit)="handleSubmit()">
112+
<form (submit)="handleSubmit(); $event.preventDefault()">
111113
<div>
112114
<label>
113115
Product Name:

packages/forms/signals/examples/09-async-validation.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Use this pattern for validation that requires a network request. The `validateHt
2929

3030
- **`validateHttp()`:** A function used within a schema to add an asynchronous validator to a field based on an HTTP request.
3131
- **`pending` signal:** A signal on the `FieldState` that is `true` while an asynchronous validator is running.
32+
- **`submit()`:** An async helper function that manages the form's submission state and should be called from the submit event handler.
3233

3334
## Example Files
3435

@@ -40,7 +41,7 @@ This file defines the component's logic, including a schema that uses `validateH
4041

4142
```typescript
4243
import { Component, signal, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
43-
import { form, schema, required, validateHttp } from '@angular/forms/signals';
44+
import { form, schema, required, validateHttp, submit } from '@angular/forms/signals';
4445
import { JsonPipe } from '@angular/common';
4546

4647
export interface RegistrationForm {
@@ -71,10 +72,10 @@ export class RegistrationFormComponent {
7172

7273
registrationForm = form(this.registrationModel, registrationSchema);
7374

74-
handleSubmit() {
75-
if (this.registrationForm().valid()) {
75+
async handleSubmit() {
76+
await submit(this.registrationForm, async () => {
7677
this.submitted.emit(this.registrationForm().value());
77-
}
78+
});
7879
}
7980
}
8081
```
@@ -84,7 +85,7 @@ export class RegistrationFormComponent {
8485
This file provides the template for the form, showing how to display feedback while the asynchronous validation is pending.
8586

8687
```html
87-
<form (ngSubmit)="handleSubmit()">
88+
<form (submit)="handleSubmit(); $event.preventDefault()">
8889
<div>
8990
<label>
9091
Username:

packages/forms/signals/examples/10-debounced-async-validation.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Use this pattern for any asynchronous validation that is tied to user input, suc
3131
- **`validateAsync()`:** The function used to add an asynchronous validator.
3232
- **RxJS `Subject` and `switchMap`:** Used to create an observable stream of user input. `switchMap` is crucial for canceling previous pending requests when new input arrives.
3333
- **RxJS `debounceTime`:** An operator that waits for a specified period of silence in the observable stream before emitting the latest value.
34-
- **`pending` signal:** A signal on the `FieldState` that is `true` while async validation is running.
34+
- **`submit()`:** An async helper function that manages the form's submission state and should be called from the submit event handler.
3535

3636
## Example Files
3737

@@ -43,7 +43,7 @@ This file defines the component's logic, using an RxJS `Subject` to debounce use
4343

4444
```typescript
4545
import { Component, inject, OnDestroy, signal, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
46-
import { form, schema, validate, validateAsync } from '@angular/forms/signals';
46+
import { form, schema, validate, validateAsync, submit } from '@angular/forms/signals';
4747
import { JsonPipe } from '@angular/common';
4848
import { UsernameService } from './username.service';
4949
import { Subject } from 'rxjs';
@@ -78,20 +78,20 @@ export class RegistrationFormComponent implements OnDestroy {
7878

7979
private createSchema() {
8080
return schema<RegistrationForm>((form) => {
81-
validate(form.username, ({value}) => (value === '' ? { required: true } : null));
81+
validate(form.username, ({value}) => (value() === '' ? { required: true } : null));
8282
validateAsync(form.username, async ({value}) => {
83-
if (value === '') return null;
84-
this.username$.next(value);
83+
if (value() === '') return null;
84+
this.username$.next(value());
8585
const isTaken = await firstValueFrom(this.validationResult$);
8686
return isTaken ? { unique: true } : null;
8787
});
8888
});
8989
}
9090

91-
handleSubmit() {
92-
if (this.registrationForm().valid()) {
91+
async handleSubmit() {
92+
await submit(this.registrationForm, async () => {
9393
this.submitted.emit(this.registrationForm().value());
94-
}
94+
});
9595
}
9696

9797
ngOnDestroy() {
@@ -106,7 +106,7 @@ export class RegistrationFormComponent implements OnDestroy {
106106
This file provides the template for the form, showing how to disable the submit button while validation is in progress.
107107

108108
```html
109-
<form (ngSubmit)="handleSubmit()">
109+
<form (submit)="handleSubmit(); $event.preventDefault()">
110110
<div>
111111
<label>
112112
Username:

packages/forms/signals/examples/11-reusable-custom-validators.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Use this pattern when you have validation logic that is shared across multiple f
2727

2828
- **Validator Function:** A standalone function that takes configuration (like a minimum length) and returns a validation function that can be used with `validate`.
2929
- **Factory Pattern:** Using a higher-order function to create and configure your validator, making it highly reusable.
30-
- **`(ngSubmit)`:** An event binding on a `<form>` element that triggers a method when the form is submitted.
30+
- **`submit()`:** An async helper function that manages the form's submission state and should be called from the submit event handler.
3131

3232
## Example Files
3333

@@ -42,8 +42,8 @@ import { FieldValidator } from '@angular/forms/signals';
4242

4343
export function minLength(minLength: number): FieldValidator<string> {
4444
return ({value}) => {
45-
if (value.length < minLength) {
46-
return { minLength: { requiredLength: minLength, actualLength: value.length } };
45+
if (value().length < minLength) {
46+
return { minLength: { requiredLength: minLength, actualLength: value().length } };
4747
}
4848
return null;
4949
};
@@ -56,7 +56,7 @@ This file defines the component's logic, importing and using the reusable valida
5656

5757
```typescript
5858
import { Component, signal, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
59-
import { form, schema, validate } from '@angular/forms/signals';
59+
import { form, schema, validate, submit } from '@angular/forms/signals';
6060
import { JsonPipe } from '@angular/common';
6161
import { minLength } from './custom-validators';
6262

@@ -87,10 +87,10 @@ export class RegistrationFormComponent {
8787

8888
registrationForm = form(this.registrationModel, registrationSchema);
8989

90-
handleSubmit() {
91-
if (this.registrationForm().valid()) {
90+
async handleSubmit() {
91+
await submit(this.registrationForm, async () => {
9292
this.submitted.emit(this.registrationForm().value());
93-
}
93+
});
9494
}
9595
}
9696
```
@@ -100,7 +100,7 @@ export class RegistrationFormComponent {
100100
This file provides the template for the form, displaying detailed error messages from the custom validator.
101101

102102
```html
103-
<form (ngSubmit)="handleSubmit()">
103+
<form (submit)="handleSubmit(); $event.preventDefault()">
104104
<div>
105105
<label>
106106
Username:

0 commit comments

Comments
 (0)