refactor(forms): remove ngForm element selector (#33058)
Removes the deprecated `ngForm` element selector and all of the code related to it. BREAKING CHANGES: * `<ngForm></ngForm>` can no longer be used as a selector. Use `<ng-form></ng-form>` instead. * The `NgFromSelectorWarning` directive has been removed. * `FormsModule.withConfig` has been removed. Use the `FormsModule` directly. PR Close #33058
This commit is contained in:
parent
15e3b5f531
commit
0b1daa9ebd
|
@ -41,8 +41,6 @@ v8 - v11
|
||||||
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v9 |
|
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v9 |
|
||||||
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v9 |
|
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v9 |
|
||||||
| `@angular/core` | [`ViewEncapsulation.Native`](#core) | v9 |
|
| `@angular/core` | [`ViewEncapsulation.Native`](#core) | v9 |
|
||||||
| `@angular/forms` | [`ngForm` element selector](#ngform) | v9 |
|
|
||||||
| `@angular/forms` | [`NgFormSelectorWarning`](#forms) | v9 |
|
|
||||||
| `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | v9 |
|
| `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | v9 |
|
||||||
| `@angular/router` | [`preserveQueryParams`](#router) | <!--v7--> v9 |
|
| `@angular/router` | [`preserveQueryParams`](#router) | <!--v7--> v9 |
|
||||||
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v9 |
|
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v9 |
|
||||||
|
@ -107,7 +105,6 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i
|
||||||
|
|
||||||
| API | Replacement | Deprecation announced | Notes |
|
| API | Replacement | Deprecation announced | Notes |
|
||||||
| --- | ----------- | --------------------- | ----- |
|
| --- | ----------- | --------------------- | ----- |
|
||||||
| [`NgFormSelectorWarning`](api/forms/NgFormSelectorWarning) | n/a | v6 | See [ngForm](#ngform). |
|
|
||||||
|
|
||||||
{@a router}
|
{@a router}
|
||||||
### @angular/router
|
### @angular/router
|
||||||
|
@ -176,27 +173,6 @@ The `<template>` tag was deprecated in v4 to avoid colliding with the DOM's elem
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a ngform}
|
|
||||||
### ngForm element selector
|
|
||||||
|
|
||||||
Support for using `ngForm` element selector was deprecated in v6.
|
|
||||||
It has been deprecated to be consistent with other core Angular selectors, which are typically written in kebab-case.
|
|
||||||
|
|
||||||
Deprecated:
|
|
||||||
|
|
||||||
```
|
|
||||||
<ngForm #myForm="ngForm">
|
|
||||||
```
|
|
||||||
|
|
||||||
Replacement:
|
|
||||||
|
|
||||||
```
|
|
||||||
<ng-form #myForm="ngForm">
|
|
||||||
```
|
|
||||||
|
|
||||||
The [`NgFormSelectorWarning`](api/forms/NgFormSelectorWarning) directive is solely used to display warnings when the deprecated `ngForm` selector is used.
|
|
||||||
|
|
||||||
|
|
||||||
{@a ngmodel-reactive}
|
{@a ngmodel-reactive}
|
||||||
### ngModel with reactive forms
|
### ngModel with reactive forms
|
||||||
|
|
||||||
|
@ -405,9 +381,17 @@ The following APIs have been removed starting with version 8.0.0:
|
||||||
| `@angular/core/testing` | [`TestBed.deprecatedOverrideProvider()`](https://v7.angular.io/api/core/testing/TestBed#deprecatedoverrideprovider) | [`TestBed.overrideProvider()`](api/core/testing/TestBed#overrideprovider) | none |
|
| `@angular/core/testing` | [`TestBed.deprecatedOverrideProvider()`](https://v7.angular.io/api/core/testing/TestBed#deprecatedoverrideprovider) | [`TestBed.overrideProvider()`](api/core/testing/TestBed#overrideprovider) | none |
|
||||||
| `@angular/core/testing` | [`TestBedStatic.deprecatedOverrideProvider()`](https://v7.angular.io/api/core/testing/TestBedStatic#deprecatedoverrideprovider) | [`TestBedStatic.overrideProvider()`](api/core/testing/TestBedStatic#overrideprovider) | none |
|
| `@angular/core/testing` | [`TestBedStatic.deprecatedOverrideProvider()`](https://v7.angular.io/api/core/testing/TestBedStatic#deprecatedoverrideprovider) | [`TestBedStatic.overrideProvider()`](api/core/testing/TestBedStatic#overrideprovider) | none |
|
||||||
| `@angular/service-worker` | `versionedFiles` | `files` | In the service worker configuration file `ngsw-config.json`, replace `versionedFiles` with `files`. See [Service Worker Configuration](guide/service-worker-config#assetgroups). |
|
| `@angular/service-worker` | `versionedFiles` | `files` | In the service worker configuration file `ngsw-config.json`, replace `versionedFiles` with `files`. See [Service Worker Configuration](guide/service-worker-config#assetgroups). |
|
||||||
| `@angular/core` | [`Renderer`](https://v8.angular.io/api/core/Renderer) | [`Renderer2`](https://angular.io/api/core/Renderer2) | [Migration guide.](guide/migration-renderer)
|
|
||||||
| `@angular/core` | [`RootRenderer`](https://v8.angular.io/api/core/RootRenderer) | [`RendererFactory2`](https://angular.io/api/core/RendererFactory2) | none
|
|
||||||
| `@angular/core` | [`RenderComponentType`](https://v8.angular.io/api/core/RenderComponentType) | [`RendererType2`](https://angular.io/api/core/RendererType2) | none
|
The following APIs have been removed starting with version 9.0.0:
|
||||||
|
|
||||||
|
| Package | API | Replacement | Notes |
|
||||||
|
| ------- | -------------- | ----------- | ----- |
|
||||||
|
| `@angular/core` | [`Renderer`](https://v8.angular.io/api/core/Renderer) | [`Renderer2`](https://angular.io/api/core/Renderer2) | [Migration guide.](guide/migration-renderer)
|
||||||
|
| `@angular/core` | [`RootRenderer`](https://v8.angular.io/api/core/RootRenderer) | [`RendererFactory2`](https://angular.io/api/core/RendererFactory2) | none
|
||||||
|
| `@angular/core` | [`RenderComponentType`](https://v8.angular.io/api/core/RenderComponentType) | [`RendererType2`](https://angular.io/api/core/RendererType2) | none
|
||||||
|
| `@angular/forms` | [`NgFormSelectorWarning`](https://v8.angular.io/api/forms/NgFormSelectorWarning) | none | none
|
||||||
|
| `@angular/forms` | `ngForm` element selector | `ng-form` element selector | none
|
||||||
|
|
||||||
|
|
||||||
<!-- The following anchor is used by redirects from the removed API pages. Do not change or remove. -->
|
<!-- The following anchor is used by redirects from the removed API pages. Do not change or remove. -->
|
||||||
|
|
|
@ -12,7 +12,6 @@ import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor
|
||||||
import {DefaultValueAccessor} from './directives/default_value_accessor';
|
import {DefaultValueAccessor} from './directives/default_value_accessor';
|
||||||
import {NgControlStatus, NgControlStatusGroup} from './directives/ng_control_status';
|
import {NgControlStatus, NgControlStatusGroup} from './directives/ng_control_status';
|
||||||
import {NgForm} from './directives/ng_form';
|
import {NgForm} from './directives/ng_form';
|
||||||
import {NgFormSelectorWarning} from './directives/ng_form_selector_warning';
|
|
||||||
import {NgModel} from './directives/ng_model';
|
import {NgModel} from './directives/ng_model';
|
||||||
import {NgModelGroup} from './directives/ng_model_group';
|
import {NgModelGroup} from './directives/ng_model_group';
|
||||||
import {NgNoValidate} from './directives/ng_no_validate_directive';
|
import {NgNoValidate} from './directives/ng_no_validate_directive';
|
||||||
|
@ -33,7 +32,6 @@ export {DefaultValueAccessor} from './directives/default_value_accessor';
|
||||||
export {NgControl} from './directives/ng_control';
|
export {NgControl} from './directives/ng_control';
|
||||||
export {NgControlStatus, NgControlStatusGroup} from './directives/ng_control_status';
|
export {NgControlStatus, NgControlStatusGroup} from './directives/ng_control_status';
|
||||||
export {NgForm} from './directives/ng_form';
|
export {NgForm} from './directives/ng_form';
|
||||||
export {NG_FORM_SELECTOR_WARNING, NgFormSelectorWarning} from './directives/ng_form_selector_warning';
|
|
||||||
export {NgModel} from './directives/ng_model';
|
export {NgModel} from './directives/ng_model';
|
||||||
export {NgModelGroup} from './directives/ng_model_group';
|
export {NgModelGroup} from './directives/ng_model_group';
|
||||||
export {NumberValueAccessor} from './directives/number_value_accessor';
|
export {NumberValueAccessor} from './directives/number_value_accessor';
|
||||||
|
@ -67,8 +65,7 @@ export const SHARED_FORM_DIRECTIVES: Type<any>[] = [
|
||||||
EmailValidator,
|
EmailValidator,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const TEMPLATE_DRIVEN_DIRECTIVES: Type<any>[] =
|
export const TEMPLATE_DRIVEN_DIRECTIVES: Type<any>[] = [NgModel, NgModelGroup, NgForm];
|
||||||
[NgModel, NgModelGroup, NgForm, NgFormSelectorWarning];
|
|
||||||
|
|
||||||
export const REACTIVE_DRIVEN_DIRECTIVES: Type<any>[] =
|
export const REACTIVE_DRIVEN_DIRECTIVES: Type<any>[] =
|
||||||
[FormControlDirective, FormGroupDirective, FormControlName, FormGroupName, FormArrayName];
|
[FormControlDirective, FormGroupDirective, FormControlName, FormGroupName, FormArrayName];
|
||||||
|
|
|
@ -94,7 +94,7 @@ const resolvedPromise = (() => Promise.resolve(null))();
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'form:not([ngNoForm]):not([formGroup]),ngForm,ng-form,[ngForm]',
|
selector: 'form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]',
|
||||||
providers: [formDirectiveProvider],
|
providers: [formDirectiveProvider],
|
||||||
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
|
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
|
||||||
outputs: ['ngSubmit'],
|
outputs: ['ngSubmit'],
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
/**
|
|
||||||
* @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 {Directive, Inject, InjectionToken, Optional} from '@angular/core';
|
|
||||||
import {TemplateDrivenErrors} from './template_driven_errors';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description
|
|
||||||
* `InjectionToken` to provide to turn off the warning when using 'ngForm' deprecated selector.
|
|
||||||
*/
|
|
||||||
export const NG_FORM_SELECTOR_WARNING = new InjectionToken('NgFormSelectorWarning');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This directive is solely used to display warnings when the deprecated `ngForm` selector is used.
|
|
||||||
*
|
|
||||||
* @deprecated in Angular v6 and will be removed in Angular v9.
|
|
||||||
* @ngModule FormsModule
|
|
||||||
* @publicApi
|
|
||||||
*/
|
|
||||||
@Directive({selector: 'ngForm'})
|
|
||||||
export class NgFormSelectorWarning {
|
|
||||||
/**
|
|
||||||
* Static property used to track whether the deprecation warning for this selector has been sent.
|
|
||||||
* Used to support warning config of "once".
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
static _ngFormWarning = false;
|
|
||||||
|
|
||||||
constructor(@Optional() @Inject(NG_FORM_SELECTOR_WARNING) ngFormWarning: string|null) {
|
|
||||||
if (((!ngFormWarning || ngFormWarning === 'once') && !NgFormSelectorWarning._ngFormWarning) ||
|
|
||||||
ngFormWarning === 'always') {
|
|
||||||
TemplateDrivenErrors.ngFormWarning();
|
|
||||||
NgFormSelectorWarning._ngFormWarning = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -57,21 +57,4 @@ export class TemplateDrivenErrors {
|
||||||
|
|
||||||
${Examples.ngModelGroup}`);
|
${Examples.ngModelGroup}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ngFormWarning() {
|
|
||||||
console.warn(`
|
|
||||||
It looks like you're using 'ngForm'.
|
|
||||||
|
|
||||||
Support for using the 'ngForm' element selector has been deprecated in Angular v6 and will be removed
|
|
||||||
in Angular v9.
|
|
||||||
|
|
||||||
Use 'ng-form' instead.
|
|
||||||
|
|
||||||
Before:
|
|
||||||
<ngForm #myForm="ngForm">
|
|
||||||
|
|
||||||
After:
|
|
||||||
<ng-form #myForm="ngForm">
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {ModuleWithProviders, NgModule} from '@angular/core';
|
import {ModuleWithProviders, NgModule} from '@angular/core';
|
||||||
|
|
||||||
import {InternalFormsSharedModule, NG_FORM_SELECTOR_WARNING, NG_MODEL_WITH_FORM_CONTROL_WARNING, REACTIVE_DRIVEN_DIRECTIVES, TEMPLATE_DRIVEN_DIRECTIVES} from './directives';
|
import {InternalFormsSharedModule, NG_MODEL_WITH_FORM_CONTROL_WARNING, REACTIVE_DRIVEN_DIRECTIVES, TEMPLATE_DRIVEN_DIRECTIVES} from './directives';
|
||||||
import {RadioControlRegistry} from './directives/radio_control_value_accessor';
|
import {RadioControlRegistry} from './directives/radio_control_value_accessor';
|
||||||
import {FormBuilder} from './form_builder';
|
import {FormBuilder} from './form_builder';
|
||||||
|
|
||||||
|
@ -26,23 +26,6 @@ import {FormBuilder} from './form_builder';
|
||||||
exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
|
exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
|
||||||
})
|
})
|
||||||
export class FormsModule {
|
export class FormsModule {
|
||||||
/**
|
|
||||||
* @description
|
|
||||||
* Provides options for configuring the template-driven forms module.
|
|
||||||
*
|
|
||||||
* @param opts An object of configuration options
|
|
||||||
* * `warnOnDeprecatedNgFormSelector` Configures when to emit a warning when the deprecated
|
|
||||||
* `ngForm` selector is used.
|
|
||||||
*/
|
|
||||||
static withConfig(opts: {
|
|
||||||
/** @deprecated as of v6 */ warnOnDeprecatedNgFormSelector?: 'never' | 'once' | 'always',
|
|
||||||
}): ModuleWithProviders<FormsModule> {
|
|
||||||
return {
|
|
||||||
ngModule: FormsModule,
|
|
||||||
providers:
|
|
||||||
[{provide: NG_FORM_SELECTOR_WARNING, useValue: opts.warnOnDeprecatedNgFormSelector}]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,7 +29,6 @@ export {Form} from './directives/form_interface';
|
||||||
export {NgControl} from './directives/ng_control';
|
export {NgControl} from './directives/ng_control';
|
||||||
export {NgControlStatus, NgControlStatusGroup} from './directives/ng_control_status';
|
export {NgControlStatus, NgControlStatusGroup} from './directives/ng_control_status';
|
||||||
export {NgForm} from './directives/ng_form';
|
export {NgForm} from './directives/ng_form';
|
||||||
export {NgFormSelectorWarning} from './directives/ng_form_selector_warning';
|
|
||||||
export {NgModel} from './directives/ng_model';
|
export {NgModel} from './directives/ng_model';
|
||||||
export {NgModelGroup} from './directives/ng_model_group';
|
export {NgModelGroup} from './directives/ng_model_group';
|
||||||
export {ɵNgNoValidate} from './directives/ng_no_validate_directive';
|
export {ɵNgNoValidate} from './directives/ng_no_validate_directive';
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import {ɵgetDOM as getDOM} from '@angular/common';
|
import {ɵgetDOM as getDOM} from '@angular/common';
|
||||||
import {Component, Directive, Type, forwardRef} from '@angular/core';
|
import {Component, Directive, Type, forwardRef} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing';
|
||||||
import {AbstractControl, AsyncValidator, COMPOSITION_BUFFER_MODE, FormControl, FormsModule, NG_ASYNC_VALIDATORS, NgForm, NgFormSelectorWarning, NgModel} from '@angular/forms';
|
import {AbstractControl, AsyncValidator, COMPOSITION_BUFFER_MODE, FormControl, FormsModule, NG_ASYNC_VALIDATORS, NgForm, NgModel} from '@angular/forms';
|
||||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||||
import {dispatchEvent, sortedClassList} from '@angular/platform-browser/testing/src/browser_util';
|
import {dispatchEvent, sortedClassList} from '@angular/platform-browser/testing/src/browser_util';
|
||||||
import {merge} from 'rxjs';
|
import {merge} from 'rxjs';
|
||||||
|
@ -1630,61 +1630,6 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngForm deprecation warnings', () => {
|
|
||||||
let warnSpy: jasmine.Spy;
|
|
||||||
|
|
||||||
@Component({selector: 'ng-form-deprecated', template: `<ngForm></ngForm><ngForm></ngForm>`})
|
|
||||||
class ngFormDeprecated {
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
(NgFormSelectorWarning as any)._ngFormWarning = false;
|
|
||||||
|
|
||||||
warnSpy = spyOn(console, 'warn');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe(`when using the deprecated 'ngForm' selector`, () => {
|
|
||||||
it(`should only warn once when global provider is provided with "once"`, () => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ngFormDeprecated],
|
|
||||||
imports: [FormsModule.withConfig({warnOnDeprecatedNgFormSelector: 'once'})]
|
|
||||||
});
|
|
||||||
TestBed.createComponent(ngFormDeprecated);
|
|
||||||
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(warnSpy.calls.mostRecent().args[0])
|
|
||||||
.toMatch(/It looks like you're using 'ngForm'/gi);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should only warn once by default`, () => {
|
|
||||||
initTest(ngFormDeprecated);
|
|
||||||
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(warnSpy.calls.mostRecent().args[0])
|
|
||||||
.toMatch(/It looks like you're using 'ngForm'/gi);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should not warn when global provider is provided with "never"`, () => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ngFormDeprecated],
|
|
||||||
imports: [FormsModule.withConfig({warnOnDeprecatedNgFormSelector: 'never'})]
|
|
||||||
});
|
|
||||||
TestBed.createComponent(ngFormDeprecated);
|
|
||||||
expect(warnSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should only warn for each instance when global provider is provided with "always"`,
|
|
||||||
() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ngFormDeprecated],
|
|
||||||
imports: [FormsModule.withConfig({warnOnDeprecatedNgFormSelector: 'always'})]
|
|
||||||
});
|
|
||||||
|
|
||||||
TestBed.createComponent(ngFormDeprecated);
|
|
||||||
expect(warnSpy).toHaveBeenCalledTimes(2);
|
|
||||||
expect(warnSpy.calls.mostRecent().args[0])
|
|
||||||
.toMatch(/It looks like you're using 'ngForm'/gi);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -327,8 +327,6 @@ export declare class FormGroupName extends AbstractFormGroupDirective implements
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class FormsModule {
|
export declare class FormsModule {
|
||||||
static withConfig(opts: { warnOnDeprecatedNgFormSelector?: 'never' | 'once' | 'always';
|
|
||||||
}): ModuleWithProviders<FormsModule>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class MaxLengthValidator implements Validator, OnChanges {
|
export declare class MaxLengthValidator implements Validator, OnChanges {
|
||||||
|
@ -397,11 +395,6 @@ export declare class NgForm extends ControlContainer implements Form, AfterViewI
|
||||||
updateModel(dir: NgControl, value: any): void;
|
updateModel(dir: NgControl, value: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated */
|
|
||||||
export declare class NgFormSelectorWarning {
|
|
||||||
constructor(ngFormWarning: string | null);
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare class NgModel extends NgControl implements OnChanges, OnDestroy {
|
export declare class NgModel extends NgControl implements OnChanges, OnDestroy {
|
||||||
readonly asyncValidator: AsyncValidatorFn | null;
|
readonly asyncValidator: AsyncValidatorFn | null;
|
||||||
readonly control: FormControl;
|
readonly control: FormControl;
|
||||||
|
|
Loading…
Reference in New Issue