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
@ -286,7 +286,7 @@ export function main() {
|
||||
vc: ViewContainerRef;
|
||||
}
|
||||
|
||||
@Component({template: '<template #t>Dynamic content</template>'})
|
||||
@Component({template: '<ng-template #t>Dynamic content</ng-template>'})
|
||||
class EmbeddedViewComp {
|
||||
@ViewChild(TemplateRef)
|
||||
tplRef: TemplateRef<Object>;
|
||||
|
@ -485,8 +485,8 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
fakeAsync(() => { expect(_bindAndCheckSimpleValue('"$"')).toEqual(['someProp=$']); }));
|
||||
|
||||
it('should read locals', fakeAsync(() => {
|
||||
const ctx =
|
||||
createCompFixture('<template testLocals let-local="someLocal">{{local}}</template>');
|
||||
const ctx = createCompFixture(
|
||||
'<ng-template testLocals let-local="someLocal">{{local}}</ng-template>');
|
||||
ctx.detectChanges(false);
|
||||
|
||||
expect(renderLog.log).toEqual(['{{someLocalValue}}']);
|
||||
@ -1242,7 +1242,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
|
||||
it('should recurse into nested view containers even if there are no bindings in the component view',
|
||||
() => {
|
||||
@Component({template: '<template #vc>{{name}}</template>'})
|
||||
@Component({template: '<ng-template #vc>{{name}}</ng-template>'})
|
||||
class Comp {
|
||||
name = 'Tom';
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
|
||||
|
@ -130,7 +130,7 @@ export function main() {
|
||||
it('should support using structural directives with ngTemplateOutlet', () => {
|
||||
@Component({
|
||||
template:
|
||||
'<child [templateCtx]="templateCtx"><template let-shown="shown" #tpl><span *ngIf="shown">hello</span></template></child>'
|
||||
'<child [templateCtx]="templateCtx"><ng-template let-shown="shown" #tpl><span *ngIf="shown">hello</span></ng-template></child>'
|
||||
})
|
||||
class Parent {
|
||||
templateCtx = {shown: false};
|
||||
|
@ -363,10 +363,10 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
});
|
||||
|
||||
it('should support template directives via `<template>` elements.', () => {
|
||||
it('should support template directives via `<ng-template>` elements.', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, SomeViewport]});
|
||||
const template =
|
||||
'<template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></template>';
|
||||
'<ng-template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></ng-template>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
@ -382,7 +382,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
it('should not share empty context for template directives - issue #10045', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, PollutedContext, NoContext]});
|
||||
const template =
|
||||
'<template pollutedContext let-foo="bar">{{foo}}</template><template noContext let-foo="bar">{{foo}}</template>';
|
||||
'<ng-template pollutedContext let-foo="bar">{{foo}}</ng-template><ng-template noContext let-foo="bar">{{foo}}</ng-template>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
@ -393,7 +393,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
it('should not detach views in ViewContainers when the parent view is destroyed.', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, SomeViewport]});
|
||||
const template =
|
||||
'<div *ngIf="ctxBoolProp"><template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></template></div>';
|
||||
'<div *ngIf="ctxBoolProp"><ng-template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></ng-template></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
@ -412,11 +412,11 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
expect(fixture.debugElement.children.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should use a comment while stamping out `<template>` elements.', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||
const template = '<template></template>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
it('should use a comment while stamping out `<ng-template>` elements.', () => {
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [MyComp]})
|
||||
.overrideComponent(MyComp, {set: {template: '<ng-template></ng-template>'}})
|
||||
.createComponent(MyComp);
|
||||
|
||||
const childNodesOfWrapper = getDOM().childNodes(fixture.nativeElement);
|
||||
expect(childNodesOfWrapper.length).toBe(1);
|
||||
@ -448,7 +448,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
});
|
||||
const template =
|
||||
'<some-directive><toolbar><template toolbarpart let-toolbarProp="toolbarProp">{{ctxProp}},{{toolbarProp}},<cmp-with-host></cmp-with-host></template></toolbar></some-directive>';
|
||||
'<some-directive><toolbar><ng-template toolbarpart let-toolbarProp="toolbarProp">{{ctxProp}},{{toolbarProp}},<cmp-with-host></cmp-with-host></ng-template></toolbar></some-directive>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
@ -484,7 +484,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ChildComp]});
|
||||
const template =
|
||||
'<template [ngIf]="true">{{alice.ctxProp}}</template>|{{alice.ctxProp}}|<child-cmp ref-alice></child-cmp>';
|
||||
'<ng-template [ngIf]="true">{{alice.ctxProp}}</ng-template>|{{alice.ctxProp}}|<child-cmp ref-alice></child-cmp>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
@ -530,10 +530,10 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
});
|
||||
|
||||
it('should assign the TemplateRef to a user-defined variable', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||
const template = '<template ref-alice></template>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [MyComp]})
|
||||
.overrideComponent(MyComp, {set: {template: '<template ref-alice></template>'}})
|
||||
.createComponent(MyComp);
|
||||
|
||||
const value = fixture.debugElement.childNodes[0].references['alice'];
|
||||
expect(value.createEmbeddedView).toBeTruthy();
|
||||
@ -552,14 +552,16 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
|
||||
describe('variables', () => {
|
||||
it('should allow to use variables in a for loop', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ChildCompNoTemplate]});
|
||||
const template =
|
||||
'<template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
'<ng-template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</ng-template>';
|
||||
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ChildCompNoTemplate]})
|
||||
.overrideComponent(MyComp, {set: {template}})
|
||||
.createComponent(MyComp);
|
||||
|
||||
fixture.detectChanges();
|
||||
// Get the element at index 2, since index 0 is the <template>.
|
||||
// Get the element at index 2, since index 0 is the <ng-template>.
|
||||
expect(getDOM().childNodes(fixture.nativeElement)[2]).toHaveText('1-hello');
|
||||
});
|
||||
});
|
||||
@ -774,11 +776,17 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
}));
|
||||
|
||||
it('should support events via EventEmitter on template elements', async(() => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, DirectiveEmittingEvent, DirectiveListeningEvent]});
|
||||
const template = '<template emitter listener (event)="ctxProp=$event"></template>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
const fixture =
|
||||
TestBed
|
||||
.configureTestingModule(
|
||||
{declarations: [MyComp, DirectiveEmittingEvent, DirectiveListeningEvent]})
|
||||
.overrideComponent(MyComp, {
|
||||
set: {
|
||||
template:
|
||||
'<ng-template emitter listener (event)="ctxProp=$event"></ng-template>'
|
||||
}
|
||||
})
|
||||
.createComponent(MyComp);
|
||||
|
||||
const tc = fixture.debugElement.childNodes[0];
|
||||
|
||||
@ -1487,10 +1495,11 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
|
||||
});
|
||||
|
||||
it('should reflect property values on template comments', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||
const template = '<template [ngIf]="ctxBoolProp"></template>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [MyComp]})
|
||||
.overrideComponent(
|
||||
MyComp, {set: {template: '<ng-template [ngIf]="ctxBoolProp"></ng-template>'}})
|
||||
.createComponent(MyComp);
|
||||
|
||||
fixture.componentInstance.ctxBoolProp = true;
|
||||
fixture.detectChanges();
|
||||
|
@ -132,7 +132,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
set: {
|
||||
template: '<multiple-content-tags>' +
|
||||
'<template manual class="left"><div>A1</div></template>' +
|
||||
'<ng-template manual class="left"><div>A1</div></ng-template>' +
|
||||
'<div>B</div>' +
|
||||
'</multiple-content-tags>'
|
||||
}
|
||||
@ -175,7 +175,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
set: {
|
||||
template: '<outer>' +
|
||||
'<template manual class="left"><div>A</div></template>' +
|
||||
'<ng-template manual class="left"><div>A</div></ng-template>' +
|
||||
'<div>B</div>' +
|
||||
'<div>C</div>' +
|
||||
'</outer>'
|
||||
@ -260,7 +260,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
set: {
|
||||
template: '<empty>' +
|
||||
' <template manual><div>A</div></template>' +
|
||||
' <ng-template manual><div>A</div></ng-template>' +
|
||||
'</empty>' +
|
||||
'START(<div project></div>)END'
|
||||
}
|
||||
@ -282,7 +282,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
{declarations: [Empty, ProjectDirective, ManualViewportDirective]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
set: {
|
||||
template: '<simple><template manual><div>A</div></template></simple>' +
|
||||
template: '<simple><ng-template manual><div>A</div></ng-template></simple>' +
|
||||
'START(<div project></div>)END'
|
||||
}
|
||||
});
|
||||
@ -488,7 +488,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
set: {
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<template manual class="left">B</template>' +
|
||||
'<ng-template manual class="left">B</ng-template>' +
|
||||
'<div class="left">C</div>' +
|
||||
'<div>D</div>' +
|
||||
'</conditional-content>'
|
||||
@ -628,7 +628,7 @@ class ConditionalContentComponent {
|
||||
@Component({
|
||||
selector: 'conditional-text',
|
||||
template:
|
||||
'MAIN(<template manual>FIRST(<template manual>SECOND(<ng-content></ng-content>)</template>)</template>)',
|
||||
'MAIN(<ng-template manual>FIRST(<ng-template manual>SECOND(<ng-content></ng-content>)</ng-template>)</ng-template>)',
|
||||
})
|
||||
class ConditionalTextComponent {
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
|
||||
describe('query for TemplateRef', () => {
|
||||
it('should find TemplateRefs in the light and shadow dom', () => {
|
||||
const template = '<needs-tpl><template><div>light</div></template></needs-tpl>';
|
||||
const template = '<needs-tpl><ng-template><div>light</div></ng-template></needs-tpl>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const needsTpl: NeedsTpl = view.debugElement.children[0].injector.get(NeedsTpl);
|
||||
|
||||
@ -237,7 +237,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
|
||||
it('should find named TemplateRefs', () => {
|
||||
const template =
|
||||
'<needs-named-tpl><template #tpl><div>light</div></template></needs-named-tpl>';
|
||||
'<needs-named-tpl><ng-template #tpl><div>light</div></ng-template></needs-named-tpl>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const needsTpl: NeedsNamedTpl = view.debugElement.children[0].injector.get(NeedsNamedTpl);
|
||||
expect(needsTpl.vc.createEmbeddedView(needsTpl.contentTpl).rootNodes[0])
|
||||
@ -308,7 +308,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
|
||||
it('should support reading a ViewContainer', () => {
|
||||
const template =
|
||||
'<needs-viewcontainer-read><template>hello</template></needs-viewcontainer-read>';
|
||||
'<needs-viewcontainer-read><ng-template>hello</ng-template></needs-viewcontainer-read>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
||||
const comp: NeedsViewContainerWithRead =
|
||||
@ -403,7 +403,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
|
||||
it('should contain all the elements in the light dom with the given var binding', () => {
|
||||
const template = '<needs-query-by-ref-binding #q>' +
|
||||
'<div template="ngFor: let item of list">' +
|
||||
'<div *ngFor="let item of list">' +
|
||||
'<div #textLabel>{{item}}</div>' +
|
||||
'</div>' +
|
||||
'</needs-query-by-ref-binding>';
|
||||
@ -536,7 +536,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
describe('query over moved templates', () => {
|
||||
it('should include manually projected templates in queries', () => {
|
||||
const template =
|
||||
'<manual-projecting #q><template><div text="1"></div></template></manual-projecting>';
|
||||
'<manual-projecting #q><ng-template><div text="1"></div></ng-template></manual-projecting>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references['q'];
|
||||
expect(q.query.length).toBe(0);
|
||||
@ -730,14 +730,15 @@ class NeedsViewQueryOrderWithParent {
|
||||
list: string[] = ['2', '3'];
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-tpl', template: '<template><div>shadow</div></template>'})
|
||||
@Component({selector: 'needs-tpl', template: '<ng-template><div>shadow</div></ng-template>'})
|
||||
class NeedsTpl {
|
||||
@ViewChildren(TemplateRef) viewQuery: QueryList<TemplateRef<Object>>;
|
||||
@ContentChildren(TemplateRef) query: QueryList<TemplateRef<Object>>;
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-named-tpl', template: '<template #tpl><div>shadow</div></template>'})
|
||||
@Component(
|
||||
{selector: 'needs-named-tpl', template: '<ng-template #tpl><div>shadow</div></ng-template>'})
|
||||
class NeedsNamedTpl {
|
||||
@ViewChild('tpl') viewTpl: TemplateRef<Object>;
|
||||
@ContentChild('tpl') contentTpl: TemplateRef<Object>;
|
||||
@ -767,7 +768,7 @@ class NeedsContentChildTemplateRef {
|
||||
@Component({
|
||||
selector: 'needs-content-child-template-ref-app',
|
||||
template: '<needs-content-child-template-ref>' +
|
||||
'<template>OUTER<template>INNER</template></template>' +
|
||||
'<ng-template>OUTER<ng-template>INNER</ng-template></ng-template>' +
|
||||
'</needs-content-child-template-ref>'
|
||||
})
|
||||
class NeedsContentChildTemplateRefApp {
|
||||
|
@ -656,7 +656,8 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
|
||||
|
||||
it('should inject TemplateRef', () => {
|
||||
TestBed.configureTestingModule({declarations: [NeedsViewContainerRef, NeedsTemplateRef]});
|
||||
const el = createComponent('<template needsViewContainerRef needsTemplateRef></template>');
|
||||
const el =
|
||||
createComponent('<ng-template needsViewContainerRef needsTemplateRef></ng-template>');
|
||||
expect(el.childNodes[0].injector.get(NeedsTemplateRef).templateRef.elementRef)
|
||||
.toEqual(el.childNodes[0].injector.get(NeedsViewContainerRef).viewContainer.element);
|
||||
});
|
||||
|
Reference in New Issue
Block a user