docs: add new signal forms cross field logic guide#67551
docs: add new signal forms cross field logic guide#67551bencodezen wants to merge 1 commit intoangular:mainfrom
Conversation
|
Deployed adev-preview for 8f754b7 to: https://ng-dev-previews-fw--pr-angular-angular-67551-adev-prev-2jz3jbuu.web.app Note: As new commits are pushed to this pull request, this link is updated after the preview is rebuilt. |
|
Ahh sorry for the confusion @SkyZeroZx and @jnizet. I'm in the midst of revising the PR which is why I put it in draft mode (but forgot to take off the label). Appreciate the initial feedback. I'll make sure to consider the suggestions before the official review is live! |
8f104c7 to
8f754b7
Compare
|
|
||
| The date range example from the previous section validates the end date against the start date. Because the rule reads `valueOf(schemaPath.startDate)`, it re-evaluates automatically whenever either date changes. In other words, a single validator is enough to keep the error state correct. | ||
|
|
||
| However, that single validator only places the error on the end date field. If you want both fields to show an error when the range is invalid, add a matching validation rule to each field: |
There was a problem hiding this comment.
Is it worth offering some advice/guidance here on how to think about which field you should attach the validator to? In talking to people about this topic, two main questions tend to come up:
- Should I put the validator on one of the two fields, or on the parent field (where i can read both values directly)?
- What if I want the error to show up on both fields?
My usual answer is: this choice depends on what UX you want. Put validation on the field(s) where you want the UI to show invalid state / want the error to show up. For good UX, this should be at the place where the user would most likely go to fix the error.
| minLength(schemaPath.password, 8); | ||
|
|
||
| validate(schemaPath.confirmPassword, ({value, valueOf, stateOf}) => { | ||
| if (stateOf(schemaPath.password).invalid()) { |
| } | ||
| ``` | ||
|
|
||
| The `stateOf()` call returns the other field's [field state](api/forms/signals/FieldState), giving you access to signals like `invalid()`, `touched()`, and `dirty()`. Because these are signals, the rule re-evaluates whenever the password field's validity changes. |
There was a problem hiding this comment.
Maybe a cautionary callout here: you can read the state of other fields with stateOf, but take care not to read state which depends on your field's validation, as that creates a cycle.
You can't, for example, add a validator which checks whether the parent field is valid, because the parent's validity depends on its children's validity, which includes your validator.
|
|
||
| The examples so far use `validate()` to add an error to the field being validated. But sometimes the error belongs on a _different_ field. For example, a shipping form might need to restrict shipping methods based on package weight — the validation logic needs both fields, but the error should appear on the shipping method field. | ||
|
|
||
| `validateTree` handles this by letting you specify which field receives the error: |
There was a problem hiding this comment.
This particular example is better served by validate + valueOf, I think. validateTree is more for situations when:
- you need to perform some higher level validation of a part of the model
- that validation may return errors that you want to show in specific places
For example, suppose I'm building a Sudoku puzzle form. It'd be really awkward to set up the whole 9x9 board with individual field validators which read surrounding fields with valueOf. It'd also be challenging to deduplicate errors in that case, and expensive because each validator would be checking the same conditions on its own.
validateTree lets me define validation logic that happens for the whole board and then decides which errors to show and on which fields. It's especially useful if you have existing APIs (like a Sudoku library) that provide this validation - you can easily run those at the top level and map the results to specific errors on specific fields.
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: N/A
What is the new behavior?
New cross-field logic guide for Signal Forms!
Does this PR introduce a breaking change?
Other information