Skip to content

docs: add new signal forms schema guide#68064

Open
bencodezen wants to merge 3 commits intoangular:mainfrom
bencodezen:docs/signal-forms-schema-guide
Open

docs: add new signal forms schema guide#68064
bencodezen wants to merge 3 commits intoangular:mainfrom
bencodezen:docs/signal-forms-schema-guide

Conversation

@bencodezen
Copy link
Copy Markdown
Contributor

@bencodezen bencodezen commented Apr 7, 2026

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.dev application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

@bencodezen bencodezen added action: review The PR is still awaiting reviews from at least one requested reviewer target: patch This PR is targeted for the next patch release area: docs Related to the documentation adev: preview labels Apr 7, 2026
@ngbot ngbot bot added this to the Backlog milestone Apr 7, 2026
@bencodezen bencodezen requested review from alxhub and kirjs April 7, 2026 14:39
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

Deployed adev-preview for 8a06669 to: https://ng-dev-previews-fw--pr-angular-angular-68064-adev-prev-bpw5n5jl.web.app

Note: As new commits are pushed to this pull request, this link is updated after the preview is rebuilt.

@bencodezen bencodezen requested a review from JeanMeche April 7, 2026 16:36
Copy link
Copy Markdown
Contributor

@michael-small michael-small left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff, thank you for this and all the other guides


When you pass a schema function to `form()`, that function _runs once_ during form creation. Its job is to set up the form's logic tree by declaring which fields have validation, which fields are disabled, and which fields depend on other fields. This is the **structural layer** of your form.

Inside a schema function, you call rule functions such as `disabled()` and `validate()`. These rule functions accept reactive logic that recomputes whenever the signals they reference change. Other rules like `required()` accept optional configuration, including a `when` function that conditionally activates the rule. Together, these form the **behavioral layer** of your form during runtime.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alxhub will be making the way conditional rules are applied more consistent: Experimental Signal Forms.

We might want to adjust these documents to reflect the intended end state.

profileForm = form(this.profileModel, (schemaPath) => {
required(schemaPath.age);

apply(schemaPath.name, (name) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's a compelling reason to demonstrate an inline schema with apply(), since you could always unnest the schema inline:

profileForm = form(this.profileModel, (schemaPath) => {
  required(schemaPath.age);
  required(schemaPath.name.first);
  required(schemaPath.name.last);
});

For forms with multiple levels of nesting, you can call `apply()` inside another `apply()`. Each level scopes the schema function to that part of the tree:

```ts
orderForm = form(this.orderModel, (schemaPath) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same point as my previous comment. Even if the argument is to reduce repetition, you could simply use a local variable to achieve the same effect:

orderForm = form(this.orderModel, (order) => {
  const billing = order.billing;
  required(billing.method);

  const address = billing.address;
  required(address.street);
  required(address.city);
  required(address.zip);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: review The PR is still awaiting reviews from at least one requested reviewer adev: preview area: docs Related to the documentation target: patch This PR is targeted for the next patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants