@angular/forms 20.2.3 → 21.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v20.2.3
2
+ * @license Angular v21.0.0-next.1
3
3
  * (c) 2010-2025 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -1401,12 +1401,12 @@ class NgControlStatusGroup extends AbstractControlStatus {
1401
1401
  super(cd);
1402
1402
  }
1403
1403
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: NgControlStatusGroup, deps: [{ token: ControlContainer, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
1404
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: NgControlStatusGroup, isStandalone: false, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]", host: { properties: { "class.ng-untouched": "isUntouched", "class.ng-touched": "isTouched", "class.ng-pristine": "isPristine", "class.ng-dirty": "isDirty", "class.ng-valid": "isValid", "class.ng-invalid": "isInvalid", "class.ng-pending": "isPending", "class.ng-submitted": "isSubmitted" } }, usesInheritance: true, ngImport: i0 });
1404
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: NgControlStatusGroup, isStandalone: false, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]", host: { properties: { "class.ng-untouched": "isUntouched", "class.ng-touched": "isTouched", "class.ng-pristine": "isPristine", "class.ng-dirty": "isDirty", "class.ng-valid": "isValid", "class.ng-invalid": "isInvalid", "class.ng-pending": "isPending", "class.ng-submitted": "isSubmitted" } }, usesInheritance: true, ngImport: i0 });
1405
1405
  }
