Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions modules/@angular/forms/src/directives/error_examples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

export const FormErrorExamples = {
formControlName: `
<div [formGroup]="myGroup">
<input formControlName="firstName">
</div>

In your class:

this.myGroup = new FormGroup({
firstName: new FormControl()
});`,

formGroupName: `
<div [formGroup]="myGroup">
<div formGroupName="person">
<input formControlName="firstName">
</div>
</div>

In your class:

this.myGroup = new FormGroup({
person: new FormGroup({ firstName: new FormControl() })
});`,

formArrayName: `
<div [formGroup]="myGroup">
<div formArrayName="cities">
<div *ngFor="let city of cityArray.controls; let i=index">
<input [formControlName]="i">
</div>
</div>
</div>

In your class:

this.cityArray = new FormArray([new FormControl('SF')]);
this.myGroup = new FormGroup({
cities: this.cityArray
});`,

ngModelGroup: `
<form>
<div ngModelGroup="person">
<input [(ngModel)]="person.name" name="firstName">
</div>
</form>`,

ngModelWithFormGroup: `
<div [formGroup]="myGroup">
<input formControlName="firstName">
<input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}">
</div>
`
};

31 changes: 23 additions & 8 deletions modules/@angular/forms/src/directives/ng_model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ import {BaseException} from '../facade/exceptions';
import {FormControl} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';

import {AbstractFormGroupDirective} from './abstract_form_group_directive';
import {ControlContainer} from './control_container';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {NgControl} from './ng_control';
import {NgForm} from './ng_form';
import {NgModelGroup} from './ng_model_group';
import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor, setUpControl} from './shared';
import {TemplateDrivenErrors} from './template_driven_errors';
import {AsyncValidatorFn, ValidatorFn} from './validators';

export const formControlBinding: any =
Expand Down Expand Up @@ -75,7 +79,7 @@ export class NgModel extends NgControl implements OnChanges,
}

ngOnChanges(changes: SimpleChanges) {
this._checkName();
this._checkForErrors();
if (!this._registered) this._setUpControl();

if (isPropertyUpdated(changes, this.viewModel)) {
Expand Down Expand Up @@ -120,17 +124,28 @@ export class NgModel extends NgControl implements OnChanges,
this._control.updateValueAndValidity({emitEvent: false});
}

private _checkForErrors(): void {
if (!this._isStandalone()) {
this._checkParentType();
}
this._checkName();
}

private _checkParentType(): void {
if (!(this._parent instanceof NgModelGroup) &&
this._parent instanceof AbstractFormGroupDirective) {
TemplateDrivenErrors.formGroupNameException();
} else if (
!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) {
TemplateDrivenErrors.modelParentException();
}
}

private _checkName(): void {
if (this.options && this.options.name) this.name = this.options.name;

if (!this._isStandalone() && !this.name) {
throw new BaseException(
`If ngModel is used within a form tag, either the name attribute must be set
or the form control must be defined as 'standalone' in ngModelOptions.

Example 1: <input [(ngModel)]="person.firstName" name="first">
Example 2: <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone: true}">
`);
TemplateDrivenErrors.missingNameException();
}
}

Expand Down
10 changes: 10 additions & 0 deletions modules/@angular/forms/src/directives/ng_model_group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@

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

import {BaseException} from '../facade/exceptions';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';

import {AbstractFormGroupDirective} from './abstract_form_group_directive';
import {ControlContainer} from './control_container';
import {NgForm} from './ng_form';
import {TemplateDrivenErrors} from './template_driven_errors';

export const modelGroupProvider: any =
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
Expand Down Expand Up @@ -69,4 +72,11 @@ export class NgModelGroup extends AbstractFormGroupDirective implements OnInit,
this._validators = validators;
this._asyncValidators = asyncValidators;
}

