feat(compiler): set `enableLegacyTemplate` to false by default (#18756)

BREAKING CHANGE: the compiler option `enableLegacyTemplate` is now disabled by default as the `<template>` element has been deprecated since v4. Use `<ng-template>` instead. The option `enableLegacyTemplate` and the `<template>` element will both be removed in Angular v6.
PR Close #18756
This commit is contained in:
Olivier Combe 2017-08-17 17:18:33 +02:00 committed by Jason Aden
parent fcadeb2079
commit 56238fe94e
22 changed files with 49 additions and 82 deletions

View File

@ -92,22 +92,22 @@
</div> </div>
<div> <div>
The <code>hero.id</code> in the &lt;template *ngIf&gt; disappears: The <code>hero.id</code> in the &lt;ng-template *ngIf&gt; disappears:
<p> <p>
<template *ngIf="showId"> <ng-template *ngIf="showId">
Id: ({{hero.id}}) Id: ({{hero.id}})
</template> </ng-template>
Name: {{hero.name}} Name: {{hero.name}}
</p> </p>
</div> </div>
<div> <div>
The <code>hero.id</code> in the &lt;template [ngIf]&gt; The <code>hero.id</code> in the &lt;ng-template [ngIf]&gt;
is unaffected by the <span>p-span</span> CSS: is unaffected by the <span>p-span</span> CSS:
<p> <p>
<template [ngIf]="showId"> <ng-template [ngIf]="showId">
Id: ({{hero.id}}) Id: ({{hero.id}})
</template> </ng-template>
Name: {{hero.name}} Name: {{hero.name}}
</p> </p>
</div> </div>

View File

@ -18,9 +18,9 @@ describe('Structural Directives', function () {
expect(allLis.get(0).getText()).toEqual('Mr. Nice'); expect(allLis.get(0).getText()).toEqual('Mr. Nice');
}); });
it('ngSwitch have three <happy-hero> instances', function () { it('ngSwitch have two <happy-hero> instances', function () {
const happyHeroEls = element.all(by.tagName('happy-hero')); const happyHeroEls = element.all(by.tagName('happy-hero'));
expect(happyHeroEls.count()).toEqual(3); expect(happyHeroEls.count()).toEqual(2);
}); });
it('should toggle *ngIf="hero" with a button', function () { it('should toggle *ngIf="hero" with a button', function () {

View File

@ -55,11 +55,6 @@
</ng-template> </ng-template>
<!-- #enddocregion ngif-template --> <!-- #enddocregion ngif-template -->
<p>template attribute</p>
<!-- #docregion ngif-template-attr -->
<div template="ngIf hero">{{hero.name}}</div>
<!-- #enddocregion ngif-template-attr -->
<hr> <hr>
<h2 id="ng-container">&lt;ng-container&gt;</h2> <h2 id="ng-container">&lt;ng-container&gt;</h2>
@ -131,14 +126,7 @@
</div> </div>
<!--#enddocregion inside-ngfor --> <!--#enddocregion inside-ngfor -->
<p class="code">&lt;div template="ngFor let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"&gt;</p> <p class="code">&lt;ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"/&gt;</p>
<!--#docregion inside-ngfor -->
<div template="ngFor let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">
({{i}}) {{hero.name}}
</div>
<!--#enddocregion inside-ngfor -->
<p class="code">&lt;template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"&gt;</p>
<!--#docregion inside-ngfor --> <!--#docregion inside-ngfor -->
<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"> <ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
<div [class.odd]="odd">({{i}}) {{hero.name}}</div> <div [class.odd]="odd">({{i}}) {{hero.name}}</div>
@ -169,16 +157,6 @@
</div> </div>
<!-- #enddocregion built-in, ngswitch --> <!-- #enddocregion built-in, ngswitch -->
<h4>NgSwitch with <i>template</i> attribute</h4>
<!-- #docregion ngswitch-template-attr -->
<div [ngSwitch]="hero?.emotion">
<happy-hero template="ngSwitchCase 'happy'" [hero]="hero"></happy-hero>
<sad-hero template="ngSwitchCase 'sad'" [hero]="hero"></sad-hero>
<confused-hero template="ngSwitchCase 'confused'" [hero]="hero"></confused-hero>
<unknown-hero template="ngSwitchDefault" [hero]="hero"></unknown-hero>
</div>
<!-- #enddocregion ngswitch-template-attr -->
<h4>NgSwitch with &lt;ng-template&gt;</h4> <h4>NgSwitch with &lt;ng-template&gt;</h4>
<!-- #docregion ngswitch-template --> <!-- #docregion ngswitch-template -->
<div [ngSwitch]="hero?.emotion"> <div [ngSwitch]="hero?.emotion">
@ -199,7 +177,7 @@
<hr> <hr>
<h2>&lt;template&gt;</h2> <h2>&lt;ng-template&gt;</h2>
<!-- #docregion template-tag --> <!-- #docregion template-tag -->
<p>Hip!</p> <p>Hip!</p>
<ng-template> <ng-template>
@ -238,13 +216,13 @@
<p *myUnless="condition">Show this sentence unless the condition is true.</p> <p *myUnless="condition">Show this sentence unless the condition is true.</p>
<!-- #enddocregion myUnless-1 --> <!-- #enddocregion myUnless-1 -->
<p template="myUnless condition" class="code unless"> <p *myUnless="condition" class="code unless">
(A) &lt;p template="myUnless condition" class="code unless"&gt; (A) &lt;p *myUnless="condition" class="code unless"&gt;
</p> </p>
<ng-template [myUnless]="condition"> <ng-template [myUnless]="condition">
<p class="code unless"> <p class="code unless">
(A) &lt;template [myUnless]="condition"&gt; (A) &lt;ng-template [myUnless]="condition"&gt;
</p> </p>
</ng-template> </ng-template>

View File

@ -19,8 +19,7 @@ import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
* ### Syntax * ### Syntax
* *
* - `<div *myUnless="condition">...</div>` * - `<div *myUnless="condition">...</div>`
* - `<div template="myUnless condition">...</div>` * - `<ng-template [myUnless]="condition"><div>...</div></ng-template>`
* - `<template [myUnless]="condition"><div>...</div></template>`
* *
// #docregion no-docs // #docregion no-docs
*/ */

View File

@ -78,11 +78,11 @@
<p>Template input variable expression context (let hero)</p> <p>Template input variable expression context (let hero)</p>
<!-- template hides the following; plenty of examples later --> <!-- template hides the following; plenty of examples later -->
<template> <ng-template>
<!-- #docregion context-var --> <!-- #docregion context-var -->
<div *ngFor="let hero of heroes">{{hero.name}}</div> <div *ngFor="let hero of heroes">{{hero.name}}</div>
<!-- #enddocregion context-var --> <!-- #enddocregion context-var -->
</template> </ng-template>
<p>Template reference variable expression context (#heroInput)</p> <p>Template reference variable expression context (#heroInput)</p>
<div (keyup)="0" class="context"> <div (keyup)="0" class="context">

View File

@ -208,17 +208,7 @@ Here is `*ngIf` displaying the hero's name if `hero` exists.
The asterisk is "syntactic sugar" for something a bit more complicated. The asterisk is "syntactic sugar" for something a bit more complicated.
Internally, Angular desugars it in two stages. Internally, Angular translates the `*ngIf` _attribute_ into a `<ng-template>` _element_, wrapped around the host element, like this.
First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`,&nbsp; like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template-attr)" region="ngif-template-attr">
</code-example>
Then it translates the template _attribute_ into a `<ng-template>` _element_, wrapped around the host element, like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template)" region="ngif-template"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template)" region="ngif-template">
@ -230,8 +220,7 @@ Then it translates the template _attribute_ into a `<ng-template>` _element_, wr
* The `*ngIf` directive moved to the `<ng-template>` element where it became a property binding,`[ngIf]`. * The `*ngIf` directive moved to the `<ng-template>` element where it became a property binding,`[ngIf]`.
* The rest of the `<div>`, including its class attribute, moved inside the `<ng-template>` element. * The rest of the `<div>`, including its class attribute, moved inside the `<ng-template>` element.
None of these forms are actually rendered. The first form is not actually rendered, only the finished product ends up in the DOM.
Only the finished product ends up in the DOM.
<figure> <figure>
@ -252,10 +241,9 @@ The [`NgFor`](guide/structural-directives#ngFor) and [`NgSwitch...`](guide/struc
## Inside _*ngFor_ ## Inside _*ngFor_
Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax through Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax to `<ng-template>` _element_.
template _attribute_ to `<ng-template>` _element_.
Here's a full-featured application of `NgFor`, written all three ways: Here's a full-featured application of `NgFor`, written both ways:
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (inside-ngfor)" region="inside-ngfor"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (inside-ngfor)" region="inside-ngfor">
@ -415,16 +403,7 @@ The `<unknown-hero>` is the host element for the `*ngSwitchDefault`.
As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault` As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
can be desugared into the template _attribute_ form. can be desugared into the `<ng-template>` element form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template-attr)" region="ngswitch-template-attr">
</code-example>
That, in turn, can be desugared into the `<ng-template>` element form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template)" region="ngswitch-template"> <code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template)" region="ngswitch-template">
@ -438,7 +417,7 @@ That, in turn, can be desugared into the `<ng-template>` element form.
## Prefer the asterisk (*) syntax. ## Prefer the asterisk (*) syntax.
The asterisk (*) syntax is more clear than the other desugared forms. The asterisk (*) syntax is more clear than the desugared form.
Use [&lt;ng-container&gt;](guide/structural-directives#ng-container) when there's no single element Use [&lt;ng-container&gt;](guide/structural-directives#ng-container) when there's no single element
to host the directive. to host the directive.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -79,7 +79,6 @@ export class NgForOfContext<T> {
* ### Syntax * ### Syntax
* *
* - `<li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>` * - `<li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>`
* - `<li template="ngFor let item of items; index as i; trackBy: trackByFn">...</li>`
* *
* With `<ng-template>` element: * With `<ng-template>` element:
* *

View File

@ -75,7 +75,6 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
* *
* Simple form: * Simple form:
* - `<div *ngIf="condition">...</div>` * - `<div *ngIf="condition">...</div>`
* - `<div template="ngIf condition">...</div>`
* - `<ng-template [ngIf]="condition"><div>...</div></ng-template>` * - `<ng-template [ngIf]="condition"><div>...</div></ng-template>`
* *
* Form with an else block: * Form with an else block:

View File

@ -101,7 +101,7 @@ export class CodeGenerator {
translations: transContent, translations: transContent,
i18nFormat: cliOptions.i18nFormat || undefined, i18nFormat: cliOptions.i18nFormat || undefined,
locale: cliOptions.locale || undefined, missingTranslation, locale: cliOptions.locale || undefined, missingTranslation,
enableLegacyTemplate: options.enableLegacyTemplate !== false, enableLegacyTemplate: options.enableLegacyTemplate === true,
enableSummariesForJit: options.enableSummariesForJit !== false, enableSummariesForJit: options.enableSummariesForJit !== false,
preserveWhitespaces: options.preserveWhitespaces, preserveWhitespaces: options.preserveWhitespaces,
}); });

View File

@ -90,7 +90,7 @@ export interface CompilerOptions extends ts.CompilerOptions {
// Print extra information while running the compiler // Print extra information while running the compiler
trace?: boolean; trace?: boolean;
// Whether to enable support for <template> and the template attribute (true by default) // Whether to enable support for <template> and the template attribute (false by default)
enableLegacyTemplate?: boolean; enableLegacyTemplate?: boolean;
// Whether to enable lowering expressions lambdas and expressions in a reference value // Whether to enable lowering expressions lambdas and expressions in a reference value

View File

@ -65,7 +65,7 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
const config = new CompilerConfig({ const config = new CompilerConfig({
defaultEncapsulation: ViewEncapsulation.Emulated, defaultEncapsulation: ViewEncapsulation.Emulated,
useJit: false, useJit: false,
enableLegacyTemplate: options.enableLegacyTemplate !== false, enableLegacyTemplate: options.enableLegacyTemplate === true,
missingTranslation: options.missingTranslation, missingTranslation: options.missingTranslation,
preserveWhitespaces: options.preserveWhitespaces, preserveWhitespaces: options.preserveWhitespaces,
}); });

View File

@ -36,7 +36,7 @@ export class CompilerConfig {
this.useJit = !!useJit; this.useJit = !!useJit;
this.jitDevMode = !!jitDevMode; this.jitDevMode = !!jitDevMode;
this.missingTranslation = missingTranslation || null; this.missingTranslation = missingTranslation || null;
this.enableLegacyTemplate = enableLegacyTemplate !== false; this.enableLegacyTemplate = enableLegacyTemplate === true;
this.preserveWhitespaces = preserveWhitespacesDefault(noUndefined(preserveWhitespaces)); this.preserveWhitespaces = preserveWhitespacesDefault(noUndefined(preserveWhitespaces));
} }
} }