1406
1406
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: NgControlStatusGroup, decorators: [{
1407
1407
  type: Directive,
1408
1408
  args: [{
1409
- selector: '[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]',
1409
+ selector: '[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]',
1410
1410
  host: ngGroupStatusHost,
1411
1411
  standalone: false,
1412
1412
  }]
@@ -1467,8 +1467,8 @@ const ngModelWithFormGroupExample = `
1467
1467
  `;
1468
1468
 
1469
1469
  function controlParentException(nameOrIndex) {
1470
- return new _RuntimeError(1050 /* RuntimeErrorCode.FORM_CONTROL_NAME_MISSING_PARENT */, `formControlName must be used with a parent formGroup directive. You'll want to add a formGroup
1471
- directive and pass it an existing FormGroup instance (you can create one in your class).
1470
+ return new _RuntimeError(1050 /* RuntimeErrorCode.FORM_CONTROL_NAME_MISSING_PARENT */, `formControlName must be used with a parent formGroup or formArray directive. You'll want to add a formGroup/formArray
1471
+ directive and pass it an existing FormGroup/FormArray instance (you can create one in your class).
1472
1472
 
1473
1473
  ${describeFormControl(nameOrIndex)}
1474
1474
 
@@ -3481,7 +3481,7 @@ function _ngModelWarning(name, type, instance, warningConfig) {
3481
3481
  }
3482
3482
  }
3483
3483
 
3484
- const formDirectiveProvider$1 = {
3484
+ const formDirectiveProvider$2 = {
3485
3485
  provide: ControlContainer,
3486
3486
  useExisting: forwardRef(() => NgForm),
3487
3487
  };
@@ -3764,13 +3764,13 @@ class NgForm extends ControlContainer {
3764
3764
  return path.length ? this.form.get(path) : this.form;
3765
3765
  }
3766
3766
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: NgForm, deps: [{ token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }, { token: CALL_SET_DISABLED_STATE, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3767
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: NgForm, isStandalone: false, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: { options: ["ngFormOptions", "options"] }, outputs: { ngSubmit: "ngSubmit" }, host: { listeners: { "submit": "onSubmit($event)", "reset": "onReset()" } }, providers: [formDirectiveProvider$1], exportAs: ["ngForm"], usesInheritance: true, ngImport: i0 });
3767
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: NgForm, isStandalone: false, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: { options: ["ngFormOptions", "options"] }, outputs: { ngSubmit: "ngSubmit" }, host: { listeners: { "submit": "onSubmit($event)", "reset": "onReset()" } }, providers: [formDirectiveProvider$2], exportAs: ["ngForm"], usesInheritance: true, ngImport: i0 });
3768
3768
  }
3769
3769
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: NgForm, decorators: [{
3770
3770
  type: Directive,
3771
3771
  args: [{
3772
- selector: 'form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]',
3773
- providers: [formDirectiveProvider$1],
3772
+ selector: 'form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]',
3773
+ providers: [formDirectiveProvider$2],
3774
3774
  host: { '(submit)': 'onSubmit($event)', '(reset)': 'onReset()' },
3775
3775
  outputs: ['ngSubmit'],
3776
3776
  exportAs: 'ngForm',
@@ -4833,941 +4833,758 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2",
4833
4833
  }] });
4834
4834
 
4835
4835
  /**
4836
- * Token to provide to turn off the ngModel warning on formControl and formControlName.
4837
- */
4838
- const NG_MODEL_WITH_FORM_CONTROL_WARNING = new InjectionToken(ngDevMode ? 'NgModelWithFormControlWarning' : '');
4839
- const formControlBinding = {
4840
- provide: NgControl,
4841
- useExisting: forwardRef(() => FormControlDirective),
4842
- };
4843
- /**
4844
- * @description
4845
- * Synchronizes a standalone `FormControl` instance to a form control element.
4836
+ * Tracks the value and validity state of an array of `FormControl`,
4837
+ * `FormGroup` or `FormArray` instances.
4846
4838
  *
4847
- * Note that support for using the `ngModel` input property and `ngModelChange` event with reactive
4848
- * form directives was deprecated in Angular v6 and is scheduled for removal in
4849
- * a future version of Angular.
4839
+ * A `FormArray` aggregates the values of each child `FormControl` into an array.
4840
+ * It calculates its status by reducing the status values of its children. For example, if one of
4841
+ * the controls in a `FormArray` is invalid, the entire array becomes invalid.
4850
4842
  *
4851
- * @see [Reactive Forms Guide](guide/forms/reactive-forms)
4852
- * @see {@link FormControl}
4853
- * @see {@link AbstractControl}
4843
+ * `FormArray` accepts one generic argument, which is the type of the controls inside.
4844
+ * If you need a heterogenous array, use {@link UntypedFormArray}.
4845
+ *
4846
+ * `FormArray` is one of the four fundamental building blocks used to define forms in Angular,
4847
+ * along with `FormControl`, `FormGroup`, and `FormRecord`.
4854
4848
  *
4855
4849
  * @usageNotes
4856
4850
  *
4857
- * The following example shows how to register a standalone control and set its value.
4851
+ * ### Create an array of form controls
4858
4852
  *
4859
- * {@example forms/ts/simpleFormControl/simple_form_control_example.ts region='Component'}
4853
+ * ```ts
4854
+ * const arr = new FormArray([
4855
+ * new FormControl('Nancy', Validators.minLength(2)),
4856
+ * new FormControl('Drew'),
4857
+ * ]);
4858
+ *
4859
+ * console.log(arr.value); // ['Nancy', 'Drew']
4860
+ * console.log(arr.status); // 'VALID'
4861
+ * ```
4862
+ *
4863
+ * ### Create a form array with array-level validators
4864
+ *
4865
+ * You include array-level validators and async validators. These come in handy
4866
+ * when you want to perform validation that considers the value of more than one child
4867
+ * control.
4868
+ *
4869
+ * The two types of validators are passed in separately as the second and third arg
4870
+ * respectively, or together as part of an options object.
4871
+ *
4872
+ * ```ts
4873
+ * const arr = new FormArray([
4874
+ * new FormControl('Nancy'),
4875
+ * new FormControl('Drew')
4876
+ * ], {validators: myValidator, asyncValidators: myAsyncValidator});
4877
+ * ```
4878
+ *
4879
+ * ### Set the updateOn property for all controls in a form array
4880
+ *
4881
+ * The options object is used to set a default value for each child
4882
+ * control's `updateOn` property. If you set `updateOn` to `'blur'` at the
4883
+ * array level, all child controls default to 'blur', unless the child
4884
+ * has explicitly specified a different `updateOn` value.
4885
+ *
4886
+ * ```ts
4887
+ * const arr = new FormArray([
4888
+ * new FormControl()
4889
+ * ], {updateOn: 'blur'});
4890
+ * ```
4891
+ *
4892
+ * ### Adding or removing controls from a form array
4893
+ *
4894
+ * To change the controls in the array, use the `push`, `insert`, `removeAt` or `clear` methods
4895
+ * in `FormArray` itself. These methods ensure the controls are properly tracked in the
4896
+ * form's hierarchy. Do not modify the array of `AbstractControl`s used to instantiate
4897
+ * the `FormArray` directly, as that result in strange and unexpected behavior such
4898
+ * as broken change detection.
4860
4899
  *
4861
- * @ngModule ReactiveFormsModule
4862
4900
  * @publicApi
4863
4901
  */
4864
- class FormControlDirective extends NgControl {
4865
- _ngModelWarningConfig;
4866
- callSetDisabledState;
4867
- /**
4868
- * Internal reference to the view model value.
4869
- * @docs-private
4870
- */
4871
- viewModel;
4872
- /**
4873
- * @description
4874
- * Tracks the `FormControl` instance bound to the directive.
4875
- */
4876
- form;
4902
+ class FormArray extends AbstractControl {
4877
4903
  /**
4878
- * @description
4879
- * Triggers a warning in dev mode that this input should not be used with reactive forms.
4904
+ * Creates a new `FormArray` instance.
4905
+ *
4906
+ * @param controls An array of child controls. Each child control is given an index
4907
+ * where it is registered.
4908
+ *
4909
+ * @param validatorOrOpts A synchronous validator function, or an array of
4910
+ * such functions, or an `AbstractControlOptions` object that contains validation functions
4911
+ * and a validation trigger.
4912
+ *
4913
+ * @param asyncValidator A single async validator or array of async validator functions
4914
+ *
4880
4915
  */
4881
- set isDisabled(isDisabled) {
4882
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
4883
- console.warn(disabledAttrWarning);
4884
- }
4916
+ constructor(controls, validatorOrOpts, asyncValidator) {
4917
+ super(pickValidators(validatorOrOpts), pickAsyncValidators(asyncValidator, validatorOrOpts));
4918
+ this.controls = controls;
4919
+ this._initObservables();
4920
+ this._setUpdateStrategy(validatorOrOpts);
4921
+ this._setUpControls();
4922
+ this.updateValueAndValidity({
4923
+ onlySelf: true,
4924
+ // If `asyncValidator` is present, it will trigger control status change from `PENDING` to
4925
+ // `VALID` or `INVALID`.
4926
+ // The status should be broadcasted via the `statusChanges` observable, so we set `emitEvent`
4927
+ // to `true` to allow that during the control creation process.
4928
+ emitEvent: !!this.asyncValidator,
4929
+ });
4885
4930
  }
4886
- // TODO(kara): remove next 4 properties once deprecation period is over
4887
- /** @deprecated as of v6 */
4888
- model;
4889
- /** @deprecated as of v6 */
4890
- update = new EventEmitter();
4931
+ controls;
4891
4932
  /**
4892
- * @description
4893
- * Static property used to track whether any ngModel warnings have been sent across
4894
- * all instances of FormControlDirective. Used to support warning config of "once".
4933
+ * Get the `AbstractControl` at the given `index` in the array.
4895
4934
  *
4896
- * @internal
4935
+ * @param index Index in the array to retrieve the control. If `index` is negative, it will wrap
4936
+ * around from the back, and if index is greatly negative (less than `-length`), the result is
4937
+ * undefined. This behavior is the same as `Array.at(index)`.
4897
4938
  */
4898
- static _ngModelWarningSentOnce = false;
4939
+ at(index) {
4940
+ return this.controls[this._adjustIndex(index)];
4941
+ }
4899
4942
  /**
4900
- * @description
4901
- * Instance property used to track whether an ngModel warning has been sent out for this
4902
- * particular `FormControlDirective` instance. Used to support warning config of "always".
4943
+ * Insert a new `AbstractControl` at the end of the array.
4903
4944
  *
4904
- * @internal
4945
+ * @param control Form control to be inserted
4946
+ * @param options Specifies whether this FormArray instance should emit events after a new
4947
+ * control is added.
4948
+ * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
4949
+ * `valueChanges` observables emit events with the latest status and value when the control is
4950
+ * inserted. When false, no events are emitted.
4905
4951
  */
4906
- _ngModelWarningSent = false;
4907
- constructor(validators, asyncValidators, valueAccessors, _ngModelWarningConfig, callSetDisabledState) {
4908
- super();
4909
- this._ngModelWarningConfig = _ngModelWarningConfig;
4910
- this.callSetDisabledState = callSetDisabledState;
4911
- this._setValidators(validators);
4912
- this._setAsyncValidators(asyncValidators);
4913
- this.valueAccessor = selectValueAccessor(this, valueAccessors);
4914
- }
4915
- /** @docs-private */
4916
- ngOnChanges(changes) {
4917
- if (this._isControlChanged(changes)) {
4918
- const previousForm = changes['form'].previousValue;
4919
- if (previousForm) {
4920
- cleanUpControl(previousForm, this, /* validateControlPresenceOnChange */ false);
4921
- }
4922
- setUpControl(this.form, this, this.callSetDisabledState);
4923
- this.form.updateValueAndValidity({ emitEvent: false });
4924
- }
4925
- if (isPropertyUpdated(changes, this.viewModel)) {
4926
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
4927
- _ngModelWarning('formControl', FormControlDirective, this, this._ngModelWarningConfig);
4928
- }
4929
- this.form.setValue(this.model);
4930
- this.viewModel = this.model;
4952
+ push(control, options = {}) {
4953
+ if (Array.isArray(control)) {
4954
+ control.forEach((ctrl) => {
4955
+ this.controls.push(ctrl);
4956
+ this._registerControl(ctrl);
4957
+ });
4931
4958
  }
4932
- }
4933
- /** @docs-private */
4934
- ngOnDestroy() {
4935
- if (this.form) {
4936
- cleanUpControl(this.form, this, /* validateControlPresenceOnChange */ false);
4959
+ else {
4960
+ this.controls.push(control);
4961
+ this._registerControl(control);
4937
4962
  }
4963
+ this.updateValueAndValidity({ emitEvent: options.emitEvent });
4964
+ this._onCollectionChange();
4938
4965
  }
4939
4966
  /**
4940
- * @description
4941
- * Returns an array that represents the path from the top-level form to this control.
4942
- * Each index is the string name of the control on that level.
4967
+ * Insert a new `AbstractControl` at the given `index` in the array.
4968
+ *
4969
+ * @param index Index in the array to insert the control. If `index` is negative, wraps around
4970
+ * from the back. If `index` is greatly negative (less than `-length`), prepends to the array.
4971
+ * This behavior is the same as `Array.splice(index, 0, control)`.
4972
+ * @param control Form control to be inserted
4973
+ * @param options Specifies whether this FormArray instance should emit events after a new
4974
+ * control is inserted.
4975
+ * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
4976
+ * `valueChanges` observables emit events with the latest status and value when the control is
4977
+ * inserted. When false, no events are emitted.
4943
4978
  */
4944
- get path() {
4945
- return [];
4979
+ insert(index, control, options = {}) {
4980
+ this.controls.splice(index, 0, control);
4981
+ this._registerControl(control);
4982
+ this.updateValueAndValidity({ emitEvent: options.emitEvent });
4946
4983
  }
4947
4984
  /**
4948
- * @description
4949
- * The `FormControl` bound to this directive.
4985
+ * Remove the control at the given `index` in the array.
4986
+ *
4987
+ * @param index Index in the array to remove the control. If `index` is negative, wraps around
4988
+ * from the back. If `index` is greatly negative (less than `-length`), removes the first
4989
+ * element. This behavior is the same as `Array.splice(index, 1)`.
4990
+ * @param options Specifies whether this FormArray instance should emit events after a
4991
+ * control is removed.
4992
+ * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
4993
+ * `valueChanges` observables emit events with the latest status and value when the control is
4994
+ * removed. When false, no events are emitted.
4950
4995
  */
4951
- get control() {
4952
- return this.form;
4996
+ removeAt(index, options = {}) {
4997
+ // Adjust the index, then clamp it at no less than 0 to prevent undesired underflows.
4998
+ let adjustedIndex = this._adjustIndex(index);
4999
+ if (adjustedIndex < 0)
5000
+ adjustedIndex = 0;
5001
+ if (this.controls[adjustedIndex])
5002
+ this.controls[adjustedIndex]._registerOnCollectionChange(() => { });
5003
+ this.controls.splice(adjustedIndex, 1);
5004
+ this.updateValueAndValidity({ emitEvent: options.emitEvent });
4953
5005
  }
4954
5006
  /**
4955
- * @description
4956
- * Sets the new value for the view model and emits an `ngModelChange` event.
5007
+ * Replace an existing control.
4957
5008
  *
4958
- * @param newValue The new value for the view model.
5009
+ * @param index Index in the array to replace the control. If `index` is negative, wraps around
5010
+ * from the back. If `index` is greatly negative (less than `-length`), replaces the first
5011
+ * element. This behavior is the same as `Array.splice(index, 1, control)`.
5012
+ * @param control The `AbstractControl` control to replace the existing control
5013
+ * @param options Specifies whether this FormArray instance should emit events after an
5014
+ * existing control is replaced with a new one.
5015
+ * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
5016
+ * `valueChanges` observables emit events with the latest status and value when the control is
5017
+ * replaced with a new one. When false, no events are emitted.
4959
5018
  */
4960
- viewToModelUpdate(newValue) {
4961
- this.viewModel = newValue;
4962
- this.update.emit(newValue);
4963
- }
4964
- _isControlChanged(changes) {
4965
- return changes.hasOwnProperty('form');
4966
- }
4967
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormControlDirective, deps: [{ token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }, { token: NG_VALUE_ACCESSOR, optional: true, self: true }, { token: NG_MODEL_WITH_FORM_CONTROL_WARNING, optional: true }, { token: CALL_SET_DISABLED_STATE, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
4968
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormControlDirective, isStandalone: false, selector: "[formControl]", inputs: { form: ["formControl", "form"], isDisabled: ["disabled", "isDisabled"], model: ["ngModel", "model"] }, outputs: { update: "ngModelChange" }, providers: [formControlBinding], exportAs: ["ngForm"], usesInheritance: true, usesOnChanges: true, ngImport: i0 });
4969
- }
4970
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormControlDirective, decorators: [{
4971
- type: Directive,
4972
- args: [{
4973
- selector: '[formControl]',
4974
- providers: [formControlBinding],
4975
- exportAs: 'ngForm',
4976
- standalone: false,
4977
- }]
4978
- }], ctorParameters: () => [{ type: undefined, decorators: [{
4979
- type: Optional
4980
- }, {
4981
- type: Self
4982
- }, {
4983
- type: Inject,
4984
- args: [NG_VALIDATORS]
4985
- }] }, { type: undefined, decorators: [{
4986
- type: Optional
4987
- }, {
4988
- type: Self
4989
- }, {
4990
- type: Inject,
4991
- args: [NG_ASYNC_VALIDATORS]
4992
- }] }, { type: undefined, decorators: [{
4993
- type: Optional
4994
- }, {
4995
- type: Self
4996
- }, {
4997
- type: Inject,
4998
- args: [NG_VALUE_ACCESSOR]
4999
- }] }, { type: undefined, decorators: [{
5000
- type: Optional
5001
- }, {
5002
- type: Inject,
5003
- args: [NG_MODEL_WITH_FORM_CONTROL_WARNING]
5004
- }] }, { type: undefined, decorators: [{
5005
- type: Optional
5006
- }, {
5007
- type: Inject,
5008
- args: [CALL_SET_DISABLED_STATE]
5009
- }] }], propDecorators: { form: [{
5010
- type: Input,
5011
- args: ['formControl']
5012
- }], isDisabled: [{
5013
- type: Input,
5014
- args: ['disabled']
5015
- }], model: [{
5016
- type: Input,
5017
- args: ['ngModel']
5018
- }], update: [{
5019
- type: Output,
5020
- args: ['ngModelChange']
5021
- }] } });
5022
-
5023
- const formDirectiveProvider = {
5024
- provide: ControlContainer,
5025
- useExisting: forwardRef(() => FormGroupDirective),
5026
- };
5027
- /**
5028
- * @description
5029
- *
5030
- * Binds an existing `FormGroup` or `FormRecord` to a DOM element.
5031
- *
5032
- * This directive accepts an existing `FormGroup` instance. It will then use this
5033
- * `FormGroup` instance to match any child `FormControl`, `FormGroup`/`FormRecord`,
5034
- * and `FormArray` instances to child `FormControlName`, `FormGroupName`,
5035
- * and `FormArrayName` directives.
5036
- *
5037
- * @see [Reactive Forms Guide](guide/forms/reactive-forms)
5038
- * @see {@link AbstractControl}
5039
- *
5040
- * @usageNotes
5041
- * ### Register Form Group
5042
- *
5043
- * The following example registers a `FormGroup` with first name and last name controls,
5044
- * and listens for the *ngSubmit* event when the button is clicked.
5045
- *
5046
- * {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
5047
- *
5048
- * @ngModule ReactiveFormsModule
5049
- * @publicApi
5050
- */
5051
- class FormGroupDirective extends ControlContainer {
5052
- callSetDisabledState;
5053
- /**
5054
- * @description
5055
- * Reports whether the form submission has been triggered.
5056
- */
5057
- get submitted() {
5058
- return untracked(this._submittedReactive);
5059
- }
5060
- // TODO(atscott): Remove once invalid API usage is cleaned up internally
5061
- set submitted(value) {
5062
- this._submittedReactive.set(value);
5063
- }
5064
- /** @internal */
5065
- _submitted = computed(() => this._submittedReactive(), ...(ngDevMode ? [{ debugName: "_submitted" }] : []));
5066
- _submittedReactive = signal(false, ...(ngDevMode ? [{ debugName: "_submittedReactive" }] : []));
5067
- /**
5068
- * Reference to an old form group input value, which is needed to cleanup
5069
- * old instance in case it was replaced with a new one.
5070
- */
5071
- _oldForm;
5072
- /**
5073
- * Callback that should be invoked when controls in FormGroup or FormArray collection change
5074
- * (added or removed). This callback triggers corresponding DOM updates.
5075
- */
5076
- _onCollectionChange = () => this._updateDomValue();
5077
- /**
5078
- * @description
5079
- * Tracks the list of added `FormControlName` instances
5080
- */
5081
- directives = [];
5082
- /**
5083
- * @description
5084
- * Tracks the `FormGroup` bound to this directive.
5085
- */
5086
- form = null;
5087
- /**
5088
- * @description
5089
- * Emits an event when the form submission has been triggered.
5090
- */
5091
- ngSubmit = new EventEmitter();
5092
- constructor(validators, asyncValidators, callSetDisabledState) {
5093
- super();
5094
- this.callSetDisabledState = callSetDisabledState;
5095
- this._setValidators(validators);
5096
- this._setAsyncValidators(asyncValidators);
5097
- }
5098
- /** @docs-private */
5099
- ngOnChanges(changes) {
5100
- if ((typeof ngDevMode === 'undefined' || ngDevMode) && !this.form) {
5101
- throw missingFormException();
5102
- }
5103
- if (changes.hasOwnProperty('form')) {
5104
- this._updateValidators();
5105
- this._updateDomValue();
5106
- this._updateRegistrations();
5107
- this._oldForm = this.form;
5108
- }
5109
- }
5110
- /** @docs-private */
5111
- ngOnDestroy() {
5112
- if (this.form) {
5113
- cleanUpValidators(this.form, this);
5114
- // Currently the `onCollectionChange` callback is rewritten each time the
5115
- // `_registerOnCollectionChange` function is invoked. The implication is that cleanup should
5116
- // happen *only* when the `onCollectionChange` callback was set by this directive instance.
5117
- // Otherwise it might cause overriding a callback of some other directive instances. We should
5118
- // consider updating this logic later to make it similar to how `onChange` callbacks are
5119
- // handled, see https://github.com/angular/angular/issues/39732 for additional info.
5120
- if (this.form._onCollectionChange === this._onCollectionChange) {
5121
- this.form._registerOnCollectionChange(() => { });
5122
- }
5019
+ setControl(index, control, options = {}) {
5020
+ // Adjust the index, then clamp it at no less than 0 to prevent undesired underflows.
5021
+ let adjustedIndex = this._adjustIndex(index);
5022
+ if (adjustedIndex < 0)
5023
+ adjustedIndex = 0;
5024
+ if (this.controls[adjustedIndex])
5025
+ this.controls[adjustedIndex]._registerOnCollectionChange(() => { });
5026
+ this.controls.splice(adjustedIndex, 1);
5027
+ if (control) {
5028
+ this.controls.splice(adjustedIndex, 0, control);
5029
+ this._registerControl(control);
5123
5030
  }
5031
+ this.updateValueAndValidity({ emitEvent: options.emitEvent });
5032
+ this._onCollectionChange();
5124
5033
  }
5125
5034
  /**
5126
- * @description
5127
- * Returns this directive's instance.
5128
- */
5129
- get formDirective() {
5130
- return this;
5131
- }
5132
- /**
5133
- * @description
5134
- * Returns the `FormGroup` bound to this directive.
5135
- */
5136
- get control() {
5137
- return this.form;
5138
- }
5139
- /**
5140
- * @description
5141
- * Returns an array representing the path to this group. Because this directive
5142
- * always lives at the top level of a form, it always an empty array.
5035
+ * Length of the control array.
5143
5036
  */
5144
- get path() {
5145
- return [];
5037
+ get length() {
5038
+ return this.controls.length;
5146
5039
  }
5147
5040
  /**
5148
- * @description
5149
- * Method that sets up the control directive in this group, re-calculates its value
5150
- * and validity, and adds the instance to the internal list of directives.
5041
+ * Sets the value of the `FormArray`. It accepts an array that matches
5042
+ * the structure of the control.
5151
5043
  *
5152
- * @param dir The `FormControlName` directive instance.
5153
- */
5154
- addControl(dir) {
5155
- const ctrl = this.form.get(dir.path);
5156
- setUpControl(ctrl, dir, this.callSetDisabledState);
5157
- ctrl.updateValueAndValidity({ emitEvent: false });
5158
- this.directives.push(dir);
5159
- return ctrl;
5160
- }
5161
- /**
5162
- * @description
5163
- * Retrieves the `FormControl` instance from the provided `FormControlName` directive
5044
+ * This method performs strict checks, and throws an error if you try
5045
+ * to set the value of a control that doesn't exist or if you exclude the
5046
+ * value of a control.
5164
5047
  *
5165
- * @param dir The `FormControlName` directive instance.
5166
- */
5167
- getControl(dir) {
5168
- return this.form.get(dir.path);
5169
- }
5170
- /**
5171
- * @description
5172
- * Removes the `FormControlName` instance from the internal list of directives
5048
+ * @usageNotes
5049
+ * ### Set the values for the controls in the form array
5173
5050
  *
5174
- * @param dir The `FormControlName` directive instance.
5175
- */
5176
- removeControl(dir) {
5177
- cleanUpControl(dir.control || null, dir, /* validateControlPresenceOnChange */ false);
5178
- removeListItem$1(this.directives, dir);
5179
- }
5180
- /**
5181
- * Adds a new `FormGroupName` directive instance to the form.
5051
+ * ```ts
5052
+ * const arr = new FormArray([
5053
+ * new FormControl(),
5054
+ * new FormControl()
5055
+ * ]);
5056
+ * console.log(arr.value); // [null, null]
5182
5057
  *
5183
- * @param dir The `FormGroupName` directive instance.
5184
- */
5185
- addFormGroup(dir) {
5186
- this._setUpFormContainer(dir);
5187
- }
5188
- /**
5189
- * Performs the necessary cleanup when a `FormGroupName` directive instance is removed from the
5190
- * view.
5058
+ * arr.setValue(['Nancy', 'Drew']);
5059
+ * console.log(arr.value); // ['Nancy', 'Drew']
5060
+ * ```
5191
5061
  *
5192
- * @param dir The `FormGroupName` directive instance.
5193
- */
5194
- removeFormGroup(dir) {
5195
- this._cleanUpFormContainer(dir);
5196
- }
5197
- /**
5198
- * @description
5199
- * Retrieves the `FormGroup` for a provided `FormGroupName` directive instance
5062
+ * @param value Array of values for the controls
5063
+ * @param options Configure options that determine how the control propagates changes and
5064
+ * emits events after the value changes
5200
5065
  *
5201
- * @param dir The `FormGroupName` directive instance.
5066
+ * * `onlySelf`: When true, each change only affects this control, and not its parent. Default
5067
+ * is false.
5068
+ * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
5069
+ * `valueChanges`
5070
+ * observables emit events with the latest status and value when the control value is updated.
5071
+ * When false, no events are emitted.
5072
+ * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
5073
+ * updateValueAndValidity} method.
5202
5074
  */
5203
- getFormGroup(dir) {
5204
- return this.form.get(dir.path);
5075
+ setValue(value, options = {}) {
5076
+ assertAllValuesPresent(this, false, value);
5077
+ value.forEach((newValue, index) => {
5078
+ assertControlPresent(this, false, index);
5079
+ this.at(index).setValue(newValue, { onlySelf: true, emitEvent: options.emitEvent });
5080
+ });
5081
+ this.updateValueAndValidity(options);
5205
5082
  }
5206
5083
  /**
5207
- * Performs the necessary setup when a `FormArrayName` directive instance is added to the view.
5084
+ * Patches the value of the `FormArray`. It accepts an array that matches the
5085
+ * structure of the control, and does its best to match the values to the correct
5086
+ * controls in the group.
5208
5087
  *
5209
- * @param dir The `FormArrayName` directive instance.
5210
- */
5211
- addFormArray(dir) {
5212
- this._setUpFormContainer(dir);
5213
- }
5214
- /**
5215
- * Performs the necessary cleanup when a `FormArrayName` directive instance is removed from the
5216
- * view.
5088
+ * It accepts both super-sets and sub-sets of the array without throwing an error.
5217
5089
  *
5218
- * @param dir The `FormArrayName` directive instance.
5090
+ * @usageNotes
5091
+ * ### Patch the values for controls in a form array
5092
+ *
5093
+ * ```ts
5094
+ * const arr = new FormArray([
5095
+ * new FormControl(),
5096
+ * new FormControl()
5097
+ * ]);
5098
+ * console.log(arr.value); // [null, null]
5099
+ *
5100
+ * arr.patchValue(['Nancy']);
5101
+ * console.log(arr.value); // ['Nancy', null]
5102
+ * ```
5103
+ *
5104
+ * @param value Array of latest values for the controls
5105
+ * @param options Configure options that determine how the control propagates changes and
5106
+ * emits events after the value changes
5107
+ *
5108
+ * * `onlySelf`: When true, each change only affects this control, and not its parent. Default
5109
+ * is false.
5110
+ * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
5111
+ * `valueChanges` observables emit events with the latest status and value when the control
5112
+ * value is updated. When false, no events are emitted. The configuration options are passed to
5113
+ * the {@link AbstractControl#updateValueAndValidity updateValueAndValidity} method.
5219
5114
  */
5220
- removeFormArray(dir) {
5221
- this._cleanUpFormContainer(dir);
5115
+ patchValue(value, options = {}) {
5116
+ // Even though the `value` argument type doesn't allow `null` and `undefined` values, the
5117
+ // `patchValue` can be called recursively and inner data structures might have these values,
5118
+ // so we just ignore such cases when a field containing FormArray instance receives `null` or
5119
+ // `undefined` as a value.
5120
+ if (value == null /* both `null` and `undefined` */)
5121
+ return;
5122
+ value.forEach((newValue, index) => {
5123
+ if (this.at(index)) {
5124
+ this.at(index).patchValue(newValue, { onlySelf: true, emitEvent: options.emitEvent });
5125
+ }
5126
+ });
5127
+ this.updateValueAndValidity(options);
5222
5128
  }
5223
5129
  /**
5224
- * @description
5225
- * Retrieves the `FormArray` for a provided `FormArrayName` directive instance.
5130
+ * Resets the `FormArray` and all descendants are marked `pristine` and `untouched`, and the
5131
+ * value of all descendants to null or null maps.
5226
5132
  *
5227
- * @param dir The `FormArrayName` directive instance.
5133
+ * You reset to a specific form state by passing in an array of states
5134
+ * that matches the structure of the control. The state is a standalone value
5135
+ * or a form state object with both a value and a disabled status.
5136
+ *
5137
+ * @usageNotes
5138
+ * ### Reset the values in a form array
5139
+ *
5140
+ * ```ts
5141
+ * const arr = new FormArray([
5142
+ * new FormControl(),
5143
+ * new FormControl()
5144
+ * ]);
5145
+ * arr.reset(['name', 'last name']);
5146
+ *
5147
+ * console.log(arr.value); // ['name', 'last name']
5148
+ * ```
5149
+ *
5150
+ * ### Reset the values in a form array and the disabled status for the first control
5151
+ *
5152
+ * ```ts
5153
+ * arr.reset([
5154
+ * {value: 'name', disabled: true},
5155
+ * 'last'
5156
+ * ]);
5157
+ *
5158
+ * console.log(arr.value); // ['last']
5159
+ * console.log(arr.at(0).status); // 'DISABLED'
5160
+ * ```
5161
+ *
5162
+ * @param value Array of values for the controls
5163
+ * @param options Configure options that determine how the control propagates changes and
5164
+ * emits events after the value changes
5165
+ *
5166
+ * * `onlySelf`: When true, each change only affects this control, and not its parent. Default
5167
+ * is false.
5168
+ * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
5169
+ * `valueChanges`
5170
+ * observables emit events with the latest status and value when the control is reset.
5171
+ * When false, no events are emitted.
5172
+ * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
5173
+ * updateValueAndValidity} method.
5228
5174
  */
5229
- getFormArray(dir) {
5230
- return this.form.get(dir.path);
5175
+ reset(value = [], options = {}) {
5176
+ this._forEachChild((control, index) => {
5177
+ control.reset(value[index], { onlySelf: true, emitEvent: options.emitEvent });
5178
+ });
5179
+ this._updatePristine(options, this);
5180
+ this._updateTouched(options, this);
5181
+ this.updateValueAndValidity(options);
5231
5182
  }
5232
5183
  /**
5233
- * Sets the new value for the provided `FormControlName` directive.
5184
+ * The aggregate value of the array, including any disabled controls.
5234
5185
  *
5235
- * @param dir The `FormControlName` directive instance.
5236
- * @param value The new value for the directive's control.
5186
+ * Reports all values regardless of disabled status.
5237
5187
  */
5238
- updateModel(dir, value) {
5239
- const ctrl = this.form.get(dir.path);
5240
- ctrl.setValue(value);
5188
+ getRawValue() {
5189
+ return this.controls.map((control) => control.getRawValue());
5241
5190
  }
5242
5191
  /**
5243
- * @description
5244
- * Method called with the "submit" event is triggered on the form.
5245
- * Triggers the `ngSubmit` emitter to emit the "submit" event as its payload.
5192
+ * Remove all controls in the `FormArray`.
5246
5193
  *
5247
- * @param $event The "submit" event object
5194
+ * @param options Specifies whether this FormArray instance should emit events after all
5195
+ * controls are removed.
5196
+ * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
5197
+ * `valueChanges` observables emit events with the latest status and value when all controls
5198
+ * in this FormArray instance are removed. When false, no events are emitted.
5199
+ *
5200
+ * @usageNotes
5201
+ * ### Remove all elements from a FormArray
5202
+ *
5203
+ * ```ts
5204
+ * const arr = new FormArray([
5205
+ * new FormControl(),
5206
+ * new FormControl()
5207
+ * ]);
5208
+ * console.log(arr.length); // 2
5209
+ *
5210
+ * arr.clear();
5211
+ * console.log(arr.length); // 0
5212
+ * ```
5213
+ *
5214
+ * It's a simpler and more efficient alternative to removing all elements one by one:
5215
+ *
5216
+ * ```ts
5217
+ * const arr = new FormArray([
5218
+ * new FormControl(),
5219
+ * new FormControl()
5220
+ * ]);
5221
+ *
5222
+ * while (arr.length) {
5223
+ * arr.removeAt(0);
5224
+ * }
5225
+ * ```
5248
5226
  */
5249
- onSubmit($event) {
5250
- this._submittedReactive.set(true);
5251
- syncPendingControls(this.form, this.directives);
5252
- this.ngSubmit.emit($event);
5253
- this.form._events.next(new FormSubmittedEvent(this.control));
5254
- // Forms with `method="dialog"` have some special behavior that won't reload the page and that
5255
- // shouldn't be prevented. Note that we need to null check the `event` and the `target`, because
5256
- // some internal apps call this method directly with the wrong arguments.
5257
- return $event?.target?.method === 'dialog';
5227
+ clear(options = {}) {
5228
+ if (this.controls.length < 1)
5229
+ return;
5230
+ this._forEachChild((control) => control._registerOnCollectionChange(() => { }));
5231
+ this.controls.splice(0);
5232
+ this.updateValueAndValidity({ emitEvent: options.emitEvent });
5258
5233
  }
5259
5234
  /**
5260
- * @description
5261
- * Method called when the "reset" event is triggered on the form.
5235
+ * Adjusts a negative index by summing it with the length of the array. For very negative
5236
+ * indices, the result may remain negative.
5237
+ * @internal
5262
5238
  */
5263
- onReset() {
5264
- this.resetForm();
5239
+ _adjustIndex(index) {
5240
+ return index < 0 ? index + this.length : index;
5265
5241
  }
5266
- /**
5267
- * @description
5268
- * Resets the form to an initial value and resets its submitted status.
5269
- *
5270
- * @param value The new value for the form, `undefined` by default
5271
- */
5272
- resetForm(value = undefined, options = {}) {
5273
- this.form.reset(value, options);
5274
- this._submittedReactive.set(false);
5275
- if (options?.emitEvent !== false) {
5276
- this.form._events.next(new FormResetEvent(this.form));
5277
- }
5242
+ /** @internal */
5243
+ _syncPendingControls() {
5244
+ let subtreeUpdated = this.controls.reduce((updated, child) => {
5245
+ return child._syncPendingControls() ? true : updated;
5246
+ }, false);
5247
+ if (subtreeUpdated)
5248
+ this.updateValueAndValidity({ onlySelf: true });
5249
+ return subtreeUpdated;
5278
5250
  }
5279
5251
  /** @internal */
5280
- _updateDomValue() {
5281
- this.directives.forEach((dir) => {
5282
- const oldCtrl = dir.control;
5283
- const newCtrl = this.form.get(dir.path);
5284
- if (oldCtrl !== newCtrl) {
5285
- // Note: the value of the `dir.control` may not be defined, for example when it's a first
5286
- // `FormControl` that is added to a `FormGroup` instance (via `addControl` call).
5287
- cleanUpControl(oldCtrl || null, dir);
5288
- // Check whether new control at the same location inside the corresponding `FormGroup` is an
5289
- // instance of `FormControl` and perform control setup only if that's the case.
5290
- // Note: we don't need to clear the list of directives (`this.directives`) here, it would be
5291
- // taken care of in the `removeControl` method invoked when corresponding `formControlName`
5292
- // directive instance is being removed (invoked from `FormControlName.ngOnDestroy`).
5293
- if (isFormControl(newCtrl)) {
5294
- setUpControl(newCtrl, dir, this.callSetDisabledState);
5295
- dir.control = newCtrl;
5296
- }
5297
- }
5252
+ _forEachChild(cb) {
5253
+ this.controls.forEach((control, index) => {
5254
+ cb(control, index);
5298
5255
  });
5299
- this.form._updateTreeValidity({ emitEvent: false });
5300
5256
  }
5301
- _setUpFormContainer(dir) {
5302
- const ctrl = this.form.get(dir.path);
5303
- setUpFormContainer(ctrl, dir);
5304
- // NOTE: this operation looks unnecessary in case no new validators were added in
5305
- // `setUpFormContainer` call. Consider updating this code to match the logic in
5306
- // `_cleanUpFormContainer` function.
5307
- ctrl.updateValueAndValidity({ emitEvent: false });
5257
+ /** @internal */
5258
+ _updateValue() {
5259
+ this.value = this.controls
5260
+ .filter((control) => control.enabled || this.disabled)
5261
+ .map((control) => control.value);
5308
5262
  }
5309
- _cleanUpFormContainer(dir) {
5310
- if (this.form) {
5311
- const ctrl = this.form.get(dir.path);
5312
- if (ctrl) {
5313
- const isControlUpdated = cleanUpFormContainer(ctrl, dir);
5314
- if (isControlUpdated) {
5315
- // Run validity check only in case a control was updated (i.e. view validators were
5316
- // removed) as removing view validators might cause validity to change.
5317
- ctrl.updateValueAndValidity({ emitEvent: false });
5318
- }
5319
- }
5320
- }
5263
+ /** @internal */
5264
+ _anyControls(condition) {
5265
+ return this.controls.some((control) => control.enabled && condition(control));
5321
5266
  }
5322
- _updateRegistrations() {
5323
- this.form._registerOnCollectionChange(this._onCollectionChange);
5324
- if (this._oldForm) {
5325
- this._oldForm._registerOnCollectionChange(() => { });
5326
- }
5267
+ /** @internal */
5268
+ _setUpControls() {
5269
+ this._forEachChild((control) => this._registerControl(control));
5327
5270
  }
5328
- _updateValidators() {
5329
- setUpValidators(this.form, this);
5330
- if (this._oldForm) {
5331
- cleanUpValidators(this._oldForm, this);
5271
+ /** @internal */
5272
+ _allControlsDisabled() {
5273
+ for (const control of this.controls) {
5274
+ if (control.enabled)
5275
+ return false;
5332
5276
  }
5277
+ return this.controls.length > 0 || this.disabled;
5278
+ }
5279
+ _registerControl(control) {
5280
+ control.setParent(this);
5281
+ control._registerOnCollectionChange(this._onCollectionChange);
5282
+ }
5283
+ /** @internal */
5284
+ _find(name) {
5285
+ return this.at(name) ?? null;
5333
5286
  }
5334
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormGroupDirective, deps: [{ token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }, { token: CALL_SET_DISABLED_STATE, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
5335
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormGroupDirective, isStandalone: false, selector: "[formGroup]", inputs: { form: ["formGroup", "form"] }, outputs: { ngSubmit: "ngSubmit" }, host: { listeners: { "submit": "onSubmit($event)", "reset": "onReset()" } }, providers: [formDirectiveProvider], exportAs: ["ngForm"], usesInheritance: true, usesOnChanges: true, ngImport: i0 });
5336
5287
  }
5337
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormGroupDirective, decorators: [{
5338
- type: Directive,
5339
- args: [{
5340
- selector: '[formGroup]',
5341
- providers: [formDirectiveProvider],
5342
- host: { '(submit)': 'onSubmit($event)', '(reset)': 'onReset()' },
5343
- exportAs: 'ngForm',
5344
- standalone: false,
5345
- }]
5346
- }], ctorParameters: () => [{ type: undefined, decorators: [{
5347
- type: Optional
5348
- }, {
5349
- type: Self
5350
- }, {
5351
- type: Inject,
5352
- args: [NG_VALIDATORS]
5353
- }] }, { type: undefined, decorators: [{
5354
- type: Optional
5355
- }, {
5356
- type: Self
5357
- }, {
5358
- type: Inject,
5359
- args: [NG_ASYNC_VALIDATORS]
5360
- }] }, { type: undefined, decorators: [{
5361
- type: Optional
5362
- }, {
5363
- type: Inject,
5364
- args: [CALL_SET_DISABLED_STATE]
5365
- }] }], propDecorators: { form: [{
5366
- type: Input,
5367
- args: ['formGroup']
5368
- }], ngSubmit: [{
5369
- type: Output
5370
- }] } });
5371
-
5372
- const formGroupNameProvider = {
5373
- provide: ControlContainer,
5374
- useExisting: forwardRef(() => FormGroupName),
5375
- };
5288
+ const UntypedFormArray = FormArray;
5376
5289
  /**
5377
5290
  * @description
5291
+ * Asserts that the given control is an instance of `FormArray`
5378
5292
  *
5379
- * Syncs a nested `FormGroup` or `FormRecord` to a DOM element.
5380
- *
5381
- * This directive can only be used with a parent `FormGroupDirective`.
5382
- *
5383
- * It accepts the string name of the nested `FormGroup` or `FormRecord` to link, and
5384
- * looks for a `FormGroup` or `FormRecord` registered with that name in the parent
5385
- * `FormGroup` instance you passed into `FormGroupDirective`.
5386
- *
5387
- * Use nested form groups to validate a sub-group of a
5388
- * form separately from the rest or to group the values of certain
5389
- * controls into their own nested object.
5390
- *
5391
- * @see [Reactive Forms Guide](guide/forms/reactive-forms)
5392
- *
5393
- * @usageNotes
5394
- *
5395
- * ### Access the group by name
5396
- *
5397
- * The following example uses the `AbstractControl.get` method to access the
5398
- * associated `FormGroup`
5399
- *
5400
- * ```ts
5401
- * this.form.get('name');
5402
- * ```
5403
- *
5404
- * ### Access individual controls in the group
5405
- *
5406
- * The following example uses the `AbstractControl.get` method to access
5407
- * individual controls within the group using dot syntax.
5408
- *
5409
- * ```ts
5410
- * this.form.get('name.first');
5411
- * ```
5412
- *
5413
- * ### Register a nested `FormGroup`.
5414
- *
5415
- * The following example registers a nested *name* `FormGroup` within an existing `FormGroup`,
5416
- * and provides methods to retrieve the nested `FormGroup` and individual controls.
5417
- *
5418
- * {@example forms/ts/nestedFormGroup/nested_form_group_example.ts region='Component'}
5419
- *
5420
- * @ngModule ReactiveFormsModule
5421
5293
  * @publicApi
5422
5294
  */
5423
- class FormGroupName extends AbstractFormGroupDirective {
5424
- /**
5425
- * @description
5426
- * Tracks the name of the `FormGroup` bound to the directive. The name corresponds
5427
- * to a key in the parent `FormGroup` or `FormArray`.
5428
- * Accepts a name as a string or a number.
5429
- * The name in the form of a string is useful for individual forms,
5430
- * while the numerical form allows for form groups to be bound
5431
- * to indices when iterating over groups in a `FormArray`.
5432
- */
5433
- name = null;
5434
- constructor(parent, validators, asyncValidators) {
5435
- super();
5436
- this._parent = parent;
5437
- this._setValidators(validators);
5438
- this._setAsyncValidators(asyncValidators);
5439
- }
5440
- /** @internal */
5441
- _checkParentType() {
5442
- if (hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
5443
- throw groupParentException();
5444
- }
5445
- }
5446
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormGroupName, deps: [{ token: ControlContainer, host: true, optional: true, skipSelf: true }, { token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
5447
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormGroupName, isStandalone: false, selector: "[formGroupName]", inputs: { name: ["formGroupName", "name"] }, providers: [formGroupNameProvider], usesInheritance: true, ngImport: i0 });
5448
- }
5449
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormGroupName, decorators: [{
5450
- type: Directive,
5451
- args: [{
5452
- selector: '[formGroupName]',
5453
- providers: [formGroupNameProvider],
5454
- standalone: false,
5455
- }]
5456
- }], ctorParameters: () => [{ type: ControlContainer, decorators: [{
5457
- type: Optional
5458
- }, {
5459
- type: Host
5460
- }, {
5461
- type: SkipSelf
5462
- }] }, { type: undefined, decorators: [{
5463
- type: Optional
5464
- }, {
5465
- type: Self
5466
- }, {
5467
- type: Inject,
5468
- args: [NG_VALIDATORS]
5469
- }] }, { type: undefined, decorators: [{
5470
- type: Optional
5471
- }, {
5472
- type: Self
5473
- }, {
5474
- type: Inject,
5475
- args: [NG_ASYNC_VALIDATORS]
5476
- }] }], propDecorators: { name: [{
5477
- type: Input,
5478
- args: ['formGroupName']
5479
- }] } });
5480
- const formArrayNameProvider = {
5481
- provide: ControlContainer,
5482
- useExisting: forwardRef(() => FormArrayName),
5483
- };
5295
+ const isFormArray = (control) => control instanceof FormArray;
5296
+
5484
5297
  /**
5485
5298
  * @description
5486
5299
  *
5487
- * Syncs a nested `FormArray` to a DOM element.
5488
- *
5489
- * This directive is designed to be used with a parent `FormGroupDirective` (selector:
5490
- * `[formGroup]`).
5491
- *
5492
- * It accepts the string name of the nested `FormArray` you want to link, and
5493
- * will look for a `FormArray` registered with that name in the parent
5494
- * `FormGroup` instance you passed into `FormGroupDirective`.
5495
- *
5496
- * @see [Reactive Forms Guide](guide/forms/reactive-forms)
5497
- * @see {@link AbstractControl}
5498
- *
5499
- * @usageNotes
5500
- *
5501
- * ### Example
5502
- *
5503
- * {@example forms/ts/nestedFormArray/nested_form_array_example.ts region='Component'}
5300
+ * Abstract class for top-level form directives (FormArrayDirective, FormGroupDirective) who bind an
5301
+ * existing `Form` to a DOM element.
5504
5302
  *
5505
- * @ngModule ReactiveFormsModule
5506
5303
  * @publicApi
5507
5304
  */
5508
- class FormArrayName extends ControlContainer {
5509
- /** @internal */
5510
- _parent;
5305
+ class AbstractFormDirective extends ControlContainer {
5306
+ callSetDisabledState;
5511
5307
  /**
5512
5308
  * @description
5513
- * Tracks the name of the `FormArray` bound to the directive. The name corresponds
5514
- * to a key in the parent `FormGroup` or `FormArray`.
5515
- * Accepts a name as a string or a number.
5516
- * The name in the form of a string is useful for individual forms,
5517
- * while the numerical form allows for form arrays to be bound
5518
- * to indices when iterating over arrays in a `FormArray`.
5309
+ * Reports whether the form submission has been triggered.
5519
5310
  */
5520
- name = null;
5521
- constructor(parent, validators, asyncValidators) {
5522
- super();
5523
- this._parent = parent;
5524
- this._setValidators(validators);
5525
- this._setAsyncValidators(asyncValidators);
5311
+ get submitted() {
5312
+ return untracked(this._submittedReactive);
5526
5313
  }
5527
- /**
5528
- * A lifecycle method called when the directive's inputs are initialized. For internal use only.
5529
- * @throws If the directive does not have a valid parent.
5530
- * @docs-private
5531
- */
5532
- ngOnInit() {
5533
- if (hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
5534
- throw arrayParentException();
5535
- }
5536
- this.formDirective.addFormArray(this);
5314
+ // TODO(atscott): Remove once invalid API usage is cleaned up internally
5315
+ set submitted(value) {
5316
+ this._submittedReactive.set(value);
5537
5317
  }
5318
+ /** @internal */
5319
+ _submitted = computed(() => this._submittedReactive(), ...(ngDevMode ? [{ debugName: "_submitted" }] : []));
5320
+ _submittedReactive = signal(false, ...(ngDevMode ? [{ debugName: "_submittedReactive" }] : []));
5538
5321
  /**
5539
- * A lifecycle method called before the directive's instance is destroyed. For internal use only.
5540
- * @docs-private
5322
+ * Reference to an old form group input value, which is needed to cleanup
5323
+ * old instance in case it was replaced with a new one.
5541
5324
  */
5542
- ngOnDestroy() {
5543
- this.formDirective?.removeFormArray(this);
5544
- }
5325
+ _oldForm;
5545
5326
  /**
5546
- * @description
5547
- * The `FormArray` bound to this directive.
5327
+ * Callback that should be invoked when controls in FormGroup or FormArray collection change
5328
+ * (added or removed). This callback triggers corresponding DOM updates.
5548
5329
  */
5549
- get control() {
5550
- return this.formDirective.getFormArray(this);
5551
- }
5330
+ _onCollectionChange = () => this._updateDomValue();
5552
5331
  /**
5553
5332
  * @description
5554
- * The top-level directive for this group if present, otherwise null.
5333
+ * Tracks the list of added `FormControlName` instances
5555
5334
  */
5556
- get formDirective() {
5557
- return this._parent ? this._parent.formDirective : null;
5335
+ directives = [];
5336
+ constructor(validators, asyncValidators, callSetDisabledState) {
5337
+ super();
5338
+ this.callSetDisabledState = callSetDisabledState;
5339
+ this._setValidators(validators);
5340
+ this._setAsyncValidators(asyncValidators);
5558
5341
  }
5559
- /**
5342
+ /** @nodoc */
5343
+ ngOnChanges(changes) {
5344
+ this.onChanges(changes);
5345
+ }
5346
+ /** @nodoc */
5347
+ ngOnDestroy() {
5348
+ this.onDestroy();
5349
+ }
5350
+ /** @nodoc */
5351
+ onChanges(changes) {
5352
+ this._checkFormPresent();
5353
+ if (changes.hasOwnProperty('form')) {
5354
+ this._updateValidators();
5355
+ this._updateDomValue();
5356
+ this._updateRegistrations();
5357
+ this._oldForm = this.form;
5358
+ }
5359
+ }
5360
+ /** @nodoc */
5361
+ onDestroy() {
5362
+ if (this.form) {
5363
+ cleanUpValidators(this.form, this);
5364
+ // Currently the `onCollectionChange` callback is rewritten each time the
5365
+ // `_registerOnCollectionChange` function is invoked. The implication is that cleanup should
5366
+ // happen *only* when the `onCollectionChange` callback was set by this directive instance.
5367
+ // Otherwise it might cause overriding a callback of some other directive instances. We should
5368
+ // consider updating this logic later to make it similar to how `onChange` callbacks are
5369
+ // handled, see https://github.com/angular/angular/issues/39732 for additional info.
5370
+ if (this.form._onCollectionChange === this._onCollectionChange) {
5371
+ this.form._registerOnCollectionChange(() => { });
5372
+ }
5373
+ }
5374
+ }
5375
+ /**
5560
5376
  * @description
5561
- * Returns an array that represents the path from the top-level form to this control.
5562
- * Each index is the string name of the control on that level.
5377
+ * Returns this directive's instance.
5563
5378
  */
5564
- get path() {
5565
- return controlPath(this.name == null ? this.name : this.name.toString(), this._parent);
5379
+ get formDirective() {
5380
+ return this;
5566
5381
  }
5567
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormArrayName, deps: [{ token: ControlContainer, host: true, optional: true, skipSelf: true }, { token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
5568
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormArrayName, isStandalone: false, selector: "[formArrayName]", inputs: { name: ["formArrayName", "name"] }, providers: [formArrayNameProvider], usesInheritance: true, ngImport: i0 });
5569
- }
5570
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormArrayName, decorators: [{
5571
- type: Directive,
5572
- args: [{
5573
- selector: '[formArrayName]',
5574
- providers: [formArrayNameProvider],
5575
- standalone: false,
5576
- }]
5577
- }], ctorParameters: () => [{ type: ControlContainer, decorators: [{
5578
- type: Optional
5579
- }, {
5580
- type: Host
5581
- }, {
5582
- type: SkipSelf
5583
- }] }, { type: undefined, decorators: [{
5584
- type: Optional
5585
- }, {
5586
- type: Self
5587
- }, {
5588
- type: Inject,
5589
- args: [NG_VALIDATORS]
5590
- }] }, { type: undefined, decorators: [{
5591
- type: Optional
5592
- }, {
5593
- type: Self
5594
- }, {
5595
- type: Inject,
5596
- args: [NG_ASYNC_VALIDATORS]
5597
- }] }], propDecorators: { name: [{
5598
- type: Input,
5599
- args: ['formArrayName']
5600
- }] } });
5601
- function hasInvalidParent(parent) {
5602
- return (!(parent instanceof FormGroupName) &&
5603
- !(parent instanceof FormGroupDirective) &&
5604
- !(parent instanceof FormArrayName));
5605
- }
5606
-
5607
- const controlNameBinding = {
5608
- provide: NgControl,
5609
- useExisting: forwardRef(() => FormControlName),
5610
- };
5611
- /**
5612
- * @description
5613
- * Syncs a `FormControl` in an existing `FormGroup` to a form control
5614
- * element by name.
5615
- *
5616
- * @see [Reactive Forms Guide](guide/forms/reactive-forms)
5617
- * @see {@link FormControl}
5618
- * @see {@link AbstractControl}
5619
- *
5620
- * @usageNotes
5621
- *
5622
- * ### Register `FormControl` within a group
5623
- *
5624
- * The following example shows how to register multiple form controls within a form group
5625
- * and set their value.
5626
- *
5627
- * {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
5628
- *
5629
- * To see `formControlName` examples with different form control types, see:
5630
- *
5631
- * * Radio buttons: `RadioControlValueAccessor`
5632
- * * Selects: `SelectControlValueAccessor`
5633
- *
5634
- * ### Use with ngModel is deprecated
5635
- *
5636
- * Support for using the `ngModel` input property and `ngModelChange` event with reactive
5637
- * form directives has been deprecated in Angular v6 and is scheduled for removal in
5638
- * a future version of Angular.
5639
- *
5640
- * @ngModule ReactiveFormsModule
5641
- * @publicApi
5642
- */
5643
- class FormControlName extends NgControl {
5644
- _ngModelWarningConfig;
5645
- _added = false;
5646
5382
  /**
5647
- * Internal reference to the view model value.
5648
- * @internal
5383
+ * @description
5384
+ * Returns an array representing the path to this group. Because this directive
5385
+ * always lives at the top level of a form, it always an empty array.
5649
5386
  */
5650
- viewModel;
5387
+ get path() {
5388
+ return [];
5389
+ }
5651
5390
  /**
5652
5391
  * @description
5653
- * Tracks the `FormControl` instance bound to the directive.
5392
+ * Method that sets up the control directive in this group, re-calculates its value
5393
+ * and validity, and adds the instance to the internal list of directives.
5394
+ *
5395
+ * @param dir The `FormControlName` directive instance.
5654
5396
  */
5655
- control;
5397
+ addControl(dir) {
5398
+ const ctrl = this.form.get(dir.path);
5399
+ setUpControl(ctrl, dir, this.callSetDisabledState);
5400
+ ctrl.updateValueAndValidity({ emitEvent: false });
5401
+ this.directives.push(dir);
5402
+ return ctrl;
5403
+ }
5656
5404
  /**
5657
5405
  * @description
5658
- * Tracks the name of the `FormControl` bound to the directive. The name corresponds
5659
- * to a key in the parent `FormGroup` or `FormArray`.
5660
- * Accepts a name as a string or a number.
5661
- * The name in the form of a string is useful for individual forms,
5662
- * while the numerical form allows for form controls to be bound
5663
- * to indices when iterating over controls in a `FormArray`.
5406
+ * Retrieves the `FormControl` instance from the provided `FormControlName` directive
5407
+ *
5408
+ * @param dir The `FormControlName` directive instance.
5664
5409
  */
5665
- name = null;
5410
+ getControl(dir) {
5411
+ return this.form.get(dir.path);
5412
+ }
5666
5413
  /**
5667
5414
  * @description
5668
- * Triggers a warning in dev mode that this input should not be used with reactive forms.
5415
+ * Removes the `FormControlName` instance from the internal list of directives
5416
+ *
5417
+ * @param dir The `FormControlName` directive instance.
5669
5418
  */
5670
- set isDisabled(isDisabled) {
5671
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
5672
- console.warn(disabledAttrWarning);
5673
- }
5419
+ removeControl(dir) {
5420
+ cleanUpControl(dir.control || null, dir, /* validateControlPresenceOnChange */ false);
5421
+ removeListItem$1(this.directives, dir);
5422
+ }
5423
+ /**
5424
+ * Adds a new `FormGroupName` directive instance to the form.
5425
+ *
5426
+ * @param dir The `FormGroupName` directive instance.
5427
+ */
5428
+ addFormGroup(dir) {
5429
+ this._setUpFormContainer(dir);
5430
+ }
5431
+ /**
5432
+ * Performs the necessary cleanup when a `FormGroupName` directive instance is removed from the
5433
+ * view.
5434
+ *
5435
+ * @param dir The `FormGroupName` directive instance.
5436
+ */
5437
+ removeFormGroup(dir) {
5438
+ this._cleanUpFormContainer(dir);
5674
5439
  }
5675
- // TODO(kara): remove next 4 properties once deprecation period is over
5676
- /** @deprecated as of v6 */
5677
- model;
5678
- /** @deprecated as of v6 */
5679
- update = new EventEmitter();
5680
5440
  /**
5681
5441
  * @description
5682
- * Static property used to track whether any ngModel warnings have been sent across
5683
- * all instances of FormControlName. Used to support warning config of "once".
5442
+ * Retrieves the `FormGroup` for a provided `FormGroupName` directive instance
5684
5443
  *
5685
- * @internal
5444
+ * @param dir The `FormGroupName` directive instance.
5686
5445
  */
5687
- static _ngModelWarningSentOnce = false;
5446
+ getFormGroup(dir) {
5447
+ return this.form.get(dir.path);
5448
+ }
5688
5449
  /**
5689
5450
  * @description
5690
- * Instance property used to track whether an ngModel warning has been sent out for this
5691
- * particular FormControlName instance. Used to support warning config of "always".
5451
+ * Retrieves the `FormArray` for a provided `FormArrayName` directive instance.
5692
5452
  *
5693
- * @internal
5453
+ * @param dir The `FormArrayName` directive instance.
5694
5454
  */
5695
- _ngModelWarningSent = false;
5696
- constructor(parent, validators, asyncValidators, valueAccessors, _ngModelWarningConfig) {
5697
- super();
5698
- this._ngModelWarningConfig = _ngModelWarningConfig;
5699
- this._parent = parent;
5700
- this._setValidators(validators);
5701
- this._setAsyncValidators(asyncValidators);
5702
- this.valueAccessor = selectValueAccessor(this, valueAccessors);
5455
+ getFormArray(dir) {
5456
+ return this.form.get(dir.path);
5703
5457
  }
5704
- /** @docs-private */
5705
- ngOnChanges(changes) {
5706
- if (!this._added)
5707
- this._setUpControl();
5708
- if (isPropertyUpdated(changes, this.viewModel)) {
5709
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
5710
- _ngModelWarning('formControlName', FormControlName, this, this._ngModelWarningConfig);
5711
- }
5712
- this.viewModel = this.model;
5713
- this.formDirective.updateModel(this, this.model);
5714
- }
5458
+ /**
5459
+ * Performs the necessary setup when a `FormArrayName` directive instance is added to the view.
5460
+ *
5461
+ * @param dir The `FormArrayName` directive instance.
5462
+ */
5463
+ addFormArray(dir) {
5464
+ this._setUpFormContainer(dir);
5715
5465
  }
5716
- /** @docs-private */
5717
- ngOnDestroy() {
5718
- if (this.formDirective) {
5719
- this.formDirective.removeControl(this);
5720
- }
5466
+ /**
5467
+ * Performs the necessary cleanup when a `FormArrayName` directive instance is removed from the
5468
+ * view.
5469
+ *
5470
+ * @param dir The `FormArrayName` directive instance.
5471
+ */
5472
+ removeFormArray(dir) {
5473
+ this._cleanUpFormContainer(dir);
5721
5474
  }
5722
5475
  /**
5723
- * @description
5724
- * Sets the new value for the view model and emits an `ngModelChange` event.
5476
+ * Sets the new value for the provided `FormControlName` directive.
5725
5477
  *
5726
- * @param newValue The new value for the view model.
5478
+ * @param dir The `FormControlName` directive instance.
5479
+ * @param value The new value for the directive's control.
5727
5480
  */
5728
- viewToModelUpdate(newValue) {
5729
- this.viewModel = newValue;
5730
- this.update.emit(newValue);
5481
+ updateModel(dir, value) {
5482
+ const ctrl = this.form.get(dir.path);
5483
+ ctrl.setValue(value);
5731
5484
  }
5732
5485
  /**
5733
5486
  * @description
5734
- * Returns an array that represents the path from the top-level form to this control.
5735
- * Each index is the string name of the control on that level.
5487
+ * Method called when the "reset" event is triggered on the form.
5736
5488
  */
5737
- get path() {
5738
- return controlPath(this.name == null ? this.name : this.name.toString(), this._parent);
5489
+ onReset() {
5490
+ this.resetForm();
5739
5491
  }
5740
5492
  /**
5741
5493
  * @description
5742
- * The top-level directive for this group if present, otherwise null.
5494
+ * Resets the form to an initial value and resets its submitted status.
5495
+ *
5496
+ * @param value The new value for the form.
5743
5497
  */
5744
- get formDirective() {
5745
- return this._parent ? this._parent.formDirective : null;
5498
+ resetForm(value = undefined, options = {}) {
5499
+ this.form.reset(value, options);
5500
+ this._submittedReactive.set(false);
5501
+ if (options?.emitEvent !== false) {
5502
+ this.form._events.next(new FormResetEvent(this.form));
5503
+ }
5746
5504
  }
5747
- _setUpControl() {
5748
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
5749
- checkParentType(this._parent, this.name);
5505
+ /**
5506
+ * @description
5507
+ * Method called with the "submit" event is triggered on the form.
5508
+ * Triggers the `ngSubmit` emitter to emit the "submit" event as its payload.
5509
+ *
5510
+ * @param $event The "submit" event object
5511
+ */
5512
+ onSubmit($event) {
5513
+ this.submitted = true;
5514
+ syncPendingControls(this.form, this.directives);
5515
+ this.ngSubmit.emit($event);
5516
+ this.form._events.next(new FormSubmittedEvent(this.control));
5517
+ // Forms with `method="dialog"` have some special behavior that won't reload the page and that
5518
+ // shouldn't be prevented. Note that we need to null check the `event` and the `target`, because
5519
+ // some internal apps call this method directly with the wrong arguments.
5520
+ return $event?.target?.method === 'dialog';
5521
+ }
5522
+ /** @internal */
5523
+ _updateDomValue() {
5524
+ this.directives.forEach((dir) => {
5525
+ const oldCtrl = dir.control;
5526
+ const newCtrl = this.form.get(dir.path);
5527
+ if (oldCtrl !== newCtrl) {
5528
+ // Note: the value of the `dir.control` may not be defined, for example when it's a first
5529
+ // `FormControl` that is added to a `FormGroup`/`FormArray` instance (via `addControl` call).
5530
+ cleanUpControl(oldCtrl || null, dir);
5531
+ // Check whether new control at the same location inside the corresponding `FormGroup`/`FormArray` is an
5532
+ // instance of `FormControl` and perform control setup only if that's the case.
5533
+ // Note: we don't need to clear the list of directives (`this.directives`) here, it would be
5534
+ // taken care of in the `removeControl` method invoked when corresponding `formControlName`
5535
+ // directive instance is being removed (invoked from `FormControlName.ngOnDestroy`).
5536
+ if (isFormControl(newCtrl)) {
5537
+ setUpControl(newCtrl, dir, this.callSetDisabledState);
5538
+ dir.control = newCtrl;
5539
+ }
5540
+ }
5541
+ });
5542
+ this.form._updateTreeValidity({ emitEvent: false });
5543
+ }
5544
+ _setUpFormContainer(dir) {
5545
+ const ctrl = this.form.get(dir.path);
5546
+ setUpFormContainer(ctrl, dir);
5547
+ // NOTE: this operation looks unnecessary in case no new validators were added in
5548
+ // `setUpFormContainer` call. Consider updating this code to match the logic in
5549
+ // `_cleanUpFormContainer` function.
5550
+ ctrl.updateValueAndValidity({ emitEvent: false });
5551
+ }
5552
+ _cleanUpFormContainer(dir) {
5553
+ if (this.form) {
5554
+ const ctrl = this.form.get(dir.path);
5555
+ if (ctrl) {
5556
+ const isControlUpdated = cleanUpFormContainer(ctrl, dir);
5557
+ if (isControlUpdated) {
5558
+ // Run validity check only in case a control was updated (i.e. view validators were
5559
+ // removed) as removing view validators might cause validity to change.
5560
+ ctrl.updateValueAndValidity({ emitEvent: false });
5561
+ }
5562
+ }
5750
5563
  }
5751
- this.control = this.formDirective.addControl(this);
5752
- this._added = true;
5753
5564
  }
5754
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormControlName, deps: [{ token: ControlContainer, host: true, optional: true, skipSelf: true }, { token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }, { token: NG_VALUE_ACCESSOR, optional: true, self: true }, { token: NG_MODEL_WITH_FORM_CONTROL_WARNING, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
5755
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormControlName, isStandalone: false, selector: "[formControlName]", inputs: { name: ["formControlName", "name"], isDisabled: ["disabled", "isDisabled"], model: ["ngModel", "model"] }, outputs: { update: "ngModelChange" }, providers: [controlNameBinding], usesInheritance: true, usesOnChanges: true, ngImport: i0 });
5565
+ _updateRegistrations() {
5566
+ this.form._registerOnCollectionChange(this._onCollectionChange);
5567
+ if (this._oldForm) {
5568
+ this._oldForm._registerOnCollectionChange(() => { });
5569
+ }
5570
+ }
5571
+ _updateValidators() {
5572
+ setUpValidators(this.form, this);
5573
+ if (this._oldForm) {
5574
+ cleanUpValidators(this._oldForm, this);
5575
+ }
5576
+ }
5577
+ _checkFormPresent() {
5578
+ if (!this.form && (typeof ngDevMode === 'undefined' || ngDevMode)) {
5579
+ throw missingFormException();
5580
+ }
5581
+ }
5582
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: AbstractFormDirective, deps: [{ token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }, { token: CALL_SET_DISABLED_STATE, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
5583
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: AbstractFormDirective, isStandalone: true, usesInheritance: true, usesOnChanges: true, ngImport: i0 });
5756
5584
  }
5757
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormControlName, decorators: [{
5758
- type: Directive,
5759
- args: [{
5760
- selector: '[formControlName]',
5761
- providers: [controlNameBinding],
5762
- standalone: false,
5763
- }]
5764
- }], ctorParameters: () => [{ type: ControlContainer, decorators: [{
5765
- type: Optional
5766
- }, {
5767
- type: Host
5768
- }, {
5769
- type: SkipSelf
5770
- }] }, { type: undefined, decorators: [{
5585
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: AbstractFormDirective, decorators: [{
5586
+ type: Directive
5587
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
5771
5588
  type: Optional
5772
5589
  }, {
5773
5590
  type: Self
@@ -5783,1623 +5600,1897 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2",
5783
5600
  args: [NG_ASYNC_VALIDATORS]
5784
5601
  }] }, { type: undefined, decorators: [{
5785
5602
  type: Optional
5786
- }, {
5787
- type: Self
5788
- }, {
5789
- type: Inject,
5790
- args: [NG_VALUE_ACCESSOR]
5791
- }] }, { type: undefined, decorators: [{
5792
- type: Optional
5793
5603
  }, {
5794
5604
  type: Inject,
5795
- args: [NG_MODEL_WITH_FORM_CONTROL_WARNING]
5796
- }] }], propDecorators: { name: [{
5797
- type: Input,
5798
- args: ['formControlName']
5799
- }], isDisabled: [{
5800
- type: Input,
5801
- args: ['disabled']
5802
- }], model: [{
5803
- type: Input,
5804
- args: ['ngModel']
5805
- }], update: [{
5806
- type: Output,
5807
- args: ['ngModelChange']
5808
- }] } });
5809
- function checkParentType(parent, name) {
5810
- if (!(parent instanceof FormGroupName) && parent instanceof AbstractFormGroupDirective) {
5811
- throw ngModelGroupException();
5812
- }
5813
- else if (!(parent instanceof FormGroupName) &&
5814
- !(parent instanceof FormGroupDirective) &&
5815
- !(parent instanceof FormArrayName)) {
5816
- throw controlParentException(name);
5817
- }
5818
- }
5605
+ args: [CALL_SET_DISABLED_STATE]
5606
+ }] }] });
5819
5607
 
5820
- const SELECT_VALUE_ACCESSOR = {
5821
- provide: NG_VALUE_ACCESSOR,
5822
- useExisting: forwardRef(() => SelectControlValueAccessor),
5823
- multi: true,
5608
+ const formDirectiveProvider$1 = {
5609
+ provide: ControlContainer,
5610
+ useExisting: forwardRef(() => FormArrayDirective),
5824
5611
  };
5825
- function _buildValueString$1(id, value) {
5826
- if (id == null)
5827
- return `${value}`;
5828
- if (value && typeof value === 'object')
5829
- value = 'Object';
5830
- return `${id}: ${value}`.slice(0, 50);
5831
- }
5832
- function _extractId$1(valueString) {
5833
- return valueString.split(':')[0];
5834
- }
5835
5612
  /**
5836
5613
  * @description
5837
- * The `ControlValueAccessor` for writing select control values and listening to select control
5838
- * changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and
5839
- * `NgModel` directives.
5840
5614
  *
5841
- * @usageNotes
5842
- *
5843
- * ### Using select controls in a reactive form
5844
- *
5845
- * The following examples show how to use a select control in a reactive form.
5846
- *
5847
- * {@example forms/ts/reactiveSelectControl/reactive_select_control_example.ts region='Component'}
5615
+ * Binds an existing `FormArray` to a DOM element.
5848
5616
  *
5849
- * ### Using select controls in a template-driven form
5617
+ * This directive accepts an existing `FormArray` instance. It will then use this
5618
+ * `FormArray` instance to match any child `FormControl`, `FormGroup`/`FormRecord`,
5619
+ * and `FormArray` instances to child `FormControlName`, `FormGroupName`,
5620
+ * and `FormArrayName` directives.
5850
5621
  *
5851
- * To use a select in a template-driven form, simply add an `ngModel` and a `name`
5852
- * attribute to the main `<select>` tag.
5622
+ * @see [Reactive Forms Guide](guide/reactive-forms)
5623
+ * @see {@link AbstractControl}
5853
5624
  *
5854
- * {@example forms/ts/selectControl/select_control_example.ts region='Component'}
5625
+ * @usageNotes
5626
+ * ### Register Form Array
5855
5627
  *
5856
- * ### Customizing option selection
5628
+ * The following example registers a `FormArray` with first name and last name controls,
5629
+ * and listens for the *ngSubmit* event when the button is clicked.
5857
5630
  *
5858
- * Angular uses object identity to select option. It's possible for the identities of items
5859
- * to change while the data does not. This can happen, for example, if the items are produced
5860
- * from an RPC to the server, and that RPC is re-run. Even if the data hasn't changed, the
5861
- * second response will produce objects with different identities.
5631
+ * @ngModule ReactiveFormsModule
5632
+ * @publicApi
5633
+ */
5634
+ class FormArrayDirective extends AbstractFormDirective {
5635
+ /**
5636
+ * @description
5637
+ * Tracks the `FormArray` bound to this directive.
5638
+ */
5639
+ form = null;
5640
+ /**
5641
+ * @description
5642
+ * Emits an event when the form submission has been triggered.
5643
+ */
5644
+ ngSubmit = new EventEmitter();
5645
+ /**
5646
+ * @description
5647
+ * Returns the `FormArray` bound to this directive.
5648
+ */
5649
+ get control() {
5650
+ return this.form;
5651
+ }
5652
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormArrayDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
5653
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormArrayDirective, isStandalone: false, selector: "[formArray]", inputs: { form: ["formArray", "form"] }, outputs: { ngSubmit: "ngSubmit" }, host: { listeners: { "submit": "onSubmit($event)", "reset": "onReset()" } }, providers: [formDirectiveProvider$1], exportAs: ["ngForm"], usesInheritance: true, ngImport: i0 });
5654
+ }
5655
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormArrayDirective, decorators: [{
5656
+ type: Directive,
5657
+ args: [{
5658
+ selector: '[formArray]',
5659
+ providers: [formDirectiveProvider$1],
5660
+ host: { '(submit)': 'onSubmit($event)', '(reset)': 'onReset()' },
5661
+ exportAs: 'ngForm',
5662
+ standalone: false,
5663
+ }]
5664
+ }], propDecorators: { form: [{
5665
+ type: Input,
5666
+ args: ['formArray']
5667
+ }], ngSubmit: [{
5668
+ type: Output
5669
+ }] } });
5670
+
5671
+ /**
5672
+ * Token to provide to turn off the ngModel warning on formControl and formControlName.
5673
+ */
5674
+ const NG_MODEL_WITH_FORM_CONTROL_WARNING = new InjectionToken(ngDevMode ? 'NgModelWithFormControlWarning' : '');
5675
+ const formControlBinding = {
5676
+ provide: NgControl,
5677
+ useExisting: forwardRef(() => FormControlDirective),
5678
+ };
5679
+ /**
5680
+ * @description
5681
+ * Synchronizes a standalone `FormControl` instance to a form control element.
5862
5682
  *
5863
- * To customize the default option comparison algorithm, `<select>` supports `compareWith` input.
5864
- * `compareWith` takes a **function** which has two arguments: `option1` and `option2`.
5865
- * If `compareWith` is given, Angular selects option by the return value of the function.
5683
+ * Note that support for using the `ngModel` input property and `ngModelChange` event with reactive
5684
+ * form directives was deprecated in Angular v6 and is scheduled for removal in
5685
+ * a future version of Angular.
5866
5686
  *
5867
- * ```ts
5868
- * const selectedCountriesControl = new FormControl();
5869
- * ```
5687
+ * @see [Reactive Forms Guide](guide/forms/reactive-forms)
5688
+ * @see {@link FormControl}
5689
+ * @see {@link AbstractControl}
5870
5690
  *
5871
- * ```html
5872
- * <select [compareWith]="compareFn" [formControl]="selectedCountriesControl">
5873
- * @for(country of countries; track $index) {
5874
- * <option[ngValue]="country">{{country.name}}</option>
5875
- * }
5876
- * </select>
5691
+ * @usageNotes
5877
5692
  *
5878
- * compareFn(c1: Country, c2: Country): boolean {
5879
- * return c1 && c2 ? c1.id === c2.id : c1 === c2;
5880
- * }
5881
- * ```
5693
+ * The following example shows how to register a standalone control and set its value.
5882
5694
  *
5883
- * **Note:** We listen to the 'change' event because 'input' events aren't fired
5884
- * for selects in IE, see:
5885
- * https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event#browser_compatibility
5695
+ * {@example forms/ts/simpleFormControl/simple_form_control_example.ts region='Component'}
5886
5696
  *
5887
5697
  * @ngModule ReactiveFormsModule
5888
- * @ngModule FormsModule
5889
5698
  * @publicApi
5890
5699
  */
5891
- class SelectControlValueAccessor extends BuiltInControlValueAccessor {
5892
- /** @docs-private */
5893
- value;
5894
- /** @internal */
5895
- _optionMap = new Map();
5896
- /** @internal */
5897
- _idCounter = 0;
5700
+ class FormControlDirective extends NgControl {
5701
+ _ngModelWarningConfig;
5702
+ callSetDisabledState;
5898
5703
  /**
5899
- * @description
5900
- * Tracks the option comparison algorithm for tracking identities when
5901
- * checking for changes.
5704
+ * Internal reference to the view model value.
5705
+ * @docs-private
5902
5706
  */
5903
- set compareWith(fn) {
5904
- if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
5905
- throw new _RuntimeError(1201 /* RuntimeErrorCode.COMPAREWITH_NOT_A_FN */, `compareWith must be a function, but received ${JSON.stringify(fn)}`);
5906
- }
5907
- this._compareWith = fn;
5908
- }
5909
- _compareWith = Object.is;
5910
- // We need this because we might be in the process of destroying the root
5911
- // injector, which is marked as destroyed before running destroy hooks.
5912
- // Attempting to use afterNextRender with the node injector would evntually
5913
- // run into that already destroyed injector.
5914
- appRefInjector = inject(ApplicationRef).injector;
5915
- destroyRef = inject(DestroyRef);
5916
- cdr = inject(ChangeDetectorRef);
5917
- _queuedWrite = false;
5707
+ viewModel;
5918
5708
  /**
5919
- * This is needed to efficiently set the select value when adding/removing options. If
5920
- * writeValue is instead called for every added/removed option, this results in exponentially
5921
- * more _compareValue calls than the number of option elements (issue #41330).
5922
- *
5923
- * Secondly, calling writeValue when rendering individual option elements instead of after they
5924
- * are all rendered caused an issue in Safari and IE 11 where the first option element failed
5925
- * to be deselected when no option matched the select ngModel. This was because Angular would
5926
- * set the select element's value property before appending the option's child text node to the
5927
- * DOM (issue #14505).
5928
- *
5929
- * Finally, this approach is necessary to avoid an issue with delayed element removal when
5930
- * using the animations module (in all browsers). Otherwise when a selected option is removed
5931
- * (so no option matches the ngModel anymore), Angular would change the select element value
5932
- * before actually removing the option from the DOM. Then when the option is finally removed
5933
- * from the DOM, the browser would change the select value to that of the first option, even
5934
- * though it doesn't match the ngModel (issue #18430).
5935
- *
5936
- * @internal
5709
+ * @description
5710
+ * Tracks the `FormControl` instance bound to the directive.
5937
5711
  */
5938
- _writeValueAfterRender() {
5939
- if (this._queuedWrite || this.appRefInjector.destroyed) {
5940
- return;
5712
+ form;
5713
+ /**
5714
+ * @description
5715
+ * Triggers a warning in dev mode that this input should not be used with reactive forms.
5716
+ */
5717
+ set isDisabled(isDisabled) {
5718
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
5719
+ console.warn(disabledAttrWarning);
5941
5720
  }
5942
- this._queuedWrite = true;
5943
- afterNextRender({
5944
- write: () => {
5945
- if (this.destroyRef.destroyed) {
5946
- return;
5947
- }
5948
- this._queuedWrite = false;
5949
- this.writeValue(this.value);
5950
- },
5951
- }, { injector: this.appRefInjector });
5952
5721
  }
5722
+ // TODO(kara): remove next 4 properties once deprecation period is over
5723
+ /** @deprecated as of v6 */
5724
+ model;
5725
+ /** @deprecated as of v6 */
5726
+ update = new EventEmitter();
5953
5727
  /**
5954
- * Sets the "value" property on the select element.
5955
- * @docs-private
5728
+ * @description
5729
+ * Static property used to track whether any ngModel warnings have been sent across
5730
+ * all instances of FormControlDirective. Used to support warning config of "once".
5731
+ *
5732
+ * @internal
5956
5733
  */
5957
- writeValue(value) {
5958
- // TODO(atscott): This could likely be optimized more by only marking for check if the value is changed
5959
- // note that this needs to include both the internal value and the value in the DOM.
5960
- this.cdr.markForCheck();
5961
- this.value = value;
5962
- const id = this._getOptionId(value);
5963
- const valueString = _buildValueString$1(id, value);
5964
- this.setProperty('value', valueString);
5965
- }
5734
+ static _ngModelWarningSentOnce = false;
5966
5735
  /**
5967
- * Registers a function called when the control value changes.
5968
- * @docs-private
5736
+ * @description
5737
+ * Instance property used to track whether an ngModel warning has been sent out for this
5738
+ * particular `FormControlDirective` instance. Used to support warning config of "always".
5739
+ *
5740
+ * @internal
5969
5741
  */
5970
- registerOnChange(fn) {
5971
- this.onChange = (valueString) => {
5972
- this.value = this._getOptionValue(valueString);
5973
- fn(this.value);
5974
- };
5975
- }
5976
- /** @internal */
5977
- _registerOption() {
5978
- return (this._idCounter++).toString();
5742
+ _ngModelWarningSent = false;
5743
+ constructor(validators, asyncValidators, valueAccessors, _ngModelWarningConfig, callSetDisabledState) {
5744
+ super();
5745
+ this._ngModelWarningConfig = _ngModelWarningConfig;
5746
+ this.callSetDisabledState = callSetDisabledState;
5747
+ this._setValidators(validators);
5748
+ this._setAsyncValidators(asyncValidators);
5749
+ this.valueAccessor = selectValueAccessor(this, valueAccessors);
5979
5750
  }
5980
- /** @internal */
5981
- _getOptionId(value) {
5982
- for (const id of this._optionMap.keys()) {
5983
- if (this._compareWith(this._optionMap.get(id), value))
5984
- return id;
5751
+ /** @docs-private */
5752
+ ngOnChanges(changes) {
5753
+ if (this._isControlChanged(changes)) {
5754
+ const previousForm = changes['form'].previousValue;
5755
+ if (previousForm) {
5756
+ cleanUpControl(previousForm, this, /* validateControlPresenceOnChange */ false);
5757
+ }
5758
+ setUpControl(this.form, this, this.callSetDisabledState);
5759
+ this.form.updateValueAndValidity({ emitEvent: false });
5760
+ }
5761
+ if (isPropertyUpdated(changes, this.viewModel)) {
5762
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
5763
+ _ngModelWarning('formControl', FormControlDirective, this, this._ngModelWarningConfig);
5764
+ }
5765
+ this.form.setValue(this.model);
5766
+ this.viewModel = this.model;
5985
5767
  }
5986
- return null;
5987
5768
  }
5988
- /** @internal */
5989
- _getOptionValue(valueString) {
5990
- const id = _extractId$1(valueString);
5991
- return this._optionMap.has(id) ? this._optionMap.get(id) : valueString;
5769
+ /** @docs-private */
5770
+ ngOnDestroy() {
5771
+ if (this.form) {
5772
+ cleanUpControl(this.form, this, /* validateControlPresenceOnChange */ false);
5773
+ }
5992
5774
  }
5993
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SelectControlValueAccessor, deps: null, target: i0.ɵɵFactoryTarget.Directive });
5994
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: SelectControlValueAccessor, isStandalone: false, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: { compareWith: "compareWith" }, host: { listeners: { "change": "onChange($any($event.target).value)", "blur": "onTouched()" } }, providers: [SELECT_VALUE_ACCESSOR], usesInheritance: true, ngImport: i0 });
5995
- }
5996
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SelectControlValueAccessor, decorators: [{
5997
- type: Directive,
5998
- args: [{
5999
- selector: 'select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]',
6000
- host: { '(change)': 'onChange($any($event.target).value)', '(blur)': 'onTouched()' },
6001
- providers: [SELECT_VALUE_ACCESSOR],
6002
- standalone: false,
6003
- }]
6004
- }], propDecorators: { compareWith: [{
6005
- type: Input
6006
- }] } });
6007
- /**
6008
- * @description
6009
- * Marks `<option>` as dynamic, so Angular can be notified when options change.
6010
- *
6011
- * @see {@link SelectControlValueAccessor}
6012
- *
6013
- * @ngModule ReactiveFormsModule
6014
- * @ngModule FormsModule
6015
- * @publicApi
6016
- */
6017
- class NgSelectOption {
6018
- _element;
6019
- _renderer;
6020
- _select;
6021
5775
  /**
6022
5776
  * @description
6023
- * ID of the option element
5777
+ * Returns an array that represents the path from the top-level form to this control.
5778
+ * Each index is the string name of the control on that level.
6024
5779
  */
6025
- id;
6026
- constructor(_element, _renderer, _select) {
6027
- this._element = _element;
6028
- this._renderer = _renderer;
6029
- this._select = _select;
6030
- if (this._select)
6031
- this.id = this._select._registerOption();
5780
+ get path() {
5781
+ return [];
6032
5782
  }
6033
5783
  /**
6034
5784
  * @description
6035
- * Tracks the value bound to the option element. Unlike the value binding,
6036
- * ngValue supports binding to objects.
5785
+ * The `FormControl` bound to this directive.
6037
5786
  */
6038
- set ngValue(value) {
6039
- if (this._select == null)
6040
- return;
6041
- this._select._optionMap.set(this.id, value);
6042
- this._setElementValue(_buildValueString$1(this.id, value));
6043
- this._select._writeValueAfterRender();
5787
+ get control() {
5788
+ return this.form;
6044
5789
  }
6045
5790
  /**
6046
5791
  * @description
6047
- * Tracks simple string values bound to the option element.
6048
- * For objects, use the `ngValue` input binding.
5792
+ * Sets the new value for the view model and emits an `ngModelChange` event.
5793
+ *
5794
+ * @param newValue The new value for the view model.
6049
5795
  */
6050
- set value(value) {
6051
- this._setElementValue(value);
6052
- if (this._select)
6053
- this._select._writeValueAfterRender();
6054
- }
6055
- /** @internal */
6056
- _setElementValue(value) {
6057
- this._renderer.setProperty(this._element.nativeElement, 'value', value);
5796
+ viewToModelUpdate(newValue) {
5797
+ this.viewModel = newValue;
5798
+ this.update.emit(newValue);
6058
5799
  }
6059
- /** @docs-private */
6060
- ngOnDestroy() {
6061
- if (this._select) {
6062
- this._select._optionMap.delete(this.id);
6063
- this._select._writeValueAfterRender();
6064
- }
5800
+ _isControlChanged(changes) {
5801
+ return changes.hasOwnProperty('form');
6065
5802
  }
6066
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: NgSelectOption, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: SelectControlValueAccessor, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
6067
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: NgSelectOption, isStandalone: false, selector: "option", inputs: { ngValue: "ngValue", value: "value" }, ngImport: i0 });
5803
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormControlDirective, deps: [{ token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }, { token: NG_VALUE_ACCESSOR, optional: true, self: true }, { token: NG_MODEL_WITH_FORM_CONTROL_WARNING, optional: true }, { token: CALL_SET_DISABLED_STATE, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
5804
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormControlDirective, isStandalone: false, selector: "[formControl]", inputs: { form: ["formControl", "form"], isDisabled: ["disabled", "isDisabled"], model: ["ngModel", "model"] }, outputs: { update: "ngModelChange" }, providers: [formControlBinding], exportAs: ["ngForm"], usesInheritance: true, usesOnChanges: true, ngImport: i0 });
6068
5805
  }
6069
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: NgSelectOption, decorators: [{
5806
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormControlDirective, decorators: [{
6070
5807
  type: Directive,
6071
5808
  args: [{
6072
- selector: 'option',
5809
+ selector: '[formControl]',
5810
+ providers: [formControlBinding],
5811
+ exportAs: 'ngForm',
6073
5812
  standalone: false,
6074
5813
  }]
6075
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: SelectControlValueAccessor, decorators: [{
5814
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
6076
5815
  type: Optional
6077
5816
  }, {
6078
- type: Host
6079
- }] }], propDecorators: { ngValue: [{
5817
+ type: Self
5818
+ }, {
5819
+ type: Inject,
5820
+ args: [NG_VALIDATORS]
5821
+ }] }, { type: undefined, decorators: [{
5822
+ type: Optional
5823
+ }, {
5824
+ type: Self
5825
+ }, {
5826
+ type: Inject,
5827
+ args: [NG_ASYNC_VALIDATORS]
5828
+ }] }, { type: undefined, decorators: [{
5829
+ type: Optional
5830
+ }, {
5831
+ type: Self
5832
+ }, {
5833
+ type: Inject,
5834
+ args: [NG_VALUE_ACCESSOR]
5835
+ }] }, { type: undefined, decorators: [{
5836
+ type: Optional
5837
+ }, {
5838
+ type: Inject,
5839
+ args: [NG_MODEL_WITH_FORM_CONTROL_WARNING]
5840
+ }] }, { type: undefined, decorators: [{
5841
+ type: Optional
5842
+ }, {
5843
+ type: Inject,
5844
+ args: [CALL_SET_DISABLED_STATE]
5845
+ }] }], propDecorators: { form: [{
6080
5846
  type: Input,
6081
- args: ['ngValue']
6082
- }], value: [{
5847
+ args: ['formControl']
5848
+ }], isDisabled: [{
6083
5849
  type: Input,
6084
- args: ['value']
5850
+ args: ['disabled']
5851
+ }], model: [{
5852
+ type: Input,
5853
+ args: ['ngModel']
5854
+ }], update: [{
5855
+ type: Output,
5856
+ args: ['ngModelChange']
6085
5857
  }] } });
6086
5858
 
6087
- const SELECT_MULTIPLE_VALUE_ACCESSOR = {
6088
- provide: NG_VALUE_ACCESSOR,
6089
- useExisting: forwardRef(() => SelectMultipleControlValueAccessor),
6090
- multi: true,
5859
+ const formGroupNameProvider = {
5860
+ provide: ControlContainer,
5861
+ useExisting: forwardRef(() => FormGroupName),
6091
5862
  };
6092
- function _buildValueString(id, value) {
6093
- if (id == null)
6094
- return `${value}`;
6095
- if (typeof value === 'string')
6096
- value = `'${value}'`;
6097
- if (value && typeof value === 'object')
6098
- value = 'Object';
6099
- return `${id}: ${value}`.slice(0, 50);
6100
- }
6101
- function _extractId(valueString) {
6102
- return valueString.split(':')[0];
6103
- }
6104
5863
  /**
6105
5864
  * @description
6106
- * The `ControlValueAccessor` for writing multi-select control values and listening to multi-select
6107
- * control changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and
6108
- * `NgModel` directives.
6109
5865
  *
6110
- * @see {@link SelectControlValueAccessor}
5866
+ * Syncs a nested `FormGroup` or `FormRecord` to a DOM element.
5867
+ *
5868
+ * This directive can only be used with a parent `FormGroupDirective`.
5869
+ *
5870
+ * It accepts the string name of the nested `FormGroup` or `FormRecord` to link, and
5871
+ * looks for a `FormGroup` or `FormRecord` registered with that name in the parent
5872
+ * `FormGroup` instance you passed into `FormGroupDirective`.
5873
+ *
5874
+ * Use nested form groups to validate a sub-group of a
5875
+ * form separately from the rest or to group the values of certain
5876
+ * controls into their own nested object.
5877
+ *
5878
+ * @see [Reactive Forms Guide](guide/forms/reactive-forms)
6111
5879
  *
6112
5880
  * @usageNotes
6113
5881
  *
6114
- * ### Using a multi-select control
5882
+ * ### Access the group by name
6115
5883
  *
6116
- * The follow example shows you how to use a multi-select control with a reactive form.
5884
+ * The following example uses the `AbstractControl.get` method to access the
5885
+ * associated `FormGroup`
6117
5886
  *
6118
5887
  * ```ts
6119
- * const countryControl = new FormControl();
5888
+ * this.form.get('name');
6120
5889
  * ```
6121
5890
  *
6122
- * ```html
6123
- * <select multiple name="countries" [formControl]="countryControl">
6124
- * @for(country of countries; track $index) {
6125
- * <option [ngValue]="country">{{ country.name }}</option>
6126
- * }
6127
- * </select>
5891
+ * ### Access individual controls in the group
5892
+ *
5893
+ * The following example uses the `AbstractControl.get` method to access
5894
+ * individual controls within the group using dot syntax.
5895
+ *
5896
+ * ```ts
5897
+ * this.form.get('name.first');
6128
5898
  * ```
6129
5899
  *
6130
- * ### Customizing option selection
5900
+ * ### Register a nested `FormGroup`.
6131
5901
  *
6132
- * To customize the default option comparison algorithm, `<select>` supports `compareWith` input.
6133
- * See the `SelectControlValueAccessor` for usage.
5902
+ * The following example registers a nested *name* `FormGroup` within an existing `FormGroup`,
5903
+ * and provides methods to retrieve the nested `FormGroup` and individual controls.
5904
+ *
5905
+ * {@example forms/ts/nestedFormGroup/nested_form_group_example.ts region='Component'}
6134
5906
  *
6135
5907
  * @ngModule ReactiveFormsModule
6136
- * @ngModule FormsModule
6137
5908
  * @publicApi
6138
5909
  */
6139
- class SelectMultipleControlValueAccessor extends BuiltInControlValueAccessor {
6140
- /**
6141
- * The current value.
6142
- * @docs-private
6143
- */
6144
- value;
6145
- /** @internal */
6146
- _optionMap = new Map();
6147
- /** @internal */
6148
- _idCounter = 0;
5910
+ class FormGroupName extends AbstractFormGroupDirective {
6149
5911
  /**
6150
5912
  * @description
6151
- * Tracks the option comparison algorithm for tracking identities when
6152
- * checking for changes.
6153
- */
6154
- set compareWith(fn) {
6155
- if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
6156
- throw new _RuntimeError(1201 /* RuntimeErrorCode.COMPAREWITH_NOT_A_FN */, `compareWith must be a function, but received ${JSON.stringify(fn)}`);
6157
- }
6158
- this._compareWith = fn;
6159
- }
6160
- _compareWith = Object.is;
6161
- /**
6162
- * Sets the "value" property on one or of more of the select's options.
6163
- * @docs-private
6164
- */
6165
- writeValue(value) {
6166
- this.value = value;
6167
- let optionSelectedStateSetter;
6168
- if (Array.isArray(value)) {
6169
- // convert values to ids
6170
- const ids = value.map((v) => this._getOptionId(v));
6171
- optionSelectedStateSetter = (opt, o) => {
6172
- opt._setSelected(ids.indexOf(o.toString()) > -1);
6173
- };
6174
- }
6175
- else {
6176
- optionSelectedStateSetter = (opt, o) => {
6177
- opt._setSelected(false);
6178
- };
6179
- }
6180
- this._optionMap.forEach(optionSelectedStateSetter);
6181
- }
6182
- /**
6183
- * Registers a function called when the control value changes
6184
- * and writes an array of the selected options.
6185
- * @docs-private
5913
+ * Tracks the name of the `FormGroup` bound to the directive. The name corresponds
5914
+ * to a key in the parent `FormGroup` or `FormArray`.
5915
+ * Accepts a name as a string or a number.
5916
+ * The name in the form of a string is useful for individual forms,
5917
+ * while the numerical form allows for form groups to be bound
5918
+ * to indices when iterating over groups in a `FormArray`.
6186
5919
  */
6187
- registerOnChange(fn) {
6188
- this.onChange = (element) => {
6189
- const selected = [];
6190
- const selectedOptions = element.selectedOptions;
6191
- if (selectedOptions !== undefined) {
6192
- const options = selectedOptions;
6193
- for (let i = 0; i < options.length; i++) {
6194
- const opt = options[i];
6195
- const val = this._getOptionValue(opt.value);
6196
- selected.push(val);
6197
- }
6198
- }
6199
- // Degrade to use `options` when `selectedOptions` property is not available.
6200
- // Note: the `selectedOptions` is available in all supported browsers, but the Domino lib
6201
- // doesn't have it currently, see https://github.com/fgnass/domino/issues/177.
6202
- else {
6203
- const options = element.options;
6204
- for (let i = 0; i < options.length; i++) {
6205
- const opt = options[i];
6206
- if (opt.selected) {
6207
- const val = this._getOptionValue(opt.value);
6208
- selected.push(val);
6209
- }
6210
- }
6211
- }
6212
- this.value = selected;
6213
- fn(selected);
6214
- };
6215
- }
6216
- /** @internal */
6217
- _registerOption(value) {
6218
- const id = (this._idCounter++).toString();
6219
- this._optionMap.set(id, value);
6220
- return id;
5920
+ name = null;
5921
+ constructor(parent, validators, asyncValidators) {
5922
+ super();
5923
+ this._parent = parent;
5924
+ this._setValidators(validators);
5925
+ this._setAsyncValidators(asyncValidators);
6221
5926
  }
6222
5927
  /** @internal */
6223
- _getOptionId(value) {
6224
- for (const id of this._optionMap.keys()) {
6225
- if (this._compareWith(this._optionMap.get(id)._value, value))
6226
- return id;
5928
+ _checkParentType() {
5929
+ if (hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
5930
+ throw groupParentException();
6227
5931
  }
6228
- return null;
6229
5932
  }
6230
- /** @internal */
6231
- _getOptionValue(valueString) {
6232
- const id = _extractId(valueString);
6233
- return this._optionMap.has(id) ? this._optionMap.get(id)._value : valueString;
6234
- }
6235
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SelectMultipleControlValueAccessor, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6236
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: SelectMultipleControlValueAccessor, isStandalone: false, selector: "select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]", inputs: { compareWith: "compareWith" }, host: { listeners: { "change": "onChange($event.target)", "blur": "onTouched()" } }, providers: [SELECT_MULTIPLE_VALUE_ACCESSOR], usesInheritance: true, ngImport: i0 });
5933
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormGroupName, deps: [{ token: ControlContainer, host: true, optional: true, skipSelf: true }, { token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
5934
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormGroupName, isStandalone: false, selector: "[formGroupName]", inputs: { name: ["formGroupName", "name"] }, providers: [formGroupNameProvider], usesInheritance: true, ngImport: i0 });
6237
5935
  }
6238
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SelectMultipleControlValueAccessor, decorators: [{
5936
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormGroupName, decorators: [{
6239
5937
  type: Directive,
6240
5938
  args: [{
6241
- selector: 'select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]',
6242
- host: { '(change)': 'onChange($event.target)', '(blur)': 'onTouched()' },
6243
- providers: [SELECT_MULTIPLE_VALUE_ACCESSOR],
5939
+ selector: '[formGroupName]',
5940
+ providers: [formGroupNameProvider],
6244
5941
  standalone: false,
6245
5942
  }]
6246
- }], propDecorators: { compareWith: [{
6247
- type: Input
5943
+ }], ctorParameters: () => [{ type: ControlContainer, decorators: [{
5944
+ type: Optional
5945
+ }, {
5946
+ type: Host
5947
+ }, {
5948
+ type: SkipSelf
5949
+ }] }, { type: undefined, decorators: [{
5950
+ type: Optional
5951
+ }, {
5952
+ type: Self
5953
+ }, {
5954
+ type: Inject,
5955
+ args: [NG_VALIDATORS]
5956
+ }] }, { type: undefined, decorators: [{
5957
+ type: Optional
5958
+ }, {
5959
+ type: Self
5960
+ }, {
5961
+ type: Inject,
5962
+ args: [NG_ASYNC_VALIDATORS]
5963
+ }] }], propDecorators: { name: [{
5964
+ type: Input,
5965
+ args: ['formGroupName']
6248
5966
  }] } });
5967
+ const formArrayNameProvider = {
5968
+ provide: ControlContainer,
5969
+ useExisting: forwardRef(() => FormArrayName),
5970
+ };
6249
5971
  /**
6250
5972
  * @description
6251
- * Marks `<option>` as dynamic, so Angular can be notified when options change.
6252
5973
  *
6253
- * @see {@link SelectMultipleControlValueAccessor}
5974
+ * Syncs a nested `FormArray` to a DOM element.
5975
+ *
5976
+ * This directive is designed to be used with a parent `FormGroupDirective`/`FormGroupArray` (selector:
5977
+ * `[formGroup]`/`[formArray]`).
5978
+ *
5979
+ * It accepts the string name of the nested `FormArray` you want to link, and
5980
+ * will look for a `FormArray` registered with that name in the parent
5981
+ * `FormGroup`/`FormArray` instance you passed into `FormGroupDirective`/`FormGroupArray`.
5982
+ *
5983
+ * @see [Reactive Forms Guide](guide/forms/reactive-forms)
5984
+ * @see {@link AbstractControl}
5985
+ *
5986
+ * @usageNotes
5987
+ *
5988
+ * ### Example
5989
+ *
5990
+ * {@example forms/ts/nestedFormArray/nested_form_array_example.ts region='Component'}
6254
5991
  *
6255
5992
  * @ngModule ReactiveFormsModule
6256
- * @ngModule FormsModule
6257
5993
  * @publicApi
6258
5994
  */
6259
- class ɵNgSelectMultipleOption {
6260
- _element;
6261
- _renderer;
6262
- _select;
6263
- id;
5995
+ class FormArrayName extends ControlContainer {
6264
5996
  /** @internal */
6265
- _value;
6266
- constructor(_element, _renderer, _select) {
6267
- this._element = _element;
6268
- this._renderer = _renderer;
6269
- this._select = _select;
6270
- if (this._select) {
6271
- this.id = this._select._registerOption(this);
6272
- }
6273
- }
5997
+ _parent;
6274
5998
  /**
6275
5999
  * @description
6276
- * Tracks the value bound to the option element. Unlike the value binding,
6277
- * ngValue supports binding to objects.
6000
+ * Tracks the name of the `FormArray` bound to the directive. The name corresponds
6001
+ * to a key in the parent `FormGroup` or `FormArray`.
6002
+ * Accepts a name as a string or a number.
6003
+ * The name in the form of a string is useful for individual forms,
6004
+ * while the numerical form allows for form arrays to be bound
6005
+ * to indices when iterating over arrays in a `FormArray`.
6278
6006
  */
6279
- set ngValue(value) {
6280
- if (this._select == null)
6281
- return;
6282
- this._value = value;
6283
- this._setElementValue(_buildValueString(this.id, value));
6284
- this._select.writeValue(this._select.value);
6007
+ name = null;
6008
+ constructor(parent, validators, asyncValidators) {
6009
+ super();
6010
+ this._parent = parent;
6011
+ this._setValidators(validators);
6012
+ this._setAsyncValidators(asyncValidators);
6285
6013
  }
6286
6014
  /**
6287
- * @description
6288
- * Tracks simple string values bound to the option element.
6289
- * For objects, use the `ngValue` input binding.
6015
+ * A lifecycle method called when the directive's inputs are initialized. For internal use only.
6016
+ * @throws If the directive does not have a valid parent.
6017
+ * @docs-private
6290
6018
  */
6291
- set value(value) {
6292
- if (this._select) {
6293
- this._value = value;
6294
- this._setElementValue(_buildValueString(this.id, value));
6295
- this._select.writeValue(this._select.value);
6296
- }
6297
- else {
6298
- this._setElementValue(value);
6019
+ ngOnInit() {
6020
+ if (hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
6021
+ throw arrayParentException();
6299
6022
  }
6023
+ this.formDirective.addFormArray(this);
6300
6024
  }
6301
- /** @internal */
6302
- _setElementValue(value) {
6303
- this._renderer.setProperty(this._element.nativeElement, 'value', value);
6025
+ /**
6026
+ * A lifecycle method called before the directive's instance is destroyed. For internal use only.
6027
+ * @docs-private
6028
+ */
6029
+ ngOnDestroy() {
6030
+ this.formDirective?.removeFormArray(this);
6304
6031
  }
6305
- /** @internal */
6306
- _setSelected(selected) {
6307
- this._renderer.setProperty(this._element.nativeElement, 'selected', selected);
6032
+ /**
6033
+ * @description
6034
+ * The `FormArray` bound to this directive.
6035
+ */
6036
+ get control() {
6037
+ return this.formDirective.getFormArray(this);
6308
6038
  }
6309
- /** @docs-private */
6310
- ngOnDestroy() {
6311
- if (this._select) {
6312
- this._select._optionMap.delete(this.id);
6313
- this._select.writeValue(this._select.value);
6314
- }
6039
+ /**
6040
+ * @description
6041
+ * The top-level directive for this group if present, otherwise null.
6042
+ */
6043
+ get formDirective() {
6044
+ return this._parent ? this._parent.formDirective : null;
6315
6045
  }
6316
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵNgSelectMultipleOption, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: SelectMultipleControlValueAccessor, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
6317
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: ɵNgSelectMultipleOption, isStandalone: false, selector: "option", inputs: { ngValue: "ngValue", value: "value" }, ngImport: i0 });
6046
+ /**
6047
+ * @description
6048
+ * Returns an array that represents the path from the top-level form to this control.
6049
+ * Each index is the string name of the control on that level.
6050
+ */
6051
+ get path() {
6052
+ return controlPath(this.name == null ? this.name : this.name.toString(), this._parent);
6053
+ }
6054
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormArrayName, deps: [{ token: ControlContainer, host: true, optional: true, skipSelf: true }, { token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
6055
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormArrayName, isStandalone: false, selector: "[formArrayName]", inputs: { name: ["formArrayName", "name"] }, providers: [formArrayNameProvider], usesInheritance: true, ngImport: i0 });
6318
6056
  }
6319
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵNgSelectMultipleOption, decorators: [{
6057
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormArrayName, decorators: [{
6320
6058
  type: Directive,
6321
6059
  args: [{
6322
- selector: 'option',
6060
+ selector: '[formArrayName]',
6061
+ providers: [formArrayNameProvider],
6323
6062
  standalone: false,
6324
6063
  }]
6325
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: SelectMultipleControlValueAccessor, decorators: [{
6064
+ }], ctorParameters: () => [{ type: ControlContainer, decorators: [{
6326
6065
  type: Optional
6327
6066
  }, {
6328
6067
  type: Host
6329
- }] }], propDecorators: { ngValue: [{
6330
- type: Input,
6331
- args: ['ngValue']
6332
- }], value: [{
6068
+ }, {
6069
+ type: SkipSelf
6070
+ }] }, { type: undefined, decorators: [{
6071
+ type: Optional
6072
+ }, {
6073
+ type: Self
6074
+ }, {
6075
+ type: Inject,
6076
+ args: [NG_VALIDATORS]
6077
+ }] }, { type: undefined, decorators: [{
6078
+ type: Optional
6079
+ }, {
6080
+ type: Self
6081
+ }, {
6082
+ type: Inject,
6083
+ args: [NG_ASYNC_VALIDATORS]
6084
+ }] }], propDecorators: { name: [{
6333
6085
  type: Input,
6334
- args: ['value']
6086
+ args: ['formArrayName']
6335
6087
  }] } });
6088
+ function hasInvalidParent(parent) {
6089
+ return (!(parent instanceof FormGroupName) &&
6090
+ !(parent instanceof AbstractFormDirective) &&
6091
+ !(parent instanceof FormArrayName));
6092
+ }
6336
6093
 
6094
+ const controlNameBinding = {
6095
+ provide: NgControl,
6096
+ useExisting: forwardRef(() => FormControlName),
6097
+ };
6337
6098
  /**
6338
- * Method that updates string to integer if not already a number
6099
+ * @description
6100
+ * Syncs a `FormControl` in an existing `FormGroup` to a form control
6101
+ * element by name.
6339
6102
  *
6340
- * @param value The value to convert to integer.
6341
- * @returns value of parameter converted to number or integer.
6342
- */
6343
- function toInteger(value) {
6344
- return typeof value === 'number' ? value : parseInt(value, 10);
6345
- }
6346
- /**
6347
- * Method that ensures that provided value is a float (and converts it to float if needed).
6103
+ * @see [Reactive Forms Guide](guide/forms/reactive-forms)
6104
+ * @see {@link FormControl}
6105
+ * @see {@link AbstractControl}
6348
6106
  *
6349
- * @param value The value to convert to float.
6350
- * @returns value of parameter converted to number or float.
6351
- */
6352
- function toFloat(value) {
6353
- return typeof value === 'number' ? value : parseFloat(value);
6354
- }
6355
- /**
6356
- * A base class for Validator-based Directives. The class contains common logic shared across such
6357
- * Directives.
6107
+ * @usageNotes
6358
6108
  *
6359
- * For internal use only, this class is not intended for use outside of the Forms package.
6109
+ * ### Register `FormControl` within a group
6110
+ *
6111
+ * The following example shows how to register multiple form controls within a form group
6112
+ * and set their value.
6113
+ *
6114
+ * {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
6115
+ *
6116
+ * To see `formControlName` examples with different form control types, see:
6117
+ *
6118
+ * * Radio buttons: `RadioControlValueAccessor`
6119
+ * * Selects: `SelectControlValueAccessor`
6120
+ *
6121
+ * ### Use with ngModel is deprecated
6122
+ *
6123
+ * Support for using the `ngModel` input property and `ngModelChange` event with reactive
6124
+ * form directives has been deprecated in Angular v6 and is scheduled for removal in
6125
+ * a future version of Angular.
6126
+ *
6127
+ * @ngModule ReactiveFormsModule
6128
+ * @publicApi
6360
6129
  */
6361
- class AbstractValidatorDirective {
6362
- _validator = nullValidator;
6363
- _onChange;
6130
+ class FormControlName extends NgControl {
6131
+ _ngModelWarningConfig;
6132
+ _added = false;
6364
6133
  /**
6365
- * A flag that tracks whether this validator is enabled.
6134
+ * Internal reference to the view model value.
6135
+ * @internal
6136
+ */
6137
+ viewModel;
6138
+ /**
6139
+ * @description
6140
+ * Tracks the `FormControl` instance bound to the directive.
6141
+ */
6142
+ control;
6143
+ /**
6144
+ * @description
6145
+ * Tracks the name of the `FormControl` bound to the directive. The name corresponds
6146
+ * to a key in the parent `FormGroup` or `FormArray`.
6147
+ * Accepts a name as a string or a number.
6148
+ * The name in the form of a string is useful for individual forms,
6149
+ * while the numerical form allows for form controls to be bound
6150
+ * to indices when iterating over controls in a `FormArray`.
6151
+ */
6152
+ name = null;
6153
+ /**
6154
+ * @description
6155
+ * Triggers a warning in dev mode that this input should not be used with reactive forms.
6156
+ */
6157
+ set isDisabled(isDisabled) {
6158
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
6159
+ console.warn(disabledAttrWarning);
6160
+ }
6161
+ }
6162
+ // TODO(kara): remove next 4 properties once deprecation period is over
6163
+ /** @deprecated as of v6 */
6164
+ model;
6165
+ /** @deprecated as of v6 */
6166
+ update = new EventEmitter();
6167
+ /**
6168
+ * @description
6169
+ * Static property used to track whether any ngModel warnings have been sent across
6170
+ * all instances of FormControlName. Used to support warning config of "once".
6366
6171
  *
6367
- * Marking it `internal` (vs `protected`), so that this flag can be used in host bindings of
6368
- * directive classes that extend this base class.
6369
6172
  * @internal
6370
6173
  */
6371
- _enabled;
6174
+ static _ngModelWarningSentOnce = false;
6175
+ /**
6176
+ * @description
6177
+ * Instance property used to track whether an ngModel warning has been sent out for this
6178
+ * particular FormControlName instance. Used to support warning config of "always".
6179
+ *
6180
+ * @internal
6181
+ */
6182
+ _ngModelWarningSent = false;
6183
+ constructor(parent, validators, asyncValidators, valueAccessors, _ngModelWarningConfig) {
6184
+ super();
6185
+ this._ngModelWarningConfig = _ngModelWarningConfig;
6186
+ this._parent = parent;
6187
+ this._setValidators(validators);
6188
+ this._setAsyncValidators(asyncValidators);
6189
+ this.valueAccessor = selectValueAccessor(this, valueAccessors);
6190
+ }
6372
6191
  /** @docs-private */
6373
6192
  ngOnChanges(changes) {
6374
- if (this.inputName in changes) {
6375
- const input = this.normalizeInput(changes[this.inputName].currentValue);
6376
- this._enabled = this.enabled(input);
6377
- this._validator = this._enabled ? this.createValidator(input) : nullValidator;
6378
- if (this._onChange) {
6379
- this._onChange();
6193
+ if (!this._added)
6194
+ this._setUpControl();
6195
+ if (isPropertyUpdated(changes, this.viewModel)) {
6196
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
6197
+ _ngModelWarning('formControlName', FormControlName, this, this._ngModelWarningConfig);
6380
6198
  }
6199
+ this.viewModel = this.model;
6200
+ this.formDirective.updateModel(this, this.model);
6381
6201
  }
6382
6202
  }
6383
6203
  /** @docs-private */
6384
- validate(control) {
6385
- return this._validator(control);
6386
- }
6387
- /** @docs-private */
6388
- registerOnValidatorChange(fn) {
6389
- this._onChange = fn;
6204
+ ngOnDestroy() {
6205
+ if (this.formDirective) {
6206
+ this.formDirective.removeControl(this);
6207
+ }
6390
6208
  }
6391
6209
  /**
6392
6210
  * @description
6393
- * Determines whether this validator should be active or not based on an input.
6394
- * Base class implementation checks whether an input is defined (if the value is different from
6395
- * `null` and `undefined`). Validator classes that extend this base class can override this
6396
- * function with the logic specific to a particular validator directive.
6211
+ * Sets the new value for the view model and emits an `ngModelChange` event.
6212
+ *
6213
+ * @param newValue The new value for the view model.
6397
6214
  */
6398
- enabled(input) {
6399
- return input != null /* both `null` and `undefined` */;
6215
+ viewToModelUpdate(newValue) {
6216
+ this.viewModel = newValue;
6217
+ this.update.emit(newValue);
6400
6218
  }
6401
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: AbstractValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
6402
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: AbstractValidatorDirective, isStandalone: true, usesOnChanges: true, ngImport: i0 });
6219
+ /**
6220
+ * @description
6221
+ * Returns an array that represents the path from the top-level form to this control.
6222
+ * Each index is the string name of the control on that level.
6223
+ */
6224
+ get path() {
6225
+ return controlPath(this.name == null ? this.name : this.name.toString(), this._parent);
6226
+ }
6227
+ /**
6228
+ * @description
6229
+ * The top-level directive for this group if present, otherwise null.
6230
+ */
6231
+ get formDirective() {
6232
+ return this._parent ? this._parent.formDirective : null;
6233
+ }
6234
+ _setUpControl() {
6235
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
6236
+ checkParentType(this._parent, this.name);
6237
+ }
6238
+ this.control = this.formDirective.addControl(this);
6239
+ this._added = true;
6240
+ }
6241
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormControlName, deps: [{ token: ControlContainer, host: true, optional: true, skipSelf: true }, { token: NG_VALIDATORS, optional: true, self: true }, { token: NG_ASYNC_VALIDATORS, optional: true, self: true }, { token: NG_VALUE_ACCESSOR, optional: true, self: true }, { token: NG_MODEL_WITH_FORM_CONTROL_WARNING, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
6242
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormControlName, isStandalone: false, selector: "[formControlName]", inputs: { name: ["formControlName", "name"], isDisabled: ["disabled", "isDisabled"], model: ["ngModel", "model"] }, outputs: { update: "ngModelChange" }, providers: [controlNameBinding], usesInheritance: true, usesOnChanges: true, ngImport: i0 });
6403
6243
  }
6404
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: AbstractValidatorDirective, decorators: [{
6405
- type: Directive
6406
- }] });
6407
- /**
6408
- * @description
6409
- * Provider which adds `MaxValidator` to the `NG_VALIDATORS` multi-provider list.
6410
- */
6411
- const MAX_VALIDATOR = {
6412
- provide: NG_VALIDATORS,
6413
- useExisting: forwardRef(() => MaxValidator),
6414
- multi: true,
6244
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormControlName, decorators: [{
6245
+ type: Directive,
6246
+ args: [{
6247
+ selector: '[formControlName]',
6248
+ providers: [controlNameBinding],
6249
+ standalone: false,
6250
+ }]
6251
+ }], ctorParameters: () => [{ type: ControlContainer, decorators: [{
6252
+ type: Optional
6253
+ }, {
6254
+ type: Host
6255
+ }, {
6256
+ type: SkipSelf
6257
+ }] }, { type: undefined, decorators: [{
6258
+ type: Optional
6259
+ }, {
6260
+ type: Self
6261
+ }, {
6262
+ type: Inject,
6263
+ args: [NG_VALIDATORS]
6264
+ }] }, { type: undefined, decorators: [{
6265
+ type: Optional
6266
+ }, {
6267
+ type: Self
6268
+ }, {
6269
+ type: Inject,
6270
+ args: [NG_ASYNC_VALIDATORS]
6271
+ }] }, { type: undefined, decorators: [{
6272
+ type: Optional
6273
+ }, {
6274
+ type: Self
6275
+ }, {
6276
+ type: Inject,
6277
+ args: [NG_VALUE_ACCESSOR]
6278
+ }] }, { type: undefined, decorators: [{
6279
+ type: Optional
6280
+ }, {
6281
+ type: Inject,
6282
+ args: [NG_MODEL_WITH_FORM_CONTROL_WARNING]
6283
+ }] }], propDecorators: { name: [{
6284
+ type: Input,
6285
+ args: ['formControlName']
6286
+ }], isDisabled: [{
6287
+ type: Input,
6288
+ args: ['disabled']
6289
+ }], model: [{
6290
+ type: Input,
6291
+ args: ['ngModel']
6292
+ }], update: [{
6293
+ type: Output,
6294
+ args: ['ngModelChange']
6295
+ }] } });
6296
+ function checkParentType(parent, name) {
6297
+ if (!(parent instanceof FormGroupName) && parent instanceof AbstractFormGroupDirective) {
6298
+ throw ngModelGroupException();
6299
+ }
6300
+ else if (!(parent instanceof FormGroupName) &&
6301
+ !(parent instanceof AbstractFormDirective) &&
6302
+ !(parent instanceof FormArrayName)) {
6303
+ throw controlParentException(name);
6304
+ }
6305
+ }
6306
+
6307
+ const formDirectiveProvider = {
6308
+ provide: ControlContainer,
6309
+ useExisting: forwardRef(() => FormGroupDirective),
6415
6310
  };
6416
6311
  /**
6417
- * A directive which installs the {@link MaxValidator} for any `formControlName`,
6418
- * `formControl`, or control with `ngModel` that also has a `max` attribute.
6312
+ * @description
6419
6313
  *
6420
- * @see [Form Validation](guide/forms/form-validation)
6314
+ * Binds an existing `FormGroup` or `FormRecord` to a DOM element.
6421
6315
  *
6422
- * @usageNotes
6316
+ * This directive accepts an existing `FormGroup` instance. It will then use this
6317
+ * `FormGroup` instance to match any child `FormControl`, `FormGroup`/`FormRecord`,
6318
+ * and `FormArray` instances to child `FormControlName`, `FormGroupName`,
6319
+ * and `FormArrayName` directives.
6423
6320
  *
6424
- * ### Adding a max validator
6321
+ * @see [Reactive Forms Guide](guide/forms/reactive-forms)
6322
+ * @see {@link AbstractControl}
6425
6323
  *
6426
- * The following example shows how to add a max validator to an input attached to an
6427
- * ngModel binding.
6324
+ * @usageNotes
6325
+ * ### Register Form Group
6428
6326
  *
6429
- * ```html
6430
- * <input type="number" ngModel max="4">
6431
- * ```
6327
+ * The following example registers a `FormGroup` with first name and last name controls,
6328
+ * and listens for the *ngSubmit* event when the button is clicked.
6329
+ *
6330
+ * {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
6432
6331
  *
6433
6332
  * @ngModule ReactiveFormsModule
6434
- * @ngModule FormsModule
6435
6333
  * @publicApi
6436
6334
  */
6437
- class MaxValidator extends AbstractValidatorDirective {
6335
+ class FormGroupDirective extends AbstractFormDirective {
6438
6336
  /**
6439
6337
  * @description
6440
- * Tracks changes to the max bound to this directive.
6338
+ * Tracks the `FormGroup` bound to this directive.
6441
6339
  */
6442
- max;
6443
- /** @internal */
6444
- inputName = 'max';
6445
- /** @internal */
6446
- normalizeInput = (input) => toFloat(input);
6447
- /** @internal */
6448
- createValidator = (max) => maxValidator(max);
6449
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MaxValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6450
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: MaxValidator, isStandalone: false, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: { max: "max" }, host: { properties: { "attr.max": "_enabled ? max : null" } }, providers: [MAX_VALIDATOR], usesInheritance: true, ngImport: i0 });
6340
+ form = null;
6341
+ /**
6342
+ * @description
6343
+ * Emits an event when the form submission has been triggered.
6344
+ */
6345
+ ngSubmit = new EventEmitter();
6346
+ /**
6347
+ * @description
6348
+ * Returns the `FormGroup` bound to this directive.
6349
+ */
6350
+ get control() {
6351
+ return this.form;
6352
+ }
6353
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormGroupDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6354
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: FormGroupDirective, isStandalone: false, selector: "[formGroup]", inputs: { form: ["formGroup", "form"] }, outputs: { ngSubmit: "ngSubmit" }, host: { listeners: { "submit": "onSubmit($event)", "reset": "onReset()" } }, providers: [formDirectiveProvider], exportAs: ["ngForm"], usesInheritance: true, ngImport: i0 });
6451
6355
  }
6452
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MaxValidator, decorators: [{
6356
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FormGroupDirective, decorators: [{
6453
6357
  type: Directive,
6454
6358
  args: [{
6455
- selector: 'input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]',
6456
- providers: [MAX_VALIDATOR],
6457
- host: { '[attr.max]': '_enabled ? max : null' },
6359
+ selector: '[formGroup]',
6360
+ providers: [formDirectiveProvider],
6361
+ host: { '(submit)': 'onSubmit($event)', '(reset)': 'onReset()' },
6362
+ exportAs: 'ngForm',
6458
6363
  standalone: false,
6459
6364
  }]
6460
- }], propDecorators: { max: [{
6461
- type: Input
6365
+ }], propDecorators: { form: [{
6366
+ type: Input,
6367
+ args: ['formGroup']
6368
+ }], ngSubmit: [{
6369
+ type: Output
6462
6370
  }] } });
6463
- /**
6464
- * @description
6465
- * Provider which adds `MinValidator` to the `NG_VALIDATORS` multi-provider list.
6466
- */
6467
- const MIN_VALIDATOR = {
6468
- provide: NG_VALIDATORS,
6469
- useExisting: forwardRef(() => MinValidator),
6371
+
6372
+ const SELECT_VALUE_ACCESSOR = {
6373
+ provide: NG_VALUE_ACCESSOR,
6374
+ useExisting: forwardRef(() => SelectControlValueAccessor),
6470
6375
  multi: true,
6471
6376
  };
6377
+ function _buildValueString$1(id, value) {
6378
+ if (id == null)
6379
+ return `${value}`;
6380
+ if (value && typeof value === 'object')
6381
+ value = 'Object';
6382
+ return `${id}: ${value}`.slice(0, 50);
6383
+ }
6384
+ function _extractId$1(valueString) {
6385
+ return valueString.split(':')[0];
6386
+ }
6472
6387
  /**
6473
- * A directive which installs the {@link MinValidator} for any `formControlName`,
6474
- * `formControl`, or control with `ngModel` that also has a `min` attribute.
6475
- *
6476
- * @see [Form Validation](guide/forms/form-validation)
6388
+ * @description
6389
+ * The `ControlValueAccessor` for writing select control values and listening to select control
6390
+ * changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and
6391
+ * `NgModel` directives.
6477
6392
  *
6478
6393
  * @usageNotes
6479
6394
  *
6480
- * ### Adding a min validator
6395
+ * ### Using select controls in a reactive form
6481
6396
  *
6482
- * The following example shows how to add a min validator to an input attached to an
6483
- * ngModel binding.
6397
+ * The following examples show how to use a select control in a reactive form.
6398
+ *
6399
+ * {@example forms/ts/reactiveSelectControl/reactive_select_control_example.ts region='Component'}
6400
+ *
6401
+ * ### Using select controls in a template-driven form
6402
+ *
6403
+ * To use a select in a template-driven form, simply add an `ngModel` and a `name`
6404
+ * attribute to the main `<select>` tag.
6405
+ *
6406
+ * {@example forms/ts/selectControl/select_control_example.ts region='Component'}
6407
+ *
6408
+ * ### Customizing option selection
6409
+ *
6410
+ * Angular uses object identity to select option. It's possible for the identities of items
6411
+ * to change while the data does not. This can happen, for example, if the items are produced
6412
+ * from an RPC to the server, and that RPC is re-run. Even if the data hasn't changed, the
6413
+ * second response will produce objects with different identities.
6414
+ *
6415
+ * To customize the default option comparison algorithm, `<select>` supports `compareWith` input.
6416
+ * `compareWith` takes a **function** which has two arguments: `option1` and `option2`.
6417
+ * If `compareWith` is given, Angular selects option by the return value of the function.
6418
+ *
6419
+ * ```ts
6420
+ * const selectedCountriesControl = new FormControl();
6421
+ * ```
6484
6422
  *
6485
6423
  * ```html
6486
- * <input type="number" ngModel min="4">
6424
+ * <select [compareWith]="compareFn" [formControl]="selectedCountriesControl">
6425
+ * @for(country of countries; track $index) {
6426
+ * <option[ngValue]="country">{{country.name}}</option>
6427
+ * }
6428
+ * </select>
6429
+ *
6430
+ * compareFn(c1: Country, c2: Country): boolean {
6431
+ * return c1 && c2 ? c1.id === c2.id : c1 === c2;
6432
+ * }
6487
6433
  * ```
6488
6434
  *
6435
+ * **Note:** We listen to the 'change' event because 'input' events aren't fired
6436
+ * for selects in IE, see:
6437
+ * https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event#browser_compatibility
6438
+ *
6489
6439
  * @ngModule ReactiveFormsModule
6490
6440
  * @ngModule FormsModule
6491
6441
  * @publicApi
6492
6442
  */
6493
- class MinValidator extends AbstractValidatorDirective {
6443
+ class SelectControlValueAccessor extends BuiltInControlValueAccessor {
6444
+ /** @docs-private */
6445
+ value;
6446
+ /** @internal */
6447
+ _optionMap = new Map();
6448
+ /** @internal */
6449
+ _idCounter = 0;
6494
6450
  /**
6495
6451
  * @description
6496
- * Tracks changes to the min bound to this directive.
6452
+ * Tracks the option comparison algorithm for tracking identities when
6453
+ * checking for changes.
6497
6454
  */
6498
- min;
6455
+ set compareWith(fn) {
6456
+ if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
6457
+ throw new _RuntimeError(1201 /* RuntimeErrorCode.COMPAREWITH_NOT_A_FN */, `compareWith must be a function, but received ${JSON.stringify(fn)}`);
6458
+ }
6459
+ this._compareWith = fn;
6460
+ }
6461
+ _compareWith = Object.is;
6462
+ // We need this because we might be in the process of destroying the root
6463
+ // injector, which is marked as destroyed before running destroy hooks.
6464
+ // Attempting to use afterNextRender with the node injector would evntually
6465
+ // run into that already destroyed injector.
6466
+ appRefInjector = inject(ApplicationRef).injector;
6467
+ destroyRef = inject(DestroyRef);
6468
+ cdr = inject(ChangeDetectorRef);
6469
+ _queuedWrite = false;
6470
+ /**
6471
+ * This is needed to efficiently set the select value when adding/removing options. If
6472
+ * writeValue is instead called for every added/removed option, this results in exponentially
6473
+ * more _compareValue calls than the number of option elements (issue #41330).
6474
+ *
6475
+ * Secondly, calling writeValue when rendering individual option elements instead of after they
6476
+ * are all rendered caused an issue in Safari and IE 11 where the first option element failed
6477
+ * to be deselected when no option matched the select ngModel. This was because Angular would
6478
+ * set the select element's value property before appending the option's child text node to the
6479
+ * DOM (issue #14505).
6480
+ *
6481
+ * Finally, this approach is necessary to avoid an issue with delayed element removal when
6482
+ * using the animations module (in all browsers). Otherwise when a selected option is removed
6483
+ * (so no option matches the ngModel anymore), Angular would change the select element value
6484
+ * before actually removing the option from the DOM. Then when the option is finally removed
6485
+ * from the DOM, the browser would change the select value to that of the first option, even
6486
+ * though it doesn't match the ngModel (issue #18430).
6487
+ *
6488
+ * @internal
6489
+ */
6490
+ _writeValueAfterRender() {
6491
+ if (this._queuedWrite || this.appRefInjector.destroyed) {
6492
+ return;
6493
+ }
6494
+ this._queuedWrite = true;
6495
+ afterNextRender({
6496
+ write: () => {
6497
+ if (this.destroyRef.destroyed) {
6498
+ return;
6499
+ }
6500
+ this._queuedWrite = false;
6501
+ this.writeValue(this.value);
6502
+ },
6503
+ }, { injector: this.appRefInjector });
6504
+ }
6505
+ /**
6506
+ * Sets the "value" property on the select element.
6507
+ * @docs-private
6508
+ */
6509
+ writeValue(value) {
6510
+ // TODO(atscott): This could likely be optimized more by only marking for check if the value is changed
6511
+ // note that this needs to include both the internal value and the value in the DOM.
6512
+ this.cdr.markForCheck();
6513
+ this.value = value;
6514
+ const id = this._getOptionId(value);
6515
+ const valueString = _buildValueString$1(id, value);
6516
+ this.setProperty('value', valueString);
6517
+ }
6518
+ /**
6519
+ * Registers a function called when the control value changes.
6520
+ * @docs-private
6521
+ */
6522
+ registerOnChange(fn) {
6523
+ this.onChange = (valueString) => {
6524
+ this.value = this._getOptionValue(valueString);
6525
+ fn(this.value);
6526
+ };
6527
+ }
6499
6528
  /** @internal */
6500
- inputName = 'min';
6529
+ _registerOption() {
6530
+ return (this._idCounter++).toString();
6531
+ }
6501
6532
  /** @internal */
6502
- normalizeInput = (input) => toFloat(input);
6533
+ _getOptionId(value) {
6534
+ for (const id of this._optionMap.keys()) {
6535
+ if (this._compareWith(this._optionMap.get(id), value))
6536
+ return id;
6537
+ }
6538
+ return null;
6539
+ }
6503
6540
  /** @internal */
6504
- createValidator = (min) => minValidator(min);
6505
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MinValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6506
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: MinValidator, isStandalone: false, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: { min: "min" }, host: { properties: { "attr.min": "_enabled ? min : null" } }, providers: [MIN_VALIDATOR], usesInheritance: true, ngImport: i0 });
6541
+ _getOptionValue(valueString) {
6542
+ const id = _extractId$1(valueString);
6543
+ return this._optionMap.has(id) ? this._optionMap.get(id) : valueString;
6544
+ }
6545
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SelectControlValueAccessor, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6546
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: SelectControlValueAccessor, isStandalone: false, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: { compareWith: "compareWith" }, host: { listeners: { "change": "onChange($any($event.target).value)", "blur": "onTouched()" } }, providers: [SELECT_VALUE_ACCESSOR], usesInheritance: true, ngImport: i0 });
6507
6547
  }
6508
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MinValidator, decorators: [{
6548
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SelectControlValueAccessor, decorators: [{
6509
6549
  type: Directive,
6510
6550
  args: [{
6511
- selector: 'input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]',
6512
- providers: [MIN_VALIDATOR],
6513
- host: { '[attr.min]': '_enabled ? min : null' },
6551
+ selector: 'select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]',
6552
+ host: { '(change)': 'onChange($any($event.target).value)', '(blur)': 'onTouched()' },
6553
+ providers: [SELECT_VALUE_ACCESSOR],
6514
6554
  standalone: false,
6515
6555
  }]
6516
- }], propDecorators: { min: [{
6556
+ }], propDecorators: { compareWith: [{
6517
6557
  type: Input
6518
6558
  }] } });
6519
6559
  /**
6520
6560
  * @description
6521
- * Provider which adds `RequiredValidator` to the `NG_VALIDATORS` multi-provider list.
6522
- */
6523
- const REQUIRED_VALIDATOR = {
6524
- provide: NG_VALIDATORS,
6525
- useExisting: forwardRef(() => RequiredValidator),
6526
- multi: true,
6527
- };
6528
- /**
6529
- * @description
6530
- * Provider which adds `CheckboxRequiredValidator` to the `NG_VALIDATORS` multi-provider list.
6531
- */
6532
- const CHECKBOX_REQUIRED_VALIDATOR = {
6533
- provide: NG_VALIDATORS,
6534
- useExisting: forwardRef(() => CheckboxRequiredValidator),
6535
- multi: true,
6536
- };
6537
- /**
6538
- * @description
6539
- * A directive that adds the `required` validator to any controls marked with the
6540
- * `required` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
6541
- *
6542
- * @see [Form Validation](guide/forms/form-validation)
6543
- *
6544
- * @usageNotes
6545
- *
6546
- * ### Adding a required validator using template-driven forms
6561
+ * Marks `<option>` as dynamic, so Angular can be notified when options change.
6547
6562
  *
6548
- * ```html
6549
- * <input name="fullName" ngModel required>
6550
- * ```
6563
+ * @see {@link SelectControlValueAccessor}
6551
6564
  *
6552
- * @ngModule FormsModule
6553
6565
  * @ngModule ReactiveFormsModule
6566
+ * @ngModule FormsModule
6554
6567
  * @publicApi
6555
6568
  */
6556
- class RequiredValidator extends AbstractValidatorDirective {
6569
+ class NgSelectOption {
6570
+ _element;
6571
+ _renderer;
6572
+ _select;
6557
6573
  /**
6558
6574
  * @description
6559
- * Tracks changes to the required attribute bound to this directive.
6575
+ * ID of the option element
6560
6576
  */
6561
- required;
6562
- /** @internal */
6563
- inputName = 'required';
6564
- /** @internal */
6565
- normalizeInput = booleanAttribute;
6577
+ id;
6578
+ constructor(_element, _renderer, _select) {
6579
+ this._element = _element;
6580
+ this._renderer = _renderer;
6581
+ this._select = _select;
6582
+ if (this._select)
6583
+ this.id = this._select._registerOption();
6584
+ }
6585
+ /**
6586
+ * @description
6587
+ * Tracks the value bound to the option element. Unlike the value binding,
6588
+ * ngValue supports binding to objects.
6589
+ */
6590
+ set ngValue(value) {
6591
+ if (this._select == null)
6592
+ return;
6593
+ this._select._optionMap.set(this.id, value);
6594
+ this._setElementValue(_buildValueString$1(this.id, value));
6595
+ this._select._writeValueAfterRender();
6596
+ }
6597
+ /**
6598
+ * @description
6599
+ * Tracks simple string values bound to the option element.
6600
+ * For objects, use the `ngValue` input binding.
6601
+ */
6602
+ set value(value) {
6603
+ this._setElementValue(value);
6604
+ if (this._select)
6605
+ this._select._writeValueAfterRender();
6606
+ }
6566
6607
  /** @internal */
6567
- createValidator = (input) => requiredValidator;
6608
+ _setElementValue(value) {
6609
+ this._renderer.setProperty(this._element.nativeElement, 'value', value);
6610
+ }
6568
6611
  /** @docs-private */
6569
- enabled(input) {
6570
- return input;
6612
+ ngOnDestroy() {
6613
+ if (this._select) {
6614
+ this._select._optionMap.delete(this.id);
6615
+ this._select._writeValueAfterRender();
6616
+ }
6571
6617
  }
6572
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: RequiredValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6573
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: RequiredValidator, isStandalone: false, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: { required: "required" }, host: { properties: { "attr.required": "_enabled ? \"\" : null" } }, providers: [REQUIRED_VALIDATOR], usesInheritance: true, ngImport: i0 });
6618
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: NgSelectOption, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: SelectControlValueAccessor, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
6619
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: NgSelectOption, isStandalone: false, selector: "option", inputs: { ngValue: "ngValue", value: "value" }, ngImport: i0 });
6574
6620
  }
6575
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: RequiredValidator, decorators: [{
6621
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: NgSelectOption, decorators: [{
6576
6622
  type: Directive,
6577
6623
  args: [{
6578
- selector: ':not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]',
6579
- providers: [REQUIRED_VALIDATOR],
6580
- host: { '[attr.required]': '_enabled ? "" : null' },
6624
+ selector: 'option',
6581
6625
  standalone: false,
6582
6626
  }]
6583
- }], propDecorators: { required: [{
6584
- type: Input
6627
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: SelectControlValueAccessor, decorators: [{
6628
+ type: Optional
6629
+ }, {
6630
+ type: Host
6631
+ }] }], propDecorators: { ngValue: [{
6632
+ type: Input,
6633
+ args: ['ngValue']
6634
+ }], value: [{
6635
+ type: Input,
6636
+ args: ['value']
6585
6637
  }] } });
6638
+
6639
+ const SELECT_MULTIPLE_VALUE_ACCESSOR = {
6640
+ provide: NG_VALUE_ACCESSOR,
6641
+ useExisting: forwardRef(() => SelectMultipleControlValueAccessor),
6642
+ multi: true,
6643
+ };
6644
+ function _buildValueString(id, value) {
6645
+ if (id == null)
6646
+ return `${value}`;
6647
+ if (typeof value === 'string')
6648
+ value = `'${value}'`;
6649
+ if (value && typeof value === 'object')
6650
+ value = 'Object';
6651
+ return `${id}: ${value}`.slice(0, 50);
6652
+ }
6653
+ function _extractId(valueString) {
6654
+ return valueString.split(':')[0];
6655
+ }
6586
6656
  /**
6587
- * A Directive that adds the `required` validator to checkbox controls marked with the
6588
- * `required` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
6657
+ * @description
6658
+ * The `ControlValueAccessor` for writing multi-select control values and listening to multi-select
6659
+ * control changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and
6660
+ * `NgModel` directives.
6589
6661
  *
6590
- * @see [Form Validation](guide/forms/form-validation)
6662
+ * @see {@link SelectControlValueAccessor}
6591
6663
  *
6592
6664
  * @usageNotes
6593
6665
  *
6594
- * ### Adding a required checkbox validator using template-driven forms
6666
+ * ### Using a multi-select control
6595
6667
  *
6596
- * The following example shows how to add a checkbox required validator to an input attached to an
6597
- * ngModel binding.
6668
+ * The follow example shows you how to use a multi-select control with a reactive form.
6669
+ *
6670
+ * ```ts
6671
+ * const countryControl = new FormControl();
6672
+ * ```
6598
6673
  *
6599
6674
  * ```html
6600
- * <input type="checkbox" name="active" ngModel required>
6675
+ * <select multiple name="countries" [formControl]="countryControl">
6676
+ * @for(country of countries; track $index) {
6677
+ * <option [ngValue]="country">{{ country.name }}</option>
6678
+ * }
6679
+ * </select>
6601
6680
  * ```
6602
6681
  *
6603
- * @publicApi
6604
- * @ngModule FormsModule
6605
- * @ngModule ReactiveFormsModule
6606
- */
6607
- class CheckboxRequiredValidator extends RequiredValidator {
6608
- /** @internal */
6609
- createValidator = (input) => requiredTrueValidator;
6610
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: CheckboxRequiredValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6611
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: CheckboxRequiredValidator, isStandalone: false, selector: "input[type=checkbox][required][formControlName],input[type=checkbox][required][formControl],input[type=checkbox][required][ngModel]", host: { properties: { "attr.required": "_enabled ? \"\" : null" } }, providers: [CHECKBOX_REQUIRED_VALIDATOR], usesInheritance: true, ngImport: i0 });
6612
- }
6613
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: CheckboxRequiredValidator, decorators: [{
6614
- type: Directive,
6615
- args: [{
6616
- selector: 'input[type=checkbox][required][formControlName],input[type=checkbox][required][formControl],input[type=checkbox][required][ngModel]',
6617
- providers: [CHECKBOX_REQUIRED_VALIDATOR],
6618
- host: { '[attr.required]': '_enabled ? "" : null' },
6619
- standalone: false,
6620
- }]
6621
- }] });
6622
- /**
6623
- * @description
6624
- * Provider which adds `EmailValidator` to the `NG_VALIDATORS` multi-provider list.
6625
- */
6626
- const EMAIL_VALIDATOR = {
6627
- provide: NG_VALIDATORS,
6628
- useExisting: forwardRef(() => EmailValidator),
6629
- multi: true,
6630
- };
6631
- /**
6632
- * A directive that adds the `email` validator to controls marked with the
6633
- * `email` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
6634
- *
6635
- * The email validation is based on the WHATWG HTML specification with some enhancements to
6636
- * incorporate more RFC rules. More information can be found on the [Validators.email
6637
- * page](api/forms/Validators#email).
6638
- *
6639
- * @see [Form Validation](guide/forms/form-validation)
6640
- *
6641
- * @usageNotes
6642
- *
6643
- * ### Adding an email validator
6644
- *
6645
- * The following example shows how to add an email validator to an input attached to an ngModel
6646
- * binding.
6682
+ * ### Customizing option selection
6647
6683
  *
6648
- * ```html
6649
- * <input type="email" name="email" ngModel email>
6650
- * <input type="email" name="email" ngModel email="true">
6651
- * <input type="email" name="email" ngModel [email]="true">
6652
- * ```
6684
+ * To customize the default option comparison algorithm, `<select>` supports `compareWith` input.
6685
+ * See the `SelectControlValueAccessor` for usage.
6653
6686
  *
6654
- * @publicApi
6655
- * @ngModule FormsModule
6656
6687
  * @ngModule ReactiveFormsModule
6688
+ * @ngModule FormsModule
6689
+ * @publicApi
6657
6690
  */
6658
- class EmailValidator extends AbstractValidatorDirective {
6691
+ class SelectMultipleControlValueAccessor extends BuiltInControlValueAccessor {
6692
+ /**
6693
+ * The current value.
6694
+ * @docs-private
6695
+ */
6696
+ value;
6697
+ /** @internal */
6698
+ _optionMap = new Map();
6699
+ /** @internal */
6700
+ _idCounter = 0;
6659
6701
  /**
6660
6702
  * @description
6661
- * Tracks changes to the email attribute bound to this directive.
6703
+ * Tracks the option comparison algorithm for tracking identities when
6704
+ * checking for changes.
6662
6705
  */
6663
- email;
6706
+ set compareWith(fn) {
6707
+ if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
6708
+ throw new _RuntimeError(1201 /* RuntimeErrorCode.COMPAREWITH_NOT_A_FN */, `compareWith must be a function, but received ${JSON.stringify(fn)}`);
6709
+ }
6710
+ this._compareWith = fn;
6711
+ }
6712
+ _compareWith = Object.is;
6713
+ /**
6714
+ * Sets the "value" property on one or of more of the select's options.
6715
+ * @docs-private
6716
+ */
6717
+ writeValue(value) {
6718
+ this.value = value;
6719
+ let optionSelectedStateSetter;
6720
+ if (Array.isArray(value)) {
6721
+ // convert values to ids
6722
+ const ids = value.map((v) => this._getOptionId(v));
6723
+ optionSelectedStateSetter = (opt, o) => {
6724
+ opt._setSelected(ids.indexOf(o.toString()) > -1);
6725
+ };
6726
+ }
6727
+ else {
6728
+ optionSelectedStateSetter = (opt, o) => {
6729
+ opt._setSelected(false);
6730
+ };
6731
+ }
6732
+ this._optionMap.forEach(optionSelectedStateSetter);
6733
+ }
6734
+ /**
6735
+ * Registers a function called when the control value changes
6736
+ * and writes an array of the selected options.
6737
+ * @docs-private
6738
+ */
6739
+ registerOnChange(fn) {
6740
+ this.onChange = (element) => {
6741
+ const selected = [];
6742
+ const selectedOptions = element.selectedOptions;
6743
+ if (selectedOptions !== undefined) {
6744
+ const options = selectedOptions;
6745
+ for (let i = 0; i < options.length; i++) {
6746
+ const opt = options[i];
6747
+ const val = this._getOptionValue(opt.value);
6748
+ selected.push(val);
6749
+ }
6750
+ }
6751
+ // Degrade to use `options` when `selectedOptions` property is not available.
6752
+ // Note: the `selectedOptions` is available in all supported browsers, but the Domino lib
6753
+ // doesn't have it currently, see https://github.com/fgnass/domino/issues/177.
6754
+ else {
6755
+ const options = element.options;
6756
+ for (let i = 0; i < options.length; i++) {
6757
+ const opt = options[i];
6758
+ if (opt.selected) {
6759
+ const val = this._getOptionValue(opt.value);
6760
+ selected.push(val);
6761
+ }
6762
+ }
6763
+ }
6764
+ this.value = selected;
6765
+ fn(selected);
6766
+ };
6767
+ }
6664
6768
  /** @internal */
6665
- inputName = 'email';
6769
+ _registerOption(value) {
6770
+ const id = (this._idCounter++).toString();
6771
+ this._optionMap.set(id, value);
6772
+ return id;
6773
+ }
6666
6774
  /** @internal */
6667
- normalizeInput = booleanAttribute;
6775
+ _getOptionId(value) {
6776
+ for (const id of this._optionMap.keys()) {
6777
+ if (this._compareWith(this._optionMap.get(id)._value, value))
6778
+ return id;
6779
+ }
6780
+ return null;
6781
+ }
6668
6782
  /** @internal */
6669
- createValidator = (input) => emailValidator;
6670
- /** @docs-private */
6671
- enabled(input) {
6672
- return input;
6783
+ _getOptionValue(valueString) {
6784
+ const id = _extractId(valueString);
6785
+ return this._optionMap.has(id) ? this._optionMap.get(id)._value : valueString;
6673
6786
  }
6674
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: EmailValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6675
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: EmailValidator, isStandalone: false, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: { email: "email" }, providers: [EMAIL_VALIDATOR], usesInheritance: true, ngImport: i0 });
6787
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SelectMultipleControlValueAccessor, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6788
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: SelectMultipleControlValueAccessor, isStandalone: false, selector: "select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]", inputs: { compareWith: "compareWith" }, host: { listeners: { "change": "onChange($event.target)", "blur": "onTouched()" } }, providers: [SELECT_MULTIPLE_VALUE_ACCESSOR], usesInheritance: true, ngImport: i0 });
6676
6789
  }
6677
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: EmailValidator, decorators: [{
6790
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: SelectMultipleControlValueAccessor, decorators: [{
6678
6791
  type: Directive,
6679
6792
  args: [{
6680
- selector: '[email][formControlName],[email][formControl],[email][ngModel]',
6681
- providers: [EMAIL_VALIDATOR],
6793
+ selector: 'select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]',
6794
+ host: { '(change)': 'onChange($event.target)', '(blur)': 'onTouched()' },
6795
+ providers: [SELECT_MULTIPLE_VALUE_ACCESSOR],
6682
6796
  standalone: false,
6683
6797
  }]
6684
- }], propDecorators: { email: [{
6798
+ }], propDecorators: { compareWith: [{
6685
6799
  type: Input
6686
6800
  }] } });
6687
6801
  /**
6688
6802
  * @description
6689
- * Provider which adds `MinLengthValidator` to the `NG_VALIDATORS` multi-provider list.
6690
- */
6691
- const MIN_LENGTH_VALIDATOR = {
6692
- provide: NG_VALIDATORS,
6693
- useExisting: forwardRef(() => MinLengthValidator),
6694
- multi: true,
6695
- };
6696
- /**
6697
- * A directive that adds minimum length validation to controls marked with the
6698
- * `minlength` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
6699
- *
6700
- * @see [Form Validation](guide/forms/form-validation)
6701
- *
6702
- * @usageNotes
6703
- *
6704
- * ### Adding a minimum length validator
6705
- *
6706
- * The following example shows how to add a minimum length validator to an input attached to an
6707
- * ngModel binding.
6803
+ * Marks `<option>` as dynamic, so Angular can be notified when options change.
6708
6804
  *
6709
- * ```html
6710
- * <input name="firstName" ngModel minlength="4">
6711
- * ```
6805
+ * @see {@link SelectMultipleControlValueAccessor}
6712
6806
  *
6713
6807
  * @ngModule ReactiveFormsModule
6714
6808
  * @ngModule FormsModule
6715
6809
  * @publicApi
6716
6810
  */
6717
- class MinLengthValidator extends AbstractValidatorDirective {
6811
+ class ɵNgSelectMultipleOption {
6812
+ _element;
6813
+ _renderer;
6814
+ _select;
6815
+ id;
6816
+ /** @internal */
6817
+ _value;
6818
+ constructor(_element, _renderer, _select) {
6819
+ this._element = _element;
6820
+ this._renderer = _renderer;
6821
+ this._select = _select;
6822
+ if (this._select) {
6823
+ this.id = this._select._registerOption(this);
6824
+ }
6825
+ }
6718
6826
  /**
6719
6827
  * @description
6720
- * Tracks changes to the minimum length bound to this directive.
6828
+ * Tracks the value bound to the option element. Unlike the value binding,
6829
+ * ngValue supports binding to objects.
6721
6830
  */
6722
- minlength;
6723
- /** @internal */
6724
- inputName = 'minlength';
6831
+ set ngValue(value) {
6832
+ if (this._select == null)
6833
+ return;
6834
+ this._value = value;
6835
+ this._setElementValue(_buildValueString(this.id, value));
6836
+ this._select.writeValue(this._select.value);
6837
+ }
6838
+ /**
6839
+ * @description
6840
+ * Tracks simple string values bound to the option element.
6841
+ * For objects, use the `ngValue` input binding.
6842
+ */
6843
+ set value(value) {
6844
+ if (this._select) {
6845
+ this._value = value;
6846
+ this._setElementValue(_buildValueString(this.id, value));
6847
+ this._select.writeValue(this._select.value);
6848
+ }
6849
+ else {
6850
+ this._setElementValue(value);
6851
+ }
6852
+ }
6725
6853
  /** @internal */
6726
- normalizeInput = (input) => toInteger(input);
6854
+ _setElementValue(value) {
6855
+ this._renderer.setProperty(this._element.nativeElement, 'value', value);
6856
+ }
6727
6857
  /** @internal */
6728
- createValidator = (minlength) => minLengthValidator(minlength);
6729
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MinLengthValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6730
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: MinLengthValidator, isStandalone: false, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: { minlength: "minlength" }, host: { properties: { "attr.minlength": "_enabled ? minlength : null" } }, providers: [MIN_LENGTH_VALIDATOR], usesInheritance: true, ngImport: i0 });
6858
+ _setSelected(selected) {
6859
+ this._renderer.setProperty(this._element.nativeElement, 'selected', selected);
6860
+ }
6861
+ /** @docs-private */
6862
+ ngOnDestroy() {
6863
+ if (this._select) {
6864
+ this._select._optionMap.delete(this.id);
6865
+ this._select.writeValue(this._select.value);
6866
+ }
6867
+ }
6868
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵNgSelectMultipleOption, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: SelectMultipleControlValueAccessor, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
6869
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: ɵNgSelectMultipleOption, isStandalone: false, selector: "option", inputs: { ngValue: "ngValue", value: "value" }, ngImport: i0 });
6731
6870
  }
6732
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MinLengthValidator, decorators: [{
6871
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵNgSelectMultipleOption, decorators: [{
6733
6872
  type: Directive,
6734
6873
  args: [{
6735
- selector: '[minlength][formControlName],[minlength][formControl],[minlength][ngModel]',
6736
- providers: [MIN_LENGTH_VALIDATOR],
6737
- host: { '[attr.minlength]': '_enabled ? minlength : null' },
6874
+ selector: 'option',
6738
6875
  standalone: false,
6739
6876
  }]
6740
- }], propDecorators: { minlength: [{
6741
- type: Input
6877
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: SelectMultipleControlValueAccessor, decorators: [{
6878
+ type: Optional
6879
+ }, {
6880
+ type: Host
6881
+ }] }], propDecorators: { ngValue: [{
6882
+ type: Input,
6883
+ args: ['ngValue']
6884
+ }], value: [{
6885
+ type: Input,
6886
+ args: ['value']
6742
6887
  }] } });
6888
+
6889
+ /**
6890
+ * Method that updates string to integer if not already a number
6891
+ *
6892
+ * @param value The value to convert to integer.
6893
+ * @returns value of parameter converted to number or integer.
6894
+ */
6895
+ function toInteger(value) {
6896
+ return typeof value === 'number' ? value : parseInt(value, 10);
6897
+ }
6898
+ /**
6899
+ * Method that ensures that provided value is a float (and converts it to float if needed).
6900
+ *
6901
+ * @param value The value to convert to float.
6902
+ * @returns value of parameter converted to number or float.
6903
+ */
6904
+ function toFloat(value) {
6905
+ return typeof value === 'number' ? value : parseFloat(value);
6906
+ }
6907
+ /**
6908
+ * A base class for Validator-based Directives. The class contains common logic shared across such
6909
+ * Directives.
6910
+ *
6911
+ * For internal use only, this class is not intended for use outside of the Forms package.
6912
+ */
6913
+ class AbstractValidatorDirective {
6914
+ _validator = nullValidator;
6915
+ _onChange;
6916
+ /**
6917
+ * A flag that tracks whether this validator is enabled.
6918
+ *
6919
+ * Marking it `internal` (vs `protected`), so that this flag can be used in host bindings of
6920
+ * directive classes that extend this base class.
6921
+ * @internal
6922
+ */
6923
+ _enabled;
6924
+ /** @docs-private */
6925
+ ngOnChanges(changes) {
6926
+ if (this.inputName in changes) {
6927
+ const input = this.normalizeInput(changes[this.inputName].currentValue);
6928
+ this._enabled = this.enabled(input);
6929
+ this._validator = this._enabled ? this.createValidator(input) : nullValidator;
6930
+ if (this._onChange) {
6931
+ this._onChange();
6932
+ }
6933
+ }
6934
+ }
6935
+ /** @docs-private */
6936
+ validate(control) {
6937
+ return this._validator(control);
6938
+ }
6939
+ /** @docs-private */
6940
+ registerOnValidatorChange(fn) {
6941
+ this._onChange = fn;
6942
+ }
6943
+ /**
6944
+ * @description
6945
+ * Determines whether this validator should be active or not based on an input.
6946
+ * Base class implementation checks whether an input is defined (if the value is different from
6947
+ * `null` and `undefined`). Validator classes that extend this base class can override this
6948
+ * function with the logic specific to a particular validator directive.
6949
+ */
6950
+ enabled(input) {
6951
+ return input != null /* both `null` and `undefined` */;
6952
+ }
6953
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: AbstractValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
6954
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: AbstractValidatorDirective, isStandalone: true, usesOnChanges: true, ngImport: i0 });
6955
+ }
6956
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: AbstractValidatorDirective, decorators: [{
6957
+ type: Directive
6958
+ }] });
6743
6959
  /**
6744
6960
  * @description
6745
- * Provider which adds `MaxLengthValidator` to the `NG_VALIDATORS` multi-provider list.
6961
+ * Provider which adds `MaxValidator` to the `NG_VALIDATORS` multi-provider list.
6746
6962
  */
6747
- const MAX_LENGTH_VALIDATOR = {
6963
+ const MAX_VALIDATOR = {
6748
6964
  provide: NG_VALIDATORS,
6749
- useExisting: forwardRef(() => MaxLengthValidator),
6965
+ useExisting: forwardRef(() => MaxValidator),
6750
6966
  multi: true,
6751
6967
  };
6752
6968
  /**
6753
- * A directive that adds maximum length validation to controls marked with the
6754
- * `maxlength` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
6969
+ * A directive which installs the {@link MaxValidator} for any `formControlName`,
6970
+ * `formControl`, or control with `ngModel` that also has a `max` attribute.
6755
6971
  *
6756
6972
  * @see [Form Validation](guide/forms/form-validation)
6757
6973
  *
6758
6974
  * @usageNotes
6759
6975
  *
6760
- * ### Adding a maximum length validator
6976
+ * ### Adding a max validator
6761
6977
  *
6762
- * The following example shows how to add a maximum length validator to an input attached to an
6978
+ * The following example shows how to add a max validator to an input attached to an
6763
6979
  * ngModel binding.
6764
6980
  *
6765
6981
  * ```html
6766
- * <input name="firstName" ngModel maxlength="25">
6982
+ * <input type="number" ngModel max="4">
6767
6983
  * ```
6768
6984
  *
6769
6985
  * @ngModule ReactiveFormsModule
6770
6986
  * @ngModule FormsModule
6771
6987
  * @publicApi
6772
6988
  */
6773
- class MaxLengthValidator extends AbstractValidatorDirective {
6989
+ class MaxValidator extends AbstractValidatorDirective {
6774
6990
  /**
6775
6991
  * @description
6776
- * Tracks changes to the maximum length bound to this directive.
6992
+ * Tracks changes to the max bound to this directive.
6777
6993
  */
6778
- maxlength;
6994
+ max;
6779
6995
  /** @internal */
6780
- inputName = 'maxlength';
6996
+ inputName = 'max';
6781
6997
  /** @internal */
6782
- normalizeInput = (input) => toInteger(input);
6998
+ normalizeInput = (input) => toFloat(input);
6783
6999
  /** @internal */
6784
- createValidator = (maxlength) => maxLengthValidator(maxlength);
6785
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MaxLengthValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6786
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: MaxLengthValidator, isStandalone: false, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: { maxlength: "maxlength" }, host: { properties: { "attr.maxlength": "_enabled ? maxlength : null" } }, providers: [MAX_LENGTH_VALIDATOR], usesInheritance: true, ngImport: i0 });
7000
+ createValidator = (max) => maxValidator(max);
7001
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MaxValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
7002
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: MaxValidator, isStandalone: false, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: { max: "max" }, host: { properties: { "attr.max": "_enabled ? max : null" } }, providers: [MAX_VALIDATOR], usesInheritance: true, ngImport: i0 });
6787
7003
  }
6788
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MaxLengthValidator, decorators: [{
7004
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MaxValidator, decorators: [{
6789
7005
  type: Directive,
6790
7006
  args: [{
6791
- selector: '[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]',
6792
- providers: [MAX_LENGTH_VALIDATOR],
6793
- host: { '[attr.maxlength]': '_enabled ? maxlength : null' },
7007
+ selector: 'input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]',
7008
+ providers: [MAX_VALIDATOR],
7009
+ host: { '[attr.max]': '_enabled ? max : null' },
6794
7010
  standalone: false,
6795
7011
  }]
6796
- }], propDecorators: { maxlength: [{
7012
+ }], propDecorators: { max: [{
6797
7013
  type: Input
6798
7014
  }] } });
6799
7015
  /**
6800
7016
  * @description
6801
- * Provider which adds `PatternValidator` to the `NG_VALIDATORS` multi-provider list.
7017
+ * Provider which adds `MinValidator` to the `NG_VALIDATORS` multi-provider list.
6802
7018
  */
6803
- const PATTERN_VALIDATOR = {
7019
+ const MIN_VALIDATOR = {
6804
7020
  provide: NG_VALIDATORS,
6805
- useExisting: forwardRef(() => PatternValidator),
7021
+ useExisting: forwardRef(() => MinValidator),
6806
7022
  multi: true,
6807
7023
  };
6808
7024
  /**
6809
- * @description
6810
- * A directive that adds regex pattern validation to controls marked with the
6811
- * `pattern` attribute. The regex must match the entire control value.
6812
- * The directive is provided with the `NG_VALIDATORS` multi-provider list.
7025
+ * A directive which installs the {@link MinValidator} for any `formControlName`,
7026
+ * `formControl`, or control with `ngModel` that also has a `min` attribute.
6813
7027
  *
6814
7028
  * @see [Form Validation](guide/forms/form-validation)
6815
7029
  *
6816
7030
  * @usageNotes
6817
7031
  *
6818
- * ### Adding a pattern validator
7032
+ * ### Adding a min validator
6819
7033
  *
6820
- * The following example shows how to add a pattern validator to an input attached to an
7034
+ * The following example shows how to add a min validator to an input attached to an
6821
7035
  * ngModel binding.
6822
7036
  *
6823
7037
  * ```html
6824
- * <input name="firstName" ngModel pattern="[a-zA-Z ]*">
7038
+ * <input type="number" ngModel min="4">
6825
7039
  * ```
6826
7040
  *
6827
7041
  * @ngModule ReactiveFormsModule
6828
7042
  * @ngModule FormsModule
6829
7043
  * @publicApi
6830
7044
  */
6831
- class PatternValidator extends AbstractValidatorDirective {
7045
+ class MinValidator extends AbstractValidatorDirective {
6832
7046
  /**
6833
7047
  * @description
6834
- * Tracks changes to the pattern bound to this directive.
7048
+ * Tracks changes to the min bound to this directive.
6835
7049
  */
6836
- pattern; // This input is always defined, since the name matches selector.
7050
+ min;
6837
7051
  /** @internal */
6838
- inputName = 'pattern';
7052
+ inputName = 'min';
6839
7053
  /** @internal */
6840
- normalizeInput = (input) => input;
7054
+ normalizeInput = (input) => toFloat(input);
6841
7055
  /** @internal */
6842
- createValidator = (input) => patternValidator(input);
6843
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: PatternValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
6844
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: PatternValidator, isStandalone: false, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: { pattern: "pattern" }, host: { properties: { "attr.pattern": "_enabled ? pattern : null" } }, providers: [PATTERN_VALIDATOR], usesInheritance: true, ngImport: i0 });
7056
+ createValidator = (min) => minValidator(min);
7057
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MinValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
7058
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: MinValidator, isStandalone: false, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: { min: "min" }, host: { properties: { "attr.min": "_enabled ? min : null" } }, providers: [MIN_VALIDATOR], usesInheritance: true, ngImport: i0 });
6845
7059
  }
6846
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: PatternValidator, decorators: [{
7060
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MinValidator, decorators: [{
6847
7061
  type: Directive,
6848
7062
  args: [{
6849
- selector: '[pattern][formControlName],[pattern][formControl],[pattern][ngModel]',
6850
- providers: [PATTERN_VALIDATOR],
6851
- host: { '[attr.pattern]': '_enabled ? pattern : null' },
7063
+ selector: 'input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]',
7064
+ providers: [MIN_VALIDATOR],
7065
+ host: { '[attr.min]': '_enabled ? min : null' },
6852
7066
  standalone: false,
6853
7067
  }]
6854
- }], propDecorators: { pattern: [{
7068
+ }], propDecorators: { min: [{
6855
7069
  type: Input
6856
7070
  }] } });
6857
-
6858
- const SHARED_FORM_DIRECTIVES = [
6859
- ɵNgNoValidate,
6860
- NgSelectOption,
6861
- ɵNgSelectMultipleOption,
6862
- DefaultValueAccessor,
6863
- NumberValueAccessor,
6864
- RangeValueAccessor,
6865
- CheckboxControlValueAccessor,
6866
- SelectControlValueAccessor,
6867
- SelectMultipleControlValueAccessor,
6868
- RadioControlValueAccessor,
6869
- NgControlStatus,
6870
- NgControlStatusGroup,
6871
- RequiredValidator,
6872
- MinLengthValidator,
6873
- MaxLengthValidator,
6874
- PatternValidator,
6875
- CheckboxRequiredValidator,
6876
- EmailValidator,
6877
- MinValidator,
6878
- MaxValidator,
6879
- ];
6880
- const TEMPLATE_DRIVEN_DIRECTIVES = [NgModel, NgModelGroup, NgForm];
6881
- const REACTIVE_DRIVEN_DIRECTIVES = [
6882
- FormControlDirective,
6883
- FormGroupDirective,
6884
- FormControlName,
6885
- FormGroupName,
6886
- FormArrayName,
6887
- ];
6888
7071
  /**
6889
- * Internal module used for sharing directives between FormsModule and ReactiveFormsModule
7072
+ * @description
7073
+ * Provider which adds `RequiredValidator` to the `NG_VALIDATORS` multi-provider list.
6890
7074
  */
6891
- class ɵInternalFormsSharedModule {
6892
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵInternalFormsSharedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
6893
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵInternalFormsSharedModule, declarations: [ɵNgNoValidate,
6894
- NgSelectOption,
6895
- ɵNgSelectMultipleOption,
6896
- DefaultValueAccessor,
6897
- NumberValueAccessor,
6898
- RangeValueAccessor,
6899
- CheckboxControlValueAccessor,
6900
- SelectControlValueAccessor,
6901
- SelectMultipleControlValueAccessor,
6902
- RadioControlValueAccessor,
6903
- NgControlStatus,
6904
- NgControlStatusGroup,
6905
- RequiredValidator,
6906
- MinLengthValidator,
6907
- MaxLengthValidator,
6908
- PatternValidator,
6909
- CheckboxRequiredValidator,
6910
- EmailValidator,
6911
- MinValidator,
6912
- MaxValidator], exports: [ɵNgNoValidate,
6913
- NgSelectOption,
6914
- ɵNgSelectMultipleOption,
6915
- DefaultValueAccessor,
6916
- NumberValueAccessor,
6917
- RangeValueAccessor,
6918
- CheckboxControlValueAccessor,
6919
- SelectControlValueAccessor,
6920
- SelectMultipleControlValueAccessor,
6921
- RadioControlValueAccessor,
6922
- NgControlStatus,
6923
- NgControlStatusGroup,
6924
- RequiredValidator,
6925
- MinLengthValidator,
6926
- MaxLengthValidator,
6927
- PatternValidator,
6928
- CheckboxRequiredValidator,
6929
- EmailValidator,
6930
- MinValidator,
6931
- MaxValidator] });
6932
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵInternalFormsSharedModule });
6933
- }
6934
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵInternalFormsSharedModule, decorators: [{
6935
- type: NgModule,
6936
- args: [{
6937
- declarations: SHARED_FORM_DIRECTIVES,
6938
- exports: SHARED_FORM_DIRECTIVES,
6939
- }]
6940
- }] });
6941
-
6942
- /**
6943
- * Tracks the value and validity state of an array of `FormControl`,
6944
- * `FormGroup` or `FormArray` instances.
7075
+ const REQUIRED_VALIDATOR = {
7076
+ provide: NG_VALIDATORS,
7077
+ useExisting: forwardRef(() => RequiredValidator),
7078
+ multi: true,
7079
+ };
7080
+ /**
7081
+ * @description
7082
+ * Provider which adds `CheckboxRequiredValidator` to the `NG_VALIDATORS` multi-provider list.
7083
+ */
7084
+ const CHECKBOX_REQUIRED_VALIDATOR = {
7085
+ provide: NG_VALIDATORS,
7086
+ useExisting: forwardRef(() => CheckboxRequiredValidator),
7087
+ multi: true,
7088
+ };
7089
+ /**
7090
+ * @description
7091
+ * A directive that adds the `required` validator to any controls marked with the
7092
+ * `required` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
6945
7093
  *
6946
- * A `FormArray` aggregates the values of each child `FormControl` into an array.
6947
- * It calculates its status by reducing the status values of its children. For example, if one of
6948
- * the controls in a `FormArray` is invalid, the entire array becomes invalid.
7094
+ * @see [Form Validation](guide/forms/form-validation)
6949
7095
  *
6950
- * `FormArray` accepts one generic argument, which is the type of the controls inside.
6951
- * If you need a heterogenous array, use {@link UntypedFormArray}.
7096
+ * @usageNotes
6952
7097
  *
6953
- * `FormArray` is one of the four fundamental building blocks used to define forms in Angular,
6954
- * along with `FormControl`, `FormGroup`, and `FormRecord`.
7098
+ * ### Adding a required validator using template-driven forms
7099
+ *
7100
+ * ```html
7101
+ * <input name="fullName" ngModel required>
7102
+ * ```
7103
+ *
7104
+ * @ngModule FormsModule
7105
+ * @ngModule ReactiveFormsModule
7106
+ * @publicApi
7107
+ */
7108
+ class RequiredValidator extends AbstractValidatorDirective {
7109
+ /**
7110
+ * @description
7111
+ * Tracks changes to the required attribute bound to this directive.
7112
+ */
7113
+ required;
7114
+ /** @internal */
7115
+ inputName = 'required';
7116
+ /** @internal */
7117
+ normalizeInput = booleanAttribute;
7118
+ /** @internal */
7119
+ createValidator = (input) => requiredValidator;
7120
+ /** @docs-private */
7121
+ enabled(input) {
7122
+ return input;
7123
+ }
7124
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: RequiredValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
7125
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: RequiredValidator, isStandalone: false, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: { required: "required" }, host: { properties: { "attr.required": "_enabled ? \"\" : null" } }, providers: [REQUIRED_VALIDATOR], usesInheritance: true, ngImport: i0 });
7126
+ }
7127
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: RequiredValidator, decorators: [{
7128
+ type: Directive,
7129
+ args: [{
7130
+ selector: ':not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]',
7131
+ providers: [REQUIRED_VALIDATOR],
7132
+ host: { '[attr.required]': '_enabled ? "" : null' },
7133
+ standalone: false,
7134
+ }]
7135
+ }], propDecorators: { required: [{
7136
+ type: Input
7137
+ }] } });
7138
+ /**
7139
+ * A Directive that adds the `required` validator to checkbox controls marked with the
7140
+ * `required` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
7141
+ *
7142
+ * @see [Form Validation](guide/forms/form-validation)
6955
7143
  *
6956
7144
  * @usageNotes
6957
7145
  *
6958
- * ### Create an array of form controls
7146
+ * ### Adding a required checkbox validator using template-driven forms
6959
7147
  *
6960
- * ```ts
6961
- * const arr = new FormArray([
6962
- * new FormControl('Nancy', Validators.minLength(2)),
6963
- * new FormControl('Drew'),
6964
- * ]);
7148
+ * The following example shows how to add a checkbox required validator to an input attached to an
7149
+ * ngModel binding.
6965
7150
  *
6966
- * console.log(arr.value); // ['Nancy', 'Drew']
6967
- * console.log(arr.status); // 'VALID'
7151
+ * ```html
7152
+ * <input type="checkbox" name="active" ngModel required>
6968
7153
  * ```
6969
7154
  *
6970
- * ### Create a form array with array-level validators
7155
+ * @publicApi
7156
+ * @ngModule FormsModule
7157
+ * @ngModule ReactiveFormsModule
7158
+ */
7159
+ class CheckboxRequiredValidator extends RequiredValidator {
7160
+ /** @internal */
7161
+ createValidator = (input) => requiredTrueValidator;
7162
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: CheckboxRequiredValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
7163
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: CheckboxRequiredValidator, isStandalone: false, selector: "input[type=checkbox][required][formControlName],input[type=checkbox][required][formControl],input[type=checkbox][required][ngModel]", host: { properties: { "attr.required": "_enabled ? \"\" : null" } }, providers: [CHECKBOX_REQUIRED_VALIDATOR], usesInheritance: true, ngImport: i0 });
7164
+ }
7165
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: CheckboxRequiredValidator, decorators: [{
7166
+ type: Directive,
7167
+ args: [{
7168
+ selector: 'input[type=checkbox][required][formControlName],input[type=checkbox][required][formControl],input[type=checkbox][required][ngModel]',
7169
+ providers: [CHECKBOX_REQUIRED_VALIDATOR],
7170
+ host: { '[attr.required]': '_enabled ? "" : null' },
7171
+ standalone: false,
7172
+ }]
7173
+ }] });
7174
+ /**
7175
+ * @description
7176
+ * Provider which adds `EmailValidator` to the `NG_VALIDATORS` multi-provider list.
7177
+ */
7178
+ const EMAIL_VALIDATOR = {
7179
+ provide: NG_VALIDATORS,
7180
+ useExisting: forwardRef(() => EmailValidator),
7181
+ multi: true,
7182
+ };
7183
+ /**
7184
+ * A directive that adds the `email` validator to controls marked with the
7185
+ * `email` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
6971
7186
  *
6972
- * You include array-level validators and async validators. These come in handy
6973
- * when you want to perform validation that considers the value of more than one child
6974
- * control.
7187
+ * The email validation is based on the WHATWG HTML specification with some enhancements to
7188
+ * incorporate more RFC rules. More information can be found on the [Validators.email
7189
+ * page](api/forms/Validators#email).
6975
7190
  *
6976
- * The two types of validators are passed in separately as the second and third arg
6977
- * respectively, or together as part of an options object.
7191
+ * @see [Form Validation](guide/forms/form-validation)
6978
7192
  *
6979
- * ```ts
6980
- * const arr = new FormArray([
6981
- * new FormControl('Nancy'),
6982
- * new FormControl('Drew')
6983
- * ], {validators: myValidator, asyncValidators: myAsyncValidator});
6984
- * ```
7193
+ * @usageNotes
6985
7194
  *
6986
- * ### Set the updateOn property for all controls in a form array
7195
+ * ### Adding an email validator
6987
7196
  *
6988
- * The options object is used to set a default value for each child
6989
- * control's `updateOn` property. If you set `updateOn` to `'blur'` at the
6990
- * array level, all child controls default to 'blur', unless the child
6991
- * has explicitly specified a different `updateOn` value.
7197
+ * The following example shows how to add an email validator to an input attached to an ngModel
7198
+ * binding.
6992
7199
  *
6993
- * ```ts
6994
- * const arr = new FormArray([
6995
- * new FormControl()
6996
- * ], {updateOn: 'blur'});
7200
+ * ```html
7201
+ * <input type="email" name="email" ngModel email>
7202
+ * <input type="email" name="email" ngModel email="true">
7203
+ * <input type="email" name="email" ngModel [email]="true">
6997
7204
  * ```
6998
7205
  *
6999
- * ### Adding or removing controls from a form array
7000
- *
7001
- * To change the controls in the array, use the `push`, `insert`, `removeAt` or `clear` methods
7002
- * in `FormArray` itself. These methods ensure the controls are properly tracked in the
7003
- * form's hierarchy. Do not modify the array of `AbstractControl`s used to instantiate
7004
- * the `FormArray` directly, as that result in strange and unexpected behavior such
7005
- * as broken change detection.
7006
- *
7007
7206
  * @publicApi
7207
+ * @ngModule FormsModule
7208
+ * @ngModule ReactiveFormsModule
7008
7209
  */
7009
- class FormArray extends AbstractControl {
7010
- /**
7011
- * Creates a new `FormArray` instance.
7012
- *
7013
- * @param controls An array of child controls. Each child control is given an index
7014
- * where it is registered.
7015
- *
7016
- * @param validatorOrOpts A synchronous validator function, or an array of
7017
- * such functions, or an `AbstractControlOptions` object that contains validation functions
7018
- * and a validation trigger.
7019
- *
7020
- * @param asyncValidator A single async validator or array of async validator functions
7021
- *
7022
- */
7023
- constructor(controls, validatorOrOpts, asyncValidator) {
7024
- super(pickValidators(validatorOrOpts), pickAsyncValidators(asyncValidator, validatorOrOpts));
7025
- this.controls = controls;
7026
- this._initObservables();
7027
- this._setUpdateStrategy(validatorOrOpts);
7028
- this._setUpControls();
7029
- this.updateValueAndValidity({
7030
- onlySelf: true,
7031
- // If `asyncValidator` is present, it will trigger control status change from `PENDING` to
7032
- // `VALID` or `INVALID`.
7033
- // The status should be broadcasted via the `statusChanges` observable, so we set `emitEvent`
7034
- // to `true` to allow that during the control creation process.
7035
- emitEvent: !!this.asyncValidator,
7036
- });
7037
- }
7038
- controls;
7039
- /**
7040
- * Get the `AbstractControl` at the given `index` in the array.
7041
- *
7042
- * @param index Index in the array to retrieve the control. If `index` is negative, it will wrap
7043
- * around from the back, and if index is greatly negative (less than `-length`), the result is
7044
- * undefined. This behavior is the same as `Array.at(index)`.
7045
- */
7046
- at(index) {
7047
- return this.controls[this._adjustIndex(index)];
7048
- }
7049
- /**
7050
- * Insert a new `AbstractControl` at the end of the array.
7051
- *
7052
- * @param control Form control to be inserted
7053
- * @param options Specifies whether this FormArray instance should emit events after a new
7054
- * control is added.
7055
- * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
7056
- * `valueChanges` observables emit events with the latest status and value when the control is
7057
- * inserted. When false, no events are emitted.
7058
- */
7059
- push(control, options = {}) {
7060
- if (Array.isArray(control)) {
7061
- control.forEach((ctrl) => {
7062
- this.controls.push(ctrl);
7063
- this._registerControl(ctrl);
7064
- });
7065
- }
7066
- else {
7067
- this.controls.push(control);
7068
- this._registerControl(control);
7069
- }
7070
- this.updateValueAndValidity({ emitEvent: options.emitEvent });
7071
- this._onCollectionChange();
7072
- }
7210
+ class EmailValidator extends AbstractValidatorDirective {
7073
7211
  /**
7074
- * Insert a new `AbstractControl` at the given `index` in the array.
7075
- *
7076
- * @param index Index in the array to insert the control. If `index` is negative, wraps around
7077
- * from the back. If `index` is greatly negative (less than `-length`), prepends to the array.
7078
- * This behavior is the same as `Array.splice(index, 0, control)`.
7079
- * @param control Form control to be inserted
7080
- * @param options Specifies whether this FormArray instance should emit events after a new
7081
- * control is inserted.
7082
- * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
7083
- * `valueChanges` observables emit events with the latest status and value when the control is
7084
- * inserted. When false, no events are emitted.
7212
+ * @description
7213
+ * Tracks changes to the email attribute bound to this directive.
7085
7214
  */
7086
- insert(index, control, options = {}) {
7087
- this.controls.splice(index, 0, control);
7088
- this._registerControl(control);
7089
- this.updateValueAndValidity({ emitEvent: options.emitEvent });
7215
+ email;
7216
+ /** @internal */
7217
+ inputName = 'email';
7218
+ /** @internal */
7219
+ normalizeInput = booleanAttribute;
7220
+ /** @internal */
7221
+ createValidator = (input) => emailValidator;
7222
+ /** @docs-private */
7223
+ enabled(input) {
7224
+ return input;
7090
7225
  }
7091
- /**
7092
- * Remove the control at the given `index` in the array.
7093
- *
7094
- * @param index Index in the array to remove the control. If `index` is negative, wraps around
7095
- * from the back. If `index` is greatly negative (less than `-length`), removes the first
7096
- * element. This behavior is the same as `Array.splice(index, 1)`.
7097
- * @param options Specifies whether this FormArray instance should emit events after a
7098
- * control is removed.
7099
- * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
7100
- * `valueChanges` observables emit events with the latest status and value when the control is
7101
- * removed. When false, no events are emitted.
7102
- */
7103
- removeAt(index, options = {}) {
7104
- // Adjust the index, then clamp it at no less than 0 to prevent undesired underflows.
7105
- let adjustedIndex = this._adjustIndex(index);
7106
- if (adjustedIndex < 0)
7107
- adjustedIndex = 0;
7108
- if (this.controls[adjustedIndex])
7109
- this.controls[adjustedIndex]._registerOnCollectionChange(() => { });
7110
- this.controls.splice(adjustedIndex, 1);
7111
- this.updateValueAndValidity({ emitEvent: options.emitEvent });
7112
- }
7113
- /**
7114
- * Replace an existing control.
7115
- *
7116
- * @param index Index in the array to replace the control. If `index` is negative, wraps around
7117
- * from the back. If `index` is greatly negative (less than `-length`), replaces the first
7118
- * element. This behavior is the same as `Array.splice(index, 1, control)`.
7119
- * @param control The `AbstractControl` control to replace the existing control
7120
- * @param options Specifies whether this FormArray instance should emit events after an
7121
- * existing control is replaced with a new one.
7122
- * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
7123
- * `valueChanges` observables emit events with the latest status and value when the control is
7124
- * replaced with a new one. When false, no events are emitted.
7125
- */
7126
- setControl(index, control, options = {}) {
7127
- // Adjust the index, then clamp it at no less than 0 to prevent undesired underflows.
7128
- let adjustedIndex = this._adjustIndex(index);
7129
- if (adjustedIndex < 0)
7130
- adjustedIndex = 0;
7131
- if (this.controls[adjustedIndex])
7132
- this.controls[adjustedIndex]._registerOnCollectionChange(() => { });
7133
- this.controls.splice(adjustedIndex, 1);
7134
- if (control) {
7135
- this.controls.splice(adjustedIndex, 0, control);
7136
- this._registerControl(control);
7137
- }
7138
- this.updateValueAndValidity({ emitEvent: options.emitEvent });
7139
- this._onCollectionChange();
7140
- }
7141
- /**
7142
- * Length of the control array.
7143
- */
7144
- get length() {
7145
- return this.controls.length;
7146
- }
7147
- /**
7148
- * Sets the value of the `FormArray`. It accepts an array that matches
7149
- * the structure of the control.
7150
- *
7151
- * This method performs strict checks, and throws an error if you try
7152
- * to set the value of a control that doesn't exist or if you exclude the
7153
- * value of a control.
7154
- *
7155
- * @usageNotes
7156
- * ### Set the values for the controls in the form array
7157
- *
7158
- * ```ts
7159
- * const arr = new FormArray([
7160
- * new FormControl(),
7161
- * new FormControl()
7162
- * ]);
7163
- * console.log(arr.value); // [null, null]
7164
- *
7165
- * arr.setValue(['Nancy', 'Drew']);
7166
- * console.log(arr.value); // ['Nancy', 'Drew']
7167
- * ```
7168
- *
7169
- * @param value Array of values for the controls
7170
- * @param options Configure options that determine how the control propagates changes and
7171
- * emits events after the value changes
7172
- *
7173
- * * `onlySelf`: When true, each change only affects this control, and not its parent. Default
7174
- * is false.
7175
- * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
7176
- * `valueChanges`
7177
- * observables emit events with the latest status and value when the control value is updated.
7178
- * When false, no events are emitted.
7179
- * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
7180
- * updateValueAndValidity} method.
7181
- */
7182
- setValue(value, options = {}) {
7183
- assertAllValuesPresent(this, false, value);
7184
- value.forEach((newValue, index) => {
7185
- assertControlPresent(this, false, index);
7186
- this.at(index).setValue(newValue, { onlySelf: true, emitEvent: options.emitEvent });
7187
- });
7188
- this.updateValueAndValidity(options);
7189
- }
7190
- /**
7191
- * Patches the value of the `FormArray`. It accepts an array that matches the
7192
- * structure of the control, and does its best to match the values to the correct
7193
- * controls in the group.
7194
- *
7195
- * It accepts both super-sets and sub-sets of the array without throwing an error.
7196
- *
7197
- * @usageNotes
7198
- * ### Patch the values for controls in a form array
7199
- *
7200
- * ```ts
7201
- * const arr = new FormArray([
7202
- * new FormControl(),
7203
- * new FormControl()
7204
- * ]);
7205
- * console.log(arr.value); // [null, null]
7206
- *
7207
- * arr.patchValue(['Nancy']);
7208
- * console.log(arr.value); // ['Nancy', null]
7209
- * ```
7210
- *
7211
- * @param value Array of latest values for the controls
7212
- * @param options Configure options that determine how the control propagates changes and
7213
- * emits events after the value changes
7214
- *
7215
- * * `onlySelf`: When true, each change only affects this control, and not its parent. Default
7216
- * is false.
7217
- * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
7218
- * `valueChanges` observables emit events with the latest status and value when the control
7219
- * value is updated. When false, no events are emitted. The configuration options are passed to
7220
- * the {@link AbstractControl#updateValueAndValidity updateValueAndValidity} method.
7221
- */
7222
- patchValue(value, options = {}) {
7223
- // Even though the `value` argument type doesn't allow `null` and `undefined` values, the
7224
- // `patchValue` can be called recursively and inner data structures might have these values,
7225
- // so we just ignore such cases when a field containing FormArray instance receives `null` or
7226
- // `undefined` as a value.
7227
- if (value == null /* both `null` and `undefined` */)
7228
- return;
7229
- value.forEach((newValue, index) => {
7230
- if (this.at(index)) {
7231
- this.at(index).patchValue(newValue, { onlySelf: true, emitEvent: options.emitEvent });
7232
- }
7233
- });
7234
- this.updateValueAndValidity(options);
7235
- }
7236
- /**
7237
- * Resets the `FormArray` and all descendants are marked `pristine` and `untouched`, and the
7238
- * value of all descendants to null or null maps.
7239
- *
7240
- * You reset to a specific form state by passing in an array of states
7241
- * that matches the structure of the control. The state is a standalone value
7242
- * or a form state object with both a value and a disabled status.
7243
- *
7244
- * @usageNotes
7245
- * ### Reset the values in a form array
7246
- *
7247
- * ```ts
7248
- * const arr = new FormArray([
7249
- * new FormControl(),
7250
- * new FormControl()
7251
- * ]);
7252
- * arr.reset(['name', 'last name']);
7253
- *
7254
- * console.log(arr.value); // ['name', 'last name']
7255
- * ```
7256
- *
7257
- * ### Reset the values in a form array and the disabled status for the first control
7258
- *
7259
- * ```ts
7260
- * arr.reset([
7261
- * {value: 'name', disabled: true},
7262
- * 'last'
7263
- * ]);
7264
- *
7265
- * console.log(arr.value); // ['last']
7266
- * console.log(arr.at(0).status); // 'DISABLED'
7267
- * ```
7268
- *
7269
- * @param value Array of values for the controls
7270
- * @param options Configure options that determine how the control propagates changes and
7271
- * emits events after the value changes
7272
- *
7273
- * * `onlySelf`: When true, each change only affects this control, and not its parent. Default
7274
- * is false.
7275
- * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
7276
- * `valueChanges`
7277
- * observables emit events with the latest status and value when the control is reset.
7278
- * When false, no events are emitted.
7279
- * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
7280
- * updateValueAndValidity} method.
7281
- */
7282
- reset(value = [], options = {}) {
7283
- this._forEachChild((control, index) => {
7284
- control.reset(value[index], { onlySelf: true, emitEvent: options.emitEvent });
7285
- });
7286
- this._updatePristine(options, this);
7287
- this._updateTouched(options, this);
7288
- this.updateValueAndValidity(options);
7289
- }
7290
- /**
7291
- * The aggregate value of the array, including any disabled controls.
7292
- *
7293
- * Reports all values regardless of disabled status.
7294
- */
7295
- getRawValue() {
7296
- return this.controls.map((control) => control.getRawValue());
7297
- }
7298
- /**
7299
- * Remove all controls in the `FormArray`.
7300
- *
7301
- * @param options Specifies whether this FormArray instance should emit events after all
7302
- * controls are removed.
7303
- * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
7304
- * `valueChanges` observables emit events with the latest status and value when all controls
7305
- * in this FormArray instance are removed. When false, no events are emitted.
7306
- *
7307
- * @usageNotes
7308
- * ### Remove all elements from a FormArray
7309
- *
7310
- * ```ts
7311
- * const arr = new FormArray([
7312
- * new FormControl(),
7313
- * new FormControl()
7314
- * ]);
7315
- * console.log(arr.length); // 2
7316
- *
7317
- * arr.clear();
7318
- * console.log(arr.length); // 0
7319
- * ```
7320
- *
7321
- * It's a simpler and more efficient alternative to removing all elements one by one:
7322
- *
7323
- * ```ts
7324
- * const arr = new FormArray([
7325
- * new FormControl(),
7326
- * new FormControl()
7327
- * ]);
7328
- *
7329
- * while (arr.length) {
7330
- * arr.removeAt(0);
7331
- * }
7332
- * ```
7333
- */
7334
- clear(options = {}) {
7335
- if (this.controls.length < 1)
7336
- return;
7337
- this._forEachChild((control) => control._registerOnCollectionChange(() => { }));
7338
- this.controls.splice(0);
7339
- this.updateValueAndValidity({ emitEvent: options.emitEvent });
7340
- }
7341
- /**
7342
- * Adjusts a negative index by summing it with the length of the array. For very negative
7343
- * indices, the result may remain negative.
7344
- * @internal
7226
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: EmailValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
7227
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: EmailValidator, isStandalone: false, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: { email: "email" }, providers: [EMAIL_VALIDATOR], usesInheritance: true, ngImport: i0 });
7228
+ }
7229
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: EmailValidator, decorators: [{
7230
+ type: Directive,
7231
+ args: [{
7232
+ selector: '[email][formControlName],[email][formControl],[email][ngModel]',
7233
+ providers: [EMAIL_VALIDATOR],
7234
+ standalone: false,
7235
+ }]
7236
+ }], propDecorators: { email: [{
7237
+ type: Input
7238
+ }] } });
7239
+ /**
7240
+ * @description
7241
+ * Provider which adds `MinLengthValidator` to the `NG_VALIDATORS` multi-provider list.
7242
+ */
7243
+ const MIN_LENGTH_VALIDATOR = {
7244
+ provide: NG_VALIDATORS,
7245
+ useExisting: forwardRef(() => MinLengthValidator),
7246
+ multi: true,
7247
+ };
7248
+ /**
7249
+ * A directive that adds minimum length validation to controls marked with the
7250
+ * `minlength` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
7251
+ *
7252
+ * @see [Form Validation](guide/forms/form-validation)
7253
+ *
7254
+ * @usageNotes
7255
+ *
7256
+ * ### Adding a minimum length validator
7257
+ *
7258
+ * The following example shows how to add a minimum length validator to an input attached to an
7259
+ * ngModel binding.
7260
+ *
7261
+ * ```html
7262
+ * <input name="firstName" ngModel minlength="4">
7263
+ * ```
7264
+ *
7265
+ * @ngModule ReactiveFormsModule
7266
+ * @ngModule FormsModule
7267
+ * @publicApi
7268
+ */
7269
+ class MinLengthValidator extends AbstractValidatorDirective {
7270
+ /**
7271
+ * @description
7272
+ * Tracks changes to the minimum length bound to this directive.
7345
7273
  */
7346
- _adjustIndex(index) {
7347
- return index < 0 ? index + this.length : index;
7348
- }
7349
- /** @internal */
7350
- _syncPendingControls() {
7351
- let subtreeUpdated = this.controls.reduce((updated, child) => {
7352
- return child._syncPendingControls() ? true : updated;
7353
- }, false);
7354
- if (subtreeUpdated)
7355
- this.updateValueAndValidity({ onlySelf: true });
7356
- return subtreeUpdated;
7357
- }
7274
+ minlength;
7358
7275
  /** @internal */
7359
- _forEachChild(cb) {
7360
- this.controls.forEach((control, index) => {
7361
- cb(control, index);
7362
- });
7363
- }
7276
+ inputName = 'minlength';
7364
7277
  /** @internal */
7365
- _updateValue() {
7366
- this.value = this.controls
7367
- .filter((control) => control.enabled || this.disabled)
7368
- .map((control) => control.value);
7369
- }
7278
+ normalizeInput = (input) => toInteger(input);
7370
7279
  /** @internal */
7371
- _anyControls(condition) {
7372
- return this.controls.some((control) => control.enabled && condition(control));
7373
- }
7280
+ createValidator = (minlength) => minLengthValidator(minlength);
7281
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MinLengthValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
7282
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: MinLengthValidator, isStandalone: false, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: { minlength: "minlength" }, host: { properties: { "attr.minlength": "_enabled ? minlength : null" } }, providers: [MIN_LENGTH_VALIDATOR], usesInheritance: true, ngImport: i0 });
7283
+ }
7284
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MinLengthValidator, decorators: [{
7285
+ type: Directive,
7286
+ args: [{
7287
+ selector: '[minlength][formControlName],[minlength][formControl],[minlength][ngModel]',
7288
+ providers: [MIN_LENGTH_VALIDATOR],
7289
+ host: { '[attr.minlength]': '_enabled ? minlength : null' },
7290
+ standalone: false,
7291
+ }]
7292
+ }], propDecorators: { minlength: [{
7293
+ type: Input
7294
+ }] } });
7295
+ /**
7296
+ * @description
7297
+ * Provider which adds `MaxLengthValidator` to the `NG_VALIDATORS` multi-provider list.
7298
+ */
7299
+ const MAX_LENGTH_VALIDATOR = {
7300
+ provide: NG_VALIDATORS,
7301
+ useExisting: forwardRef(() => MaxLengthValidator),
7302
+ multi: true,
7303
+ };
7304
+ /**
7305
+ * A directive that adds maximum length validation to controls marked with the
7306
+ * `maxlength` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list.
7307
+ *
7308
+ * @see [Form Validation](guide/forms/form-validation)
7309
+ *
7310
+ * @usageNotes
7311
+ *
7312
+ * ### Adding a maximum length validator
7313
+ *
7314
+ * The following example shows how to add a maximum length validator to an input attached to an
7315
+ * ngModel binding.
7316
+ *
7317
+ * ```html
7318
+ * <input name="firstName" ngModel maxlength="25">
7319
+ * ```
7320
+ *
7321
+ * @ngModule ReactiveFormsModule
7322
+ * @ngModule FormsModule
7323
+ * @publicApi
7324
+ */
7325
+ class MaxLengthValidator extends AbstractValidatorDirective {
7326
+ /**
7327
+ * @description
7328
+ * Tracks changes to the maximum length bound to this directive.
7329
+ */
7330
+ maxlength;
7374
7331
  /** @internal */
7375
- _setUpControls() {
7376
- this._forEachChild((control) => this._registerControl(control));
7377
- }
7332
+ inputName = 'maxlength';
7378
7333
  /** @internal */
7379
- _allControlsDisabled() {
7380
- for (const control of this.controls) {
7381
- if (control.enabled)
7382
- return false;
7383
- }
7384
- return this.controls.length > 0 || this.disabled;
7385
- }
7386
- _registerControl(control) {
7387
- control.setParent(this);
7388
- control._registerOnCollectionChange(this._onCollectionChange);
7389
- }
7334
+ normalizeInput = (input) => toInteger(input);
7390
7335
  /** @internal */
7391
- _find(name) {
7392
- return this.at(name) ?? null;
7393
- }
7336
+ createValidator = (maxlength) => maxLengthValidator(maxlength);
7337
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MaxLengthValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
7338
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: MaxLengthValidator, isStandalone: false, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: { maxlength: "maxlength" }, host: { properties: { "attr.maxlength": "_enabled ? maxlength : null" } }, providers: [MAX_LENGTH_VALIDATOR], usesInheritance: true, ngImport: i0 });
7394
7339
  }