/** @internal */
_checkParentType(): void {
if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) {
TemplateDrivenErrors.modelGroupParentException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {BaseException} from '../../facade/exceptions';
import {FormArray} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
import {ControlContainer} from '../control_container';
import {ReactiveErrors} from '../reactive_errors';
import {composeAsyncValidators, composeValidators, controlPath} from '../shared';
import {AsyncValidatorFn, ValidatorFn} from '../validators';

Expand Down Expand Up @@ -101,28 +102,7 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy

private _checkParentType(): void {
if (!(this._parent instanceof FormGroupName) && !(this._parent instanceof FormGroupDirective)) {
this._throwParentException();
ReactiveErrors.arrayParentException();
}
}

private _throwParentException(): void {
throw new BaseException(`formArrayName must be used with a parent formGroup directive.
You'll want to add a formGroup directive and pass it an existing FormGroup instance
(you can create one in your class).

Example:
<div [formGroup]="myGroup">
<div formArrayName="cities">
<div *ngFor="let city of cityArray.controls; let i=index">
<input [formControlName]="i">
</div>
</div>
</div>

In your class:
this.cityArray = new FormArray([new FormControl('SF')]);
this.myGroup = new FormGroup({
cities: this.cityArray
});`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import {EventEmitter, ObservableWrapper} from '../../facade/async';
import {BaseException} from '../../facade/exceptions';
import {FormControl} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
import {AbstractFormGroupDirective} from '../abstract_form_group_directive';
import {ControlContainer} from '../control_container';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '../control_value_accessor';
import {NgControl} from '../ng_control';
import {ReactiveErrors} from '../reactive_errors';
import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor} from '../shared';
import {AsyncValidatorFn, ValidatorFn} from '../validators';

Expand Down Expand Up @@ -153,26 +155,13 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {

private _checkParentType(): void {
if (!(this._parent instanceof FormGroupName) &&
this._parent instanceof AbstractFormGroupDirective) {
ReactiveErrors.ngModelGroupException();
} else if (
!(this._parent instanceof FormGroupName) &&
!(this._parent instanceof FormGroupDirective) &&
!(this._parent instanceof FormArrayName)) {
this._throwParentException();
ReactiveErrors.controlParentException();
}
}

private _throwParentException(): void {
throw new BaseException(
`formControlName must be used with a parent formGroup directive.
You'll want to add a formGroup directive and pass it an existing FormGroup instance
(you can create one in your class).

Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
</div>

In your class:
this.myGroup = new FormGroup({
firstName: new FormControl()
});`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators';
import {ControlContainer} from '../control_container';
import {Form} from '../form_interface';
import {NgControl} from '../ng_control';
import {ReactiveErrors} from '../reactive_errors';
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormContainer} from '../shared';

import {FormArrayName} from './form_array_name';
Expand Down Expand Up @@ -199,9 +200,7 @@ export class FormGroupDirective extends ControlContainer implements Form,

private _checkFormPresent() {
if (isBlank(this.form)) {
throw new BaseException(`formGroup expects a FormGroup instance. Please pass one in.
Example: <form [formGroup]="myFormGroup">
`);
ReactiveErrors.missingFormException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {BaseException} from '../../facade/exceptions';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
import {AbstractFormGroupDirective} from '../abstract_form_group_directive';
import {ControlContainer} from '../control_container';
import {ReactiveErrors} from '../reactive_errors';

import {FormGroupDirective} from './form_group_directive';

Expand Down Expand Up @@ -86,25 +87,7 @@ export class FormGroupName extends AbstractFormGroupDirective implements OnInit,
/** @internal */
_checkParentType(): void {
if (!(this._parent instanceof FormGroupName) && !(this._parent instanceof FormGroupDirective)) {
this._throwParentException();
ReactiveErrors.groupParentException();
}
}

private _throwParentException() {
throw new BaseException(`formGroupName must be used with a parent formGroup directive.
You'll want to add a formGroup directive and pass it an existing FormGroup instance
(you can create one in your class).

Example:
<div [formGroup]="myGroup">
<div formGroupName="person">
<input formControlName="firstName">
</div>
</div>

In your class:
this.myGroup = new FormGroup({
person: new FormGroup({ firstName: new FormControl() })
});`);
}
}
63 changes: 63 additions & 0 deletions modules/@angular/forms/src/directives/reactive_errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {BaseException} from '../facade/exceptions';
import {FormErrorExamples as Examples} from './error_examples';

export class ReactiveErrors {
static controlParentException(): void {
throw new BaseException(
`formControlName must be used with a parent formGroup directive. You'll want to add a formGroup
directive and pass it an existing FormGroup instance (you can create one in your class).

Example:

${Examples.formControlName}`);
}

static ngModelGroupException(): void {
throw new BaseException(
`formControlName cannot be used with an ngModelGroup parent. It is only compatible with parents
that also have a "form" prefix: formGroupName, formArrayName, or formGroup.

Option 1: Update the parent to be formGroupName (reactive form strategy)

${Examples.formGroupName}

Option 2: Use ngModel instead of formControlName (template-driven strategy)

${Examples.ngModelGroup}`);
}
static missingFormException(): void {
throw new BaseException(`formGroup expects a FormGroup instance. Please pass one in.

Example:

${Examples.formControlName}`);
}

static groupParentException(): void {
throw new BaseException(
`formGroupName must be used with a parent formGroup directive. You'll want to add a formGroup
directive and pass it an existing FormGroup instance (you can create one in your class).

Example:

${Examples.formGroupName}`);
}

static arrayParentException(): void {
throw new BaseException(
`formArrayName must be used with a parent formGroup directive. You'll want to add a formGroup
directive and pass it an existing FormGroup instance (you can create one in your class).

Example:

${Examples.formArrayName}`);
}
}
Loading