View File

@ -121,16 +121,19 @@ export function main() {
schemas?: SchemaMetadata[], preserveWhitespaces?: boolean) => TemplateAst[]; schemas?: SchemaMetadata[], preserveWhitespaces?: boolean) => TemplateAst[];
let console: ArrayConsole; let console: ArrayConsole;
function commonBeforeEach() { function configureCompiler() {
console = new ArrayConsole();
beforeEach(() => { beforeEach(() => {
console = new ArrayConsole();
TestBed.configureCompiler({ TestBed.configureCompiler({
providers: [ providers: [
{provide: Console, useValue: console}, {provide: Console, useValue: console},
{provide: CompilerConfig, useValue: new CompilerConfig({enableLegacyTemplate: true})}
], ],
}); });
}); });
}
function commonBeforeEach() {
beforeEach(inject([TemplateParser], (parser: TemplateParser) => { beforeEach(inject([TemplateParser], (parser: TemplateParser) => {
const someAnimation = new CompileAnimationEntryMetadata('someAnimation', []); const someAnimation = new CompileAnimationEntryMetadata('someAnimation', []);
const someTemplate = compileTemplateMetadata({animations: [someAnimation]}); const someTemplate = compileTemplateMetadata({animations: [someAnimation]});
@ -286,6 +289,7 @@ export function main() {
}); });
}); });
configureCompiler();
commonBeforeEach(); commonBeforeEach();
describe('security context', () => { describe('security context', () => {
@ -318,6 +322,7 @@ export function main() {
TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]});
}); });
configureCompiler();
commonBeforeEach(); commonBeforeEach();
describe('parse', () => { describe('parse', () => {
@ -2129,13 +2134,13 @@ The pipe 'test' could not be found ("{{[ERROR ->]a | test}}"): TestComp@0:2`);
}); });
describe('Template Parser - opt-out `<template>` support', () => { describe('Template Parser - `<template>` support disabled by default', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureCompiler({ TestBed.configureCompiler({
providers: [{ providers: [
provide: CompilerConfig, {provide: Console, useValue: console},
useValue: new CompilerConfig({enableLegacyTemplate: false}), {provide: CompilerConfig, useValue: new CompilerConfig()}
}], ],
}); });
}); });

View File

@ -7,6 +7,7 @@
*/ */
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {CompilerConfig} from '@angular/compiler';
import {Compiler, ComponentFactory, ComponentRef, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, SkipSelf, ViewRef} from '@angular/core'; import {Compiler, ComponentFactory, ComponentRef, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, SkipSelf, ViewRef} from '@angular/core';
import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection'; import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection';
import {getDebugContext} from '@angular/core/src/errors'; import {getDebugContext} from '@angular/core/src/errors';
@ -37,7 +38,14 @@ export function main() {
function declareTests({useJit}: {useJit: boolean}) { function declareTests({useJit}: {useJit: boolean}) {
describe('integration tests', function() { describe('integration tests', function() {
beforeEach(() => { TestBed.configureCompiler({useJit}); }); beforeEach(() => {
TestBed.configureCompiler({
useJit,
providers: [
{provide: CompilerConfig, useValue: new CompilerConfig({enableLegacyTemplate: true})}
]
});
});
describe('react to record changes', function() { describe('react to record changes', function() {
it('should consume text node changes', () => { it('should consume text node changes', () => {

View File

@ -160,7 +160,7 @@ export class JitCompilerFactory implements CompilerFactory {
useJit: true, useJit: true,
defaultEncapsulation: ViewEncapsulation.Emulated, defaultEncapsulation: ViewEncapsulation.Emulated,
missingTranslation: MissingTranslationStrategy.Warning, missingTranslation: MissingTranslationStrategy.Warning,
enableLegacyTemplate: true, enableLegacyTemplate: false,
}; };
this._defaultOptions = [compilerOptions, ...defaultOptions]; this._defaultOptions = [compilerOptions, ...defaultOptions];

View File

@ -76,7 +76,7 @@ interface Options extends ts.CompilerOptions {
// Print extra information while running the compiler // Print extra information while running the compiler
trace?: boolean; trace?: boolean;
// Whether to enable support for <template> and the template attribute (true by default) // Whether to enable support for <template> and the template attribute (false by default)
enableLegacyTemplate?: boolean; enableLegacyTemplate?: boolean;
// Whether to generate .ngsummary.ts files that allow to use AOTed artifacts // Whether to generate .ngsummary.ts files that allow to use AOTed artifacts