7395
- const UntypedFormArray = FormArray;
7340
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: MaxLengthValidator, decorators: [{
7341
+ type: Directive,
7342
+ args: [{
7343
+ selector: '[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]',
7344
+ providers: [MAX_LENGTH_VALIDATOR],
7345
+ host: { '[attr.maxlength]': '_enabled ? maxlength : null' },
7346
+ standalone: false,
7347
+ }]
7348
+ }], propDecorators: { maxlength: [{
7349
+ type: Input
7350
+ }] } });
7396
7351
  /**
7397
7352
  * @description
7398
- * Asserts that the given control is an instance of `FormArray`
7353
+ * Provider which adds `PatternValidator` to the `NG_VALIDATORS` multi-provider list.
7354
+ */
7355
+ const PATTERN_VALIDATOR = {
7356
+ provide: NG_VALIDATORS,
7357
+ useExisting: forwardRef(() => PatternValidator),
7358
+ multi: true,
7359
+ };
7360
+ /**
7361
+ * @description
7362
+ * A directive that adds regex pattern validation to controls marked with the
7363
+ * `pattern` attribute. The regex must match the entire control value.
7364
+ * The directive is provided with the `NG_VALIDATORS` multi-provider list.
7365
+ *
7366
+ * @see [Form Validation](guide/forms/form-validation)
7367
+ *
7368
+ * @usageNotes
7369
+ *
7370
+ * ### Adding a pattern validator
7399
7371
  *
7372
+ * The following example shows how to add a pattern validator to an input attached to an
7373
+ * ngModel binding.
7374
+ *
7375
+ * ```html
7376
+ * <input name="firstName" ngModel pattern="[a-zA-Z ]*">
7377
+ * ```
7378
+ *
7379
+ * @ngModule ReactiveFormsModule
7380
+ * @ngModule FormsModule
7400
7381
  * @publicApi
7401
7382
  */
