feat(NgTemplateOutlet): Make NgTemplateOutlet compatible with * syntax

BREAKING CHANGE:

- Deprecate `ngOutletContext`. Use `ngTemplateOutletContext` instead
This commit is contained in:
Misko Hevery 2016-12-11 21:42:20 -08:00 committed by Matias Niemelä
parent 31322e73b7
commit c0178de0e2
5 changed files with 111 additions and 22 deletions

View File

@ -15,42 +15,47 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef
* *
* @howToUse * @howToUse
* ``` * ```
* <template [ngTemplateOutlet]="templateRefExpression" * <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
* [ngOutletContext]="objectExpression">
* </template>
* ``` * ```
* *
* @description * @description
* *
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`. * You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`.
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables * `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding
* available within the `TemplateRef`. * by the local template `let` declarations.
* *
* Note: using the key `$implicit` in the context object will set it's value as default. * Note: using the key `$implicit` in the context object will set it's value as default.
* *
* # Example
*
* {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}
*
* @experimental * @experimental
*/ */
@Directive({selector: '[ngTemplateOutlet]'}) @Directive({selector: '[ngTemplateOutlet]'})
export class NgTemplateOutlet implements OnChanges { export class NgTemplateOutlet implements OnChanges {
private _viewRef: EmbeddedViewRef<any>; private _viewRef: EmbeddedViewRef<any>;
private _context: Object;
private _templateRef: TemplateRef<any>; @Input() public ngTemplateOutletContext: Object;
@Input() public ngTemplateOutlet: TemplateRef<any>;
constructor(private _viewContainerRef: ViewContainerRef) {} constructor(private _viewContainerRef: ViewContainerRef) {}
/**
* @deprecated v4.0.0 - Renamed to ngTemplateOutletContext.
*/
@Input() @Input()
set ngOutletContext(context: Object) { this._context = context; } set ngOutletContext(context: Object) { this.ngTemplateOutletContext = context; }
@Input()
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (this._viewRef) { if (this._viewRef) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef)); this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
} }
if (this._templateRef) { if (this.ngTemplateOutlet) {
this._viewRef = this._viewContainerRef.createEmbeddedView(this._templateRef, this._context); this._viewRef = this._viewContainerRef.createEmbeddedView(
this.ngTemplateOutlet, this.ngTemplateOutletContext);
} }
} }
} }

View File

@ -84,8 +84,8 @@ export function main() {
})); }));
it('should display template if context is null', async(() => { it('should display template if context is null', async(() => {
const template = const template = `<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs>` +
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`; `<ng-content *ngTemplateOutlet="currentTplRef; context: null"></ng-content>`;
fixture = createTestComponent(template); fixture = createTestComponent(template);
detectChangesAndExpectText(''); detectChangesAndExpectText('');
@ -97,7 +97,8 @@ export function main() {
it('should reflect initial context and changes', async(() => { it('should reflect initial context and changes', async(() => {
const template = const template =
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`; `<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs>` +
`<ng-content *ngTemplateOutlet="currentTplRef; context: context"></ng-content>`;
fixture = createTestComponent(template); fixture = createTestComponent(template);
fixture.detectChanges(); fixture.detectChanges();
@ -114,7 +115,8 @@ export function main() {
it('should reflect user defined $implicit property in the context', async(() => { it('should reflect user defined $implicit property in the context', async(() => {
const template = const template =
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`; `<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs>` +
`<ng-content *ngTemplateOutlet="currentTplRef; context: context"></ng-content>`;
fixture = createTestComponent(template); fixture = createTestComponent(template);
fixture.detectChanges(); fixture.detectChanges();
@ -128,7 +130,8 @@ export function main() {
it('should reflect context re-binding', async(() => { it('should reflect context re-binding', async(() => {
const template = const template =
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`; `<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs>` +
`<ng-content *ngTemplateOutlet="currentTplRef; context: context"></ng-content>`;
fixture = createTestComponent(template); fixture = createTestComponent(template);
fixture.detectChanges(); fixture.detectChanges();
@ -162,4 +165,4 @@ function createTestComponent(template: string): ComponentFixture<TestComponent>
return TestBed.overrideComponent(TestComponent, {set: {template: template}}) return TestBed.overrideComponent(TestComponent, {set: {template: template}})
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]}) .configureTestingModule({schemas: [NO_ERRORS_SCHEMA]})
.createComponent(TestComponent); .createComponent(TestComponent);
} }

View File

@ -0,0 +1,31 @@
/**
* @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 {$, ExpectedConditions, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
function waitForElement(selector: string) {
const EC = ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);
}
describe('ngTemplateOutlet', () => {
const URL = 'common/ngTemplateOutlet/ts/';
afterEach(verifyNoBrowserErrors);
describe('ng-template-outlet-example', () => {
it('should render', () => {
browser.get(URL);
waitForElement('ng-template-outlet-example');
expect(element.all(by.css('ng-template-outlet-example span')).getText()).toEqual([
'Hello', 'Hello World!', 'Ahoj Svet!'
]);
});
});
});

View File

@ -0,0 +1,49 @@
/**
* @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 {Component, NgModule, OnInit, TemplateRef, ViewChild} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {Subject} from 'rxjs/Subject';
// #docregion NgTemplateOutlet
@Component({
selector: 'ng-template-outlet-example',
template: `
<ng-container *ngTemplateOutlet="greet"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="eng; context: myContext"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="svk; context: myContext"></ng-container>
<hr>
<template #greet><span>Hello</span></template>
<template #eng let-name><span>Hello {{name}}!</span></template>
<template #svk let-person="localSk"><span>Ahoj {{person}}!</span></template>
`
})
class NgTemplateOutletExample {
myContext = {$implicit: 'World', localSk: 'Svet'};
}
// #enddocregion
@Component({
selector: 'example-app',
template: `<ng-template-outlet-example></ng-template-outlet-example>`
})
class ExampleApp {
}
@NgModule({
imports: [BrowserModule],
declarations: [ExampleApp, NgTemplateOutletExample],
bootstrap: [ExampleApp]
})
export class AppModule {
}

View File

@ -188,8 +188,9 @@ export declare class NgSwitchDefault {
/** @experimental */ /** @experimental */
export declare class NgTemplateOutlet implements OnChanges { export declare class NgTemplateOutlet implements OnChanges {
ngOutletContext: Object; /** @deprecated */ ngOutletContext: Object;
ngTemplateOutlet: TemplateRef<Object>; ngTemplateOutlet: TemplateRef<any>;
ngTemplateOutletContext: Object;
constructor(_viewContainerRef: ViewContainerRef); constructor(_viewContainerRef: ViewContainerRef);
ngOnChanges(changes: SimpleChanges): void; ngOnChanges(changes: SimpleChanges): void;
} }