Skip to content

Commit d6d4568

Browse files
authored
fix(forms): allow arrays as parents (#10440)
Closes #10432
1 parent a55d796 commit d6d4568

File tree

9 files changed

+170
-142
lines changed

9 files changed

+170
-142
lines changed

modules/@angular/forms/src/directives.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@ import {NgModel} from './directives/ng_model';
1616
import {NgModelGroup} from './directives/ng_model_group';
1717
import {NumberValueAccessor} from './directives/number_value_accessor';
1818
import {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
19-
import {FormArrayName} from './directives/reactive_directives/form_array_name';
2019
import {FormControlDirective} from './directives/reactive_directives/form_control_directive';
2120
import {FormControlName} from './directives/reactive_directives/form_control_name';
2221
import {FormGroupDirective} from './directives/reactive_directives/form_group_directive';
23-
import {FormGroupName} from './directives/reactive_directives/form_group_name';
22+
import {FormArrayName, FormGroupName} from './directives/reactive_directives/form_group_name';
2423
import {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
2524
import {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
2625
import {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
@@ -35,11 +34,10 @@ export {NgModel} from './directives/ng_model';
3534
export {NgModelGroup} from './directives/ng_model_group';
3635
export {NumberValueAccessor} from './directives/number_value_accessor';
3736
export {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
38-
export {FormArrayName} from './directives/reactive_directives/form_array_name';
3937
export {FormControlDirective} from './directives/reactive_directives/form_control_directive';
4038
export {FormControlName} from './directives/reactive_directives/form_control_name';
4139
export {FormGroupDirective} from './directives/reactive_directives/form_group_directive';
42-
export {FormGroupName} from './directives/reactive_directives/form_group_name';
40+
export {FormArrayName, FormGroupName} from './directives/reactive_directives/form_group_name';
4341
export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
4442
export {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
4543
export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';

modules/@angular/forms/src/directives/reactive_directives/form_array_name.ts

Lines changed: 0 additions & 107 deletions
This file was deleted.

modules/@angular/forms/src/directives/reactive_directives/form_control_name.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import {Directive, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core';
1010

1111
import {EventEmitter, ObservableWrapper} from '../../facade/async';
12-
import {BaseException} from '../../facade/exceptions';
1312
import {FormControl} from '../../model';
1413
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
1514
import {AbstractFormGroupDirective} from '../abstract_form_group_directive';
@@ -20,10 +19,8 @@ import {ReactiveErrors} from '../reactive_errors';
2019
import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor} from '../shared';
2120
import {AsyncValidatorFn, ValidatorFn} from '../validators';
2221

23-
import {FormArrayName} from './form_array_name';
2422
import {FormGroupDirective} from './form_group_directive';
25-
import {FormGroupName} from './form_group_name';
26-
23+
import {FormArrayName, FormGroupName} from './form_group_name';
2724

2825
export const controlNameBinding: any = {
2926
provide: NgControl,

modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ import {NgControl} from '../ng_control';
2020
import {ReactiveErrors} from '../reactive_errors';
2121
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormContainer} from '../shared';
2222

23-
import {FormArrayName} from './form_array_name';
24-
import {FormGroupName} from './form_group_name';
23+
import {FormArrayName, FormGroupName} from './form_group_name';
2524

2625
export const formDirectiveProvider: any = {
2726
provide: ControlContainer,

modules/@angular/forms/src/directives/reactive_directives/form_group_name.ts

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88

99
import {Directive, Host, Inject, Input, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
1010

11-
import {BaseException} from '../../facade/exceptions';
11+
import {FormArray} from '../../model';
1212
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
1313
import {AbstractFormGroupDirective} from '../abstract_form_group_directive';
1414
import {ControlContainer} from '../control_container';
1515
import {ReactiveErrors} from '../reactive_errors';
16+
import {composeAsyncValidators, composeValidators, controlPath} from '../shared';
17+
import {AsyncValidatorFn, ValidatorFn} from '../validators';
1618

1719
import {FormGroupDirective} from './form_group_directive';
1820

@@ -85,8 +87,100 @@ export class FormGroupName extends AbstractFormGroupDirective implements OnInit,
8587

8688
/** @internal */
8789
_checkParentType(): void {
88-
if (!(this._parent instanceof FormGroupName) && !(this._parent instanceof FormGroupDirective)) {
90+
if (_hasInvalidParent(this._parent)) {
8991
ReactiveErrors.groupParentException();
9092
}
9193
}
9294
}
95+
96+
export const formArrayNameProvider: any = {
97+
provide: ControlContainer,
98+
useExisting: forwardRef(() => FormArrayName)
99+
};
100+
101+
/**
102+
* Syncs an existing form array to a DOM element.
103+
*
104+
* This directive can only be used as a child of {@link FormGroupDirective}.
105+
*
106+
* ```typescript
107+
* @Component({
108+
* selector: 'my-app',
109+
* template: `
110+
* <div>
111+
* <h2>Angular FormArray Example</h2>
112+
* <form [formGroup]="myForm">
113+
* <div formArrayName="cities">
114+
* <div *ngFor="let city of cityArray.controls; let i=index">
115+
* <input [formControlName]="i">
116+
* </div>
117+
* </div>
118+
* </form>
119+
* {{ myForm.value | json }} // {cities: ['SF', 'NY']}
120+
* </div>
121+
* `
122+
* })
123+
* export class App {
124+
* cityArray = new FormArray([
125+
* new FormControl('SF'),
126+
* new FormControl('NY')
127+
* ]);
128+
* myForm = new FormGroup({
129+
* cities: this.cityArray
130+
* });
131+
* }
132+
* ```
133+
*
134+
* @experimental
135+
*/
136+
@Directive({selector: '[formArrayName]', providers: [formArrayNameProvider]})
137+
export class FormArrayName extends ControlContainer implements OnInit, OnDestroy {
138+
/** @internal */
139+
_parent: ControlContainer;
140+
141+
/** @internal */
142+
_validators: any[];
143+
144+
/** @internal */
145+
_asyncValidators: any[];
146+
147+
@Input('formArrayName') name: string;
148+
149+
constructor(
150+
@Optional() @Host() @SkipSelf() parent: ControlContainer,
151+
@Optional() @Self() @Inject(NG_VALIDATORS) validators: any[],
152+
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) {
153+
super();
154+
this._parent = parent;
155+
this._validators = validators;
156+
this._asyncValidators = asyncValidators;
157+
}
158+
159+
ngOnInit(): void {
160+
this._checkParentType();
161+
this.formDirective.addFormArray(this);
162+
}
163+
164+
ngOnDestroy(): void { this.formDirective.removeFormArray(this); }
165+
166+
get control(): FormArray { return this.formDirective.getFormArray(this); }
167+
168+
get formDirective(): FormGroupDirective { return <FormGroupDirective>this._parent.formDirective; }
169+
170+
get path(): string[] { return controlPath(this.name, this._parent); }
171+
172+
get validator(): ValidatorFn { return composeValidators(this._validators); }
173+
174+
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
175+
176+
private _checkParentType(): void {
177+
if (_hasInvalidParent(this._parent)) {
178+
ReactiveErrors.arrayParentException();
179+
}
180+
}
181+
}
182+
183+
function _hasInvalidParent(parent: ControlContainer): boolean {
184+
return !(parent instanceof FormGroupName) && !(parent instanceof FormGroupDirective) &&
185+
!(parent instanceof FormArrayName);
186+
}

modules/@angular/forms/src/directives/reactive_errors.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
import {BaseException} from '../facade/exceptions';
10+
1011
import {FormErrorExamples as Examples} from './error_examples';
1112

1213
export class ReactiveErrors {
@@ -60,4 +61,4 @@ export class ReactiveErrors {
6061
6162
${Examples.formArrayName}`);
6263
}
63-
}
64+
}

modules/@angular/forms/src/directives/shared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {NgControl} from './ng_control';
2222
import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator';
2323
import {NumberValueAccessor} from './number_value_accessor';
2424
import {RadioControlValueAccessor} from './radio_control_value_accessor';
25-
import {FormArrayName} from './reactive_directives/form_array_name';
25+
import {FormArrayName} from './reactive_directives/form_group_name';
2626
import {SelectControlValueAccessor} from './select_control_value_accessor';
2727
import {SelectMultipleControlValueAccessor} from './select_multiple_control_value_accessor';
2828
import {AsyncValidatorFn, ValidatorFn} from './validators';

modules/@angular/forms/src/forms.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ export {NgControlStatus} from './directives/ng_control_status';
3333
export {NgForm} from './directives/ng_form';
3434
export {NgModel} from './directives/ng_model';
3535
export {NgModelGroup} from './directives/ng_model_group';
36-
export {FormArrayName} from './directives/reactive_directives/form_array_name';
3736
export {FormControlDirective} from './directives/reactive_directives/form_control_directive';
3837
export {FormControlName} from './directives/reactive_directives/form_control_name';
3938
export {FormGroupDirective} from './directives/reactive_directives/form_group_directive';
39+
export {FormArrayName} from './directives/reactive_directives/form_group_name';
4040
export {FormGroupName} from './directives/reactive_directives/form_group_name';
4141
export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
4242
export {SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';

0 commit comments

Comments
 (0)