7402
- const isFormArray = (control) => control instanceof FormArray;
7383
+ class PatternValidator extends AbstractValidatorDirective {
7384
+ /**
7385
+ * @description
7386
+ * Tracks changes to the pattern bound to this directive.
7387
+ */
7388
+ pattern; // This input is always defined, since the name matches selector.
7389
+ /** @internal */
7390
+ inputName = 'pattern';
7391
+ /** @internal */
7392
+ normalizeInput = (input) => input;
7393
+ /** @internal */
7394
+ createValidator = (input) => patternValidator(input);
7395
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: PatternValidator, deps: null, target: i0.ɵɵFactoryTarget.Directive });
7396
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.0-next.2", type: PatternValidator, isStandalone: false, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: { pattern: "pattern" }, host: { properties: { "attr.pattern": "_enabled ? pattern : null" } }, providers: [PATTERN_VALIDATOR], usesInheritance: true, ngImport: i0 });
7397
+ }
7398
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: PatternValidator, decorators: [{
7399
+ type: Directive,
7400
+ args: [{
7401
+ selector: '[pattern][formControlName],[pattern][formControl],[pattern][ngModel]',
7402
+ providers: [PATTERN_VALIDATOR],
7403
+ host: { '[attr.pattern]': '_enabled ? pattern : null' },
7404
+ standalone: false,
7405
+ }]
7406
+ }], propDecorators: { pattern: [{
7407
+ type: Input
7408
+ }] } });
7409
+
7410
+ const SHARED_FORM_DIRECTIVES = [
7411
+ ɵNgNoValidate,
7412
+ NgSelectOption,
7413
+ ɵNgSelectMultipleOption,
7414
+ DefaultValueAccessor,
7415
+ NumberValueAccessor,
7416
+ RangeValueAccessor,
7417
+ CheckboxControlValueAccessor,
7418
+ SelectControlValueAccessor,
7419
+ SelectMultipleControlValueAccessor,
7420
+ RadioControlValueAccessor,
7421
+ NgControlStatus,
7422
+ NgControlStatusGroup,
7423
+ RequiredValidator,
7424
+ MinLengthValidator,
7425
+ MaxLengthValidator,
7426
+ PatternValidator,
7427
+ CheckboxRequiredValidator,
7428
+ EmailValidator,
7429
+ MinValidator,
7430
+ MaxValidator,
7431
+ ];
7432
+ const TEMPLATE_DRIVEN_DIRECTIVES = [NgModel, NgModelGroup, NgForm];
7433
+ const REACTIVE_DRIVEN_DIRECTIVES = [
7434
+ FormControlDirective,
7435
+ FormGroupDirective,
7436
+ FormArrayDirective,
7437
+ FormControlName,
7438
+ FormGroupName,
7439
+ FormArrayName,
7440
+ ];
7441
+ /**
7442
+ * Internal module used for sharing directives between FormsModule and ReactiveFormsModule
7443
+ */
7444
+ class ɵInternalFormsSharedModule {
7445
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵInternalFormsSharedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
7446
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵInternalFormsSharedModule, declarations: [ɵNgNoValidate,
7447
+ NgSelectOption,
7448
+ ɵNgSelectMultipleOption,
7449
+ DefaultValueAccessor,
7450
+ NumberValueAccessor,
7451
+ RangeValueAccessor,
7452
+ CheckboxControlValueAccessor,
7453
+ SelectControlValueAccessor,
7454
+ SelectMultipleControlValueAccessor,
7455
+ RadioControlValueAccessor,
7456
+ NgControlStatus,
7457
+ NgControlStatusGroup,
7458
+ RequiredValidator,
7459
+ MinLengthValidator,
7460
+ MaxLengthValidator,
7461
+ PatternValidator,
7462
+ CheckboxRequiredValidator,
7463
+ EmailValidator,
7464
+ MinValidator,
7465
+ MaxValidator], exports: [ɵNgNoValidate,
7466
+ NgSelectOption,
7467
+ ɵNgSelectMultipleOption,
7468
+ DefaultValueAccessor,
7469
+ NumberValueAccessor,
7470
+ RangeValueAccessor,
7471
+ CheckboxControlValueAccessor,
7472
+ SelectControlValueAccessor,
7473
+ SelectMultipleControlValueAccessor,
7474
+ RadioControlValueAccessor,
7475
+ NgControlStatus,
7476
+ NgControlStatusGroup,
7477
+ RequiredValidator,
7478
+ MinLengthValidator,
7479
+ MaxLengthValidator,
7480
+ PatternValidator,
7481
+ CheckboxRequiredValidator,
7482
+ EmailValidator,
7483
+ MinValidator,
7484
+ MaxValidator] });
7485
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵInternalFormsSharedModule });
7486
+ }
7487
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ɵInternalFormsSharedModule, decorators: [{
7488
+ type: NgModule,
7489
+ args: [{
7490
+ declarations: SHARED_FORM_DIRECTIVES,
7491
+ exports: SHARED_FORM_DIRECTIVES,
7492
+ }]
7493
+ }] });
7403
7494
 
7404
7495
  function isAbstractControlOptions(options) {
7405
7496
  return (!!options &&
@@ -7651,7 +7742,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2",
7651
7742
  /**
7652
7743
  * @publicApi
7653
7744
  */
7654
- const VERSION = new Version('20.2.3');
7745
+ const VERSION = new Version('21.0.0-next.1');
7655
7746
 
7656
7747
  /**
7657
7748
  * Exports the required providers and directives for template-driven forms,
@@ -7729,7 +7820,7 @@ class ReactiveFormsModule {
7729
7820
  };
7730
7821
  }
7731
7822
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ReactiveFormsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
7732
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.2.0-next.2", ngImport: i0, type: ReactiveFormsModule, declarations: [FormControlDirective, FormGroupDirective, FormControlName, FormGroupName, FormArrayName], exports: [ɵInternalFormsSharedModule, FormControlDirective, FormGroupDirective, FormControlName, FormGroupName, FormArrayName] });
7823
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.2.0-next.2", ngImport: i0, type: ReactiveFormsModule, declarations: [FormControlDirective, FormGroupDirective, FormArrayDirective, FormControlName, FormGroupName, FormArrayName], exports: [ɵInternalFormsSharedModule, FormControlDirective, FormGroupDirective, FormArrayDirective, FormControlName, FormGroupName, FormArrayName] });
7733
7824
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ReactiveFormsModule, imports: [ɵInternalFormsSharedModule] });
7734
7825
  }
7735
7826
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ReactiveFormsModule, decorators: [{
@@ -7740,5 +7831,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2",
7740
7831
  }]
7741
7832
  }] });
7742
7833
 
7743
- export { AbstractControl, AbstractControlDirective, AbstractFormGroupDirective, COMPOSITION_BUFFER_MODE, CheckboxControlValueAccessor, CheckboxRequiredValidator, ControlContainer, ControlEvent, DefaultValueAccessor, EmailValidator, FormArray, FormArrayName, FormBuilder, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormGroupName, FormRecord, FormResetEvent, FormSubmittedEvent, FormsModule, MaxLengthValidator, MaxValidator, MinLengthValidator, MinValidator, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, NgControlStatus, NgControlStatusGroup, NgForm, NgModel, NgModelGroup, NgSelectOption, NonNullableFormBuilder, NumberValueAccessor, PatternValidator, PristineChangeEvent, RadioControlValueAccessor, RangeValueAccessor, ReactiveFormsModule, RequiredValidator, SelectControlValueAccessor, SelectMultipleControlValueAccessor, StatusChangeEvent, TouchedChangeEvent, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, VERSION, Validators, ValueChangeEvent, isFormArray, isFormControl, isFormGroup, isFormRecord, ɵInternalFormsSharedModule, ɵNgNoValidate, ɵNgSelectMultipleOption };
7834
+ export { AbstractControl, AbstractControlDirective, AbstractFormDirective, AbstractFormGroupDirective, COMPOSITION_BUFFER_MODE, CheckboxControlValueAccessor, CheckboxRequiredValidator, ControlContainer, ControlEvent, DefaultValueAccessor, EmailValidator, FormArray, FormArrayDirective, FormArrayName, FormBuilder, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormGroupName, FormRecord, FormResetEvent, FormSubmittedEvent, FormsModule, MaxLengthValidator, MaxValidator, MinLengthValidator, MinValidator, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, NgControlStatus, NgControlStatusGroup, NgForm, NgModel, NgModelGroup, NgSelectOption, NonNullableFormBuilder, NumberValueAccessor, PatternValidator, PristineChangeEvent, RadioControlValueAccessor, RangeValueAccessor, ReactiveFormsModule, RequiredValidator, SelectControlValueAccessor, SelectMultipleControlValueAccessor, StatusChangeEvent, TouchedChangeEvent, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, VERSION, Validators, ValueChangeEvent, isFormArray, isFormControl, isFormGroup, isFormRecord, ɵInternalFormsSharedModule, ɵNgNoValidate, ɵNgSelectMultipleOption };
7744
7835
  //# sourceMappingURL=forms.mjs.map