feat(compiler): introduce <ng-template>
, deprecate <template>
and template
attribute
The rationale of this change is to improve the inter-operability with web components that might make use of the `<template>` tag. DEPRECATION The template tags and template attribute are deprecated: <template ngFor [ngFor]=items let-item><li>...</li></template> <li template="ngFor: let item of items">...</li> should be rewritten as: <ng-template ngFor [ngFor]=items let-item><li>...</li></ng-template> Note that they still be supported in 4.x with a deprecartion warning in development mode. MIGRATION - `template` tags (or elements with a `template` attribute) should be rewritten as a `ng-template` tag, - `ng-content` selectors should be updated to referto a `ng-template` where they use to refer to a template: `<ng-content selector="template[attr]">` should be rewritten as `<ng-content selector="ng-template[attr]">` - if you consume a component relying on your templates being actual `template` elements (that is they include a `<ng-content selector="template[attr]">`). You should still migrate to `ng-template` and make use of `ngProjectAs` to override the way `ng-content` sees the template: `<ng-template projectAs="template[attr]">` - while `template` elements are deprecated in 4.x they continue to work.
This commit is contained in:

committed by
Igor Minar

parent
3f519207a4
commit
bf8eb41248
@ -70,12 +70,12 @@ export class NgForOfRow<T> {
|
||||
* - `<li *ngFor="let item of items; let i = index; trackBy: trackByFn">...</li>`
|
||||
* - `<li template="ngFor let item of items; let i = index; trackBy: trackByFn">...</li>`
|
||||
*
|
||||
* With `<template>` element:
|
||||
* With `<ng-template>` element:
|
||||
*
|
||||
* ```
|
||||
* <template ngFor let-item [ngForOf]="items" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
* <ng-template ngFor let-item [ngForOf]="items" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
* <li>...</li>
|
||||
* </template>
|
||||
* </ng-template>
|
||||
* ```
|
||||
*
|
||||
* ### Example
|
||||
|
@ -26,7 +26,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
|
||||
* # Showing an alternative template using `else`
|
||||
*
|
||||
* If it is necessary to display a template when the `expression` is falsy use the `else` template
|
||||
* binding as shown. Note that the `else` binding points to a `<template>` labeled `#elseBlock`.
|
||||
* binding as shown. Note that the `else` binding points to a `<ng-template>` labeled `#elseBlock`.
|
||||
* The template can be defined anywhere in the component view but is typically placed right after
|
||||
* `ngIf` for readability.
|
||||
*
|
||||
@ -76,25 +76,25 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
|
||||
* Simple form:
|
||||
* - `<div *ngIf="condition">...</div>`
|
||||
* - `<div template="ngIf condition">...</div>`
|
||||
* - `<template [ngIf]="condition"><div>...</div></template>`
|
||||
* - `<ng-template [ngIf]="condition"><div>...</div></ng-template>`
|
||||
*
|
||||
* Form with an else block:
|
||||
* ```
|
||||
* <div *ngIf="condition; else elseBlock">...</div>
|
||||
* <template #elseBlock>...</template>
|
||||
* <ng-template #elseBlock>...</ng-template>
|
||||
* ```
|
||||
*
|
||||
* Form with a `then` and `else` block:
|
||||
* ```
|
||||
* <div *ngIf="condition; then thenBlock else elseBlock"></div>
|
||||
* <template #thenBlock>...</template>
|
||||
* <template #elseBlock>...</template>
|
||||
* <ng-template #thenBlock>...</ng-template>
|
||||
* <ng-template #elseBlock>...</ng-template>
|
||||
* ```
|
||||
*
|
||||
* Form with storing the value locally:
|
||||
* ```
|
||||
* <div *ngIf="condition; else elseBlock; let value">{{value}}</div>
|
||||
* <template #elseBlock>...</template>
|
||||
* <ng-template #elseBlock>...</ng-template>
|
||||
* ```
|
||||
*
|
||||
* @stable
|
||||
|
@ -18,7 +18,7 @@ export function main() {
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); });
|
||||
|
||||
it('should do nothing if component is null', async(() => {
|
||||
const template = `<template *ngComponentOutlet="currentComponent"></template>`;
|
||||
const template = `<ng-template *ngComponentOutlet="currentComponent"></ng-template>`;
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
@ -120,7 +120,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should render projectable nodes, if supplied', async(() => {
|
||||
const template = `<template>projected foo</template>${TEST_CMP_TEMPLATE}`;
|
||||
const template = `<ng-template>projected foo</ng-template>${TEST_CMP_TEMPLATE}`;
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
||||
|
||||
@ -221,7 +221,7 @@ class InjectedComponentAgain {
|
||||
}
|
||||
|
||||
const TEST_CMP_TEMPLATE =
|
||||
`<template *ngComponentOutlet="currentComponent; injector: injector; content: projectables; ngModuleFactory: module;"></template>`;
|
||||
`<ng-template *ngComponentOutlet="currentComponent; injector: injector; content: projectables; ngModuleFactory: module;"></ng-template>`;
|
||||
@Component({selector: 'test-cmp', template: TEST_CMP_TEMPLATE})
|
||||
class TestComponent {
|
||||
currentComponent: Type<any>;
|
||||
|
@ -243,7 +243,7 @@ export function main() {
|
||||
it('should allow to use a custom template', async(() => {
|
||||
const template =
|
||||
'<ng-container *ngFor="let item of items; template: tpl"></ng-container>' +
|
||||
'<template let-item let-i="index" #tpl><p>{{i}}: {{item}};</p></template>';
|
||||
'<ng-template let-item let-i="index" #tpl><p>{{i}}: {{item}};</p></ng-template>';
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
@ -262,7 +262,7 @@ export function main() {
|
||||
it('should use a custom template when both default and a custom one are present', async(() => {
|
||||
const template =
|
||||
'<ng-container *ngFor="let item of items; template: tpl">{{i}};</ng-container>' +
|
||||
'<template let-item let-i="index" #tpl>{{i}}: {{item}};</template>';
|
||||
'<ng-template let-item let-i="index" #tpl>{{i}}: {{item}};</ng-template>';
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
|
@ -37,7 +37,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should work on a template element', async(() => {
|
||||
const template = '<template [ngIf]="booleanCondition">hello2</template>';
|
||||
const template = '<ng-template [ngIf]="booleanCondition">hello2</ng-template>';
|
||||
fixture = createTestComponent(template);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('hello2');
|
||||
@ -141,7 +141,7 @@ export function main() {
|
||||
describe('else', () => {
|
||||
it('should support else', async(() => {
|
||||
const template = '<span *ngIf="booleanCondition; else elseBlock">TRUE</span>' +
|
||||
'<template #elseBlock>FALSE</template>';
|
||||
'<ng-template #elseBlock>FALSE</ng-template>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -156,8 +156,8 @@ export function main() {
|
||||
it('should support then and else', async(() => {
|
||||
const template =
|
||||
'<span *ngIf="booleanCondition; then thenBlock; else elseBlock">IGNORE</span>' +
|
||||
'<template #thenBlock>THEN</template>' +
|
||||
'<template #elseBlock>ELSE</template>';
|
||||
'<ng-template #thenBlock>THEN</ng-template>' +
|
||||
'<ng-template #elseBlock>ELSE</ng-template>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -172,8 +172,8 @@ export function main() {
|
||||
it('should support dynamic else', async(() => {
|
||||
const template =
|
||||
'<span *ngIf="booleanCondition; else nestedBooleanCondition ? b1 : b2">TRUE</span>' +
|
||||
'<template #b1>FALSE1</template>' +
|
||||
'<template #b2>FALSE2</template>';
|
||||
'<ng-template #b1>FALSE1</ng-template>' +
|
||||
'<ng-template #b2>FALSE2</ng-template>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -191,7 +191,7 @@ export function main() {
|
||||
|
||||
it('should support binding to variable', async(() => {
|
||||
const template = '<span *ngIf="booleanCondition; else elseBlock; let v">{{v}}</span>' +
|
||||
'<template #elseBlock let-v>{{v}}</template>';
|
||||
'<ng-template #elseBlock let-v>{{v}}</ng-template>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
|
@ -34,8 +34,8 @@ export function main() {
|
||||
|
||||
it('should display the template according to the exact value', async(() => {
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
|
||||
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
|
||||
'<ng-template ngPluralCase="=0"><li>you have no messages.</li></ng-template>' +
|
||||
'<ng-template ngPluralCase="=1"><li>you have one message.</li></ng-template>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
@ -67,7 +67,7 @@ export function main() {
|
||||
// https://github.com/angular/angular/issues/9882
|
||||
it('should not throw when ngPluralCase contains expressions', async(() => {
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
|
||||
'<ng-template ngPluralCase="=0"><li>{{ switchValue }}</li></ng-template>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
@ -79,8 +79,8 @@ export function main() {
|
||||
|
||||
it('should be applicable to <ng-container> elements', async(() => {
|
||||
const template = '<ng-container [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0">you have no messages.</template>' +
|
||||
'<template ngPluralCase="=1">you have one message.</template>' +
|
||||
'<ng-template ngPluralCase="=0">you have no messages.</ng-template>' +
|
||||
'<ng-template ngPluralCase="=1">you have one message.</ng-template>' +
|
||||
'</ng-container>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
@ -94,8 +94,8 @@ export function main() {
|
||||
|
||||
it('should display the template according to the category', async(() => {
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
|
||||
'<ng-template ngPluralCase="few"><li>you have a few messages.</li></ng-template>' +
|
||||
'<ng-template ngPluralCase="many"><li>you have many messages.</li></ng-template>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
@ -109,8 +109,8 @@ export function main() {
|
||||
|
||||
it('should default to other when no matches are found', async(() => {
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="other"><li>default message.</li></template>' +
|
||||
'<ng-template ngPluralCase="few"><li>you have a few messages.</li></ng-template>' +
|
||||
'<ng-template ngPluralCase="other"><li>default message.</li></ng-template>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
@ -121,8 +121,8 @@ export function main() {
|
||||
|
||||
it('should prioritize value matches over category matches', async(() => {
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="=2">you have two messages.</template>' +
|
||||
'<ng-template ngPluralCase="few"><li>you have a few messages.</li></ng-template>' +
|
||||
'<ng-template ngPluralCase="=2">you have two messages.</ng-template>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
@ -41,14 +41,14 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should insert content specified by TemplateRef', async(() => {
|
||||
const template = `<template #tpl>foo</template>` +
|
||||
const template = `<ng-template #tpl>foo</ng-template>` +
|
||||
`<ng-container [ngTemplateOutlet]="tpl"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('foo');
|
||||
}));
|
||||
|
||||
it('should clear content if TemplateRef becomes `null`', async(() => {
|
||||
const template = `<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs>` +
|
||||
const template = `<tpl-refs #refs="tplRefs"><ng-template>foo</ng-template></tpl-refs>` +
|
||||
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.detectChanges();
|
||||
@ -63,7 +63,7 @@ export function main() {
|
||||
|
||||
it('should swap content if TemplateRef changes', async(() => {
|
||||
const template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs>` +
|
||||
`<tpl-refs #refs="tplRefs"><ng-template>foo</ng-template><ng-template>bar</ng-template></tpl-refs>` +
|
||||
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -78,14 +78,14 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should display template if context is `null`', async(() => {
|
||||
const template = `<template #tpl>foo</template>` +
|
||||
const template = `<ng-template #tpl>foo</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: null"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('foo');
|
||||
}));
|
||||
|
||||
it('should reflect initial context and changes', async(() => {
|
||||
const template = `<template let-foo="foo" #tpl>{{foo}}</template>` +
|
||||
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -97,7 +97,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should reflect user defined `$implicit` property in the context', async(() => {
|
||||
const template = `<template let-ctx #tpl>{{ctx.foo}}</template>` +
|
||||
const template = `<ng-template let-ctx #tpl>{{ctx.foo}}</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
|
||||
@ -105,7 +105,8 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should reflect context re-binding', async(() => {
|
||||
const template = `<template let-shawshank="shawshank" #tpl>{{shawshank}}</template>` +
|
||||
const template =
|
||||
`<ng-template let-shawshank="shawshank" #tpl>{{shawshank}}</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
|
Reference in New Issue
Block a user