66 * found in the LICENSE file at https://angular.dev/license
77 */
88
9- import {
10- inject ,
11- Injector ,
12- runInInjectionContext ,
13- ɵRuntimeError as RuntimeError ,
14- untracked ,
15- WritableSignal ,
16- } from '@angular/core' ;
17- import { RuntimeErrorCode } from '../errors' ;
9+ import { inject , Injector , runInInjectionContext , WritableSignal } from '@angular/core' ;
1810import { BasicFieldAdapter , FieldAdapter } from '../field/field_adapter' ;
1911import { FormFieldManager } from '../field/manager' ;
2012import { FieldNode } from '../field/node' ;
21- import { addDefaultField } from '../field/validation' ;
2213import { DYNAMIC } from '../schema/logic' ;
2314import { FieldPathNode } from '../schema/path_node' ;
2415import { assertPathIsCurrent , SchemaImpl } from '../schema/schema' ;
2516import { normalizeFormArgs } from '../util/normalize_form_args' ;
26- import { isArray } from '../util/type_guards' ;
27- import type { ValidationError } from './rules/validation/validation_errors' ;
2817import type {
18+ FieldState ,
2919 FieldTree ,
20+ FormSubmitOptions ,
3021 ItemType ,
3122 LogicFn ,
32- OneOrMany ,
3323 PathKind ,
3424 Schema ,
3525 SchemaFn ,
3626 SchemaOrSchemaFn ,
3727 SchemaPath ,
38- TreeValidationResult ,
3928} from './types' ;
4029
41- /**
42- * Options that can be specified when submitting a form.
43- *
44- * @experimental 21.2.0
45- */
46- export interface FormSubmitOptions < TModel > {
47- /** Function to run when submitting the form data (when form is valid). */
48- action : ( form : FieldTree < TModel > ) => Promise < TreeValidationResult > ;
49- /** Function to run when attempting to submit the form data but validation is failing. */
50- onInvalid ?: ( form : FieldTree < TModel > ) => void ;
51- /**
52- * Whether to ignore any of the validators when submitting:
53- * - 'pending': Will submit if there are no invalid validators, pending validators do not block submission (default)
54- * - 'none': Will not submit unless all validators are passing, pending validators block submission
55- * - 'ignore': Will always submit regardless of invalid or pending validators
56- */
57- ignoreValidators ?: 'pending' | 'none' | 'all' ;
58- }
59-
6030/**
6131 * Options that may be specified when creating a form.
6232 *
@@ -399,6 +369,7 @@ export function applyWhenValue(
399369 *
400370 * @category submission
401371 * @experimental 21.0.0
372+ * @deprecated Use `form().submit(options)` instead.
402373 */
403374export async function submit < TModel > (
404375 form : FieldTree < TModel > ,
@@ -412,81 +383,8 @@ export async function submit<TModel>(
412383 form : FieldTree < TModel > ,
413384 options ?: FormSubmitOptions < TModel > | FormSubmitOptions < TModel > [ 'action' ] ,
414385) : Promise < boolean > {
415- const node = form ( ) as unknown as FieldNode ;
416- const opts =
417- typeof options === 'function'
418- ? { action : options }
419- : ( {
420- ...( node . structure . fieldManager . submitOptions ?? { } ) ,
421- ...( options ?? { } ) ,
422- } as Partial < FormSubmitOptions < TModel > > ) ;
423- const action = opts ?. action ;
424- if ( ! action ) {
425- throw new RuntimeError (
426- RuntimeErrorCode . MISSING_SUBMIT_ACTION ,
427- ngDevMode &&
428- 'Cannot submit form with no submit action. Specify the action when creating the form, or as an additional argument to `submit()`.' ,
429- ) ;
430- }
431-
432- const onInvalid = opts ?. onInvalid ;
433- const ignoreValidators = opts ?. ignoreValidators ?? 'pending' ;
434-
435- // Determine whether or not to run the action based on the current validity.
436- let shouldRunAction = true ;
437- untracked ( ( ) => {
438- markAllAsTouched ( node ) ;
439-
440- if ( ignoreValidators === 'none' ) {
441- shouldRunAction = node . valid ( ) ;
442- } else if ( ignoreValidators === 'pending' ) {
443- shouldRunAction = ! node . invalid ( ) ;
444- }
445- } ) ;
446-
447- // Run the action (or alternatively the `onInvalid` callback)
448- try {
449- if ( shouldRunAction ) {
450- node . submitState . selfSubmitting . set ( true ) ;
451- const errors = await untracked ( ( ) => action ?.( form ) ) ;
452- errors && setSubmissionErrors ( node , errors ) ;
453- return ! errors || ( isArray ( errors ) && errors . length === 0 ) ;
454- } else {
455- untracked ( ( ) => onInvalid ?.( form ) ) ;
456- }
457- return false ;
458- } finally {
459- node . submitState . selfSubmitting . set ( false ) ;
460- }
461- }
462-
463- /**
464- * Sets a list of submission errors to their individual fields.
465- *
466- * @param submittedField The field that was submitted, resulting in the errors.
467- * @param errors The errors to set.
468- */
469- function setSubmissionErrors (
470- submittedField : FieldNode ,
471- errors : OneOrMany < ValidationError . WithOptionalFieldTree > ,
472- ) {
473- if ( ! isArray ( errors ) ) {
474- errors = [ errors ] ;
475- }
476- const errorsByField = new Map < FieldNode , ValidationError . WithFieldTree [ ] > ( ) ;
477- for ( const error of errors ) {
478- const errorWithField = addDefaultField ( error , submittedField . fieldProxy ) ;
479- const field = errorWithField . fieldTree ( ) as FieldNode ;
480- let fieldErrors = errorsByField . get ( field ) ;
481- if ( ! fieldErrors ) {
482- fieldErrors = [ ] ;
483- errorsByField . set ( field , fieldErrors ) ;
484- }
485- fieldErrors . push ( errorWithField ) ;
486- }
487- for ( const [ field , fieldErrors ] of errorsByField ) {
488- field . submitState . submissionErrors . set ( fieldErrors ) ;
489- }
386+ const opts = typeof options === 'function' ? { action : options } : options ;
387+ return ( form ( ) as FieldState < any > ) . submit ( opts ) ;
490388}
491389
492390/**
@@ -501,17 +399,3 @@ function setSubmissionErrors(
501399export function schema < TValue > ( fn : SchemaFn < TValue > ) : Schema < TValue > {
502400 return SchemaImpl . create ( fn ) as unknown as Schema < TValue > ;
503401}
504-
505- /** Marks a {@link node} and its descendants as touched. */
506- function markAllAsTouched ( node : FieldNode ) {
507- // Don't mark hidden, disabled, or readonly fields as touched since they don't contribute to the
508- // form's validity. This also prevents errors from appearing immediately if they're later made
509- // interactive.
510- if ( node . validationState . shouldSkipValidation ( ) ) {
511- return ;
512- }
513- node . markAsTouched ( ) ;
514- for ( const child of node . structure . children ( ) ) {
515- markAllAsTouched ( child ) ;
516- }
517- }
0 commit comments