fix(ivy): local directives and pipes should be applied to TemplateRef (#23312)

PR Close #23312
This commit is contained in:
Marc Laval 2018-04-11 14:15:30 +02:00 committed by Igor Minar
parent d5e7f60f04
commit e7ef02722d
3 changed files with 99 additions and 14 deletions

View File

@ -20,7 +20,7 @@ import {Type} from '../type';
import {assertGreaterThan, assertLessThan, assertNotNull} from './assert';
import {addToViewTree, assertPreviousIsParent, createLContainer, createLNodeObject, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
import {ComponentTemplate, DirectiveDef} from './interfaces/definition';
import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList} from './interfaces/definition';
import {LInjector} from './interfaces/injector';
import {LContainerNode, LElementNode, LNode, LNodeType, LViewNode, TNodeFlags} from './interfaces/node';
import {QueryReadType} from './interfaces/query';
@ -701,8 +701,10 @@ class ViewContainerRef implements viewEngine_ViewContainerRef {
export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef<T> {
ngDevMode && assertNodeType(di.node, LNodeType.Container);
const data = (di.node as LContainerNode).data;
const tView = di.node.view.tView;
return di.templateRef || (di.templateRef = new TemplateRef<any>(
getOrCreateElementRef(di), data.template !, getRenderer()));
getOrCreateElementRef(di), data.template !, getRenderer(),
tView.directiveRegistry, tView.pipeRegistry));
}
class TemplateRef<T> implements viewEngine_TemplateRef<T> {
@ -711,13 +713,15 @@ class TemplateRef<T> implements viewEngine_TemplateRef<T> {
constructor(
elementRef: viewEngine_ElementRef, template: ComponentTemplate<T>,
private _renderer: Renderer3) {
private _renderer: Renderer3, private _directives: DirectiveDefList|null,
private _pipes: PipeDefList|null) {
this.elementRef = elementRef;
this._template = template;
}
createEmbeddedView(context: T): viewEngine_EmbeddedViewRef<T> {
const viewNode = renderEmbeddedTemplate(null, this._template, context, this._renderer);
const viewNode = renderEmbeddedTemplate(
null, this._template, context, this._renderer, this._directives, this._pipes);
return addDestroyable(new EmbeddedViewRef(viewNode, this._template, context));
}
}

View File

@ -19,7 +19,7 @@ import {LContainerNode, LElementNode, LNode, LNodeType, TNodeFlags, LProjectionN
import {assertNodeType} from './node_assert';
import {appendChild, insertChild, insertView, appendProjectedNode, removeView, canInsertNativeNode, createTextNode} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, DirectiveType, PipeDef, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, DirectiveType, PipeDef, PipeDefList, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
import {isDifferent, stringify} from './util';
import {executeHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks';
@ -450,8 +450,8 @@ export function renderTemplate<T>(
}
export function renderEmbeddedTemplate<T>(
viewNode: LViewNode | null, template: ComponentTemplate<T>, context: T,
renderer: Renderer3): LViewNode {
viewNode: LViewNode | null, template: ComponentTemplate<T>, context: T, renderer: Renderer3,
directives?: DirectiveDefList | null, pipes?: PipeDefList | null): LViewNode {
const _isParent = isParent;
const _previousOrParentNode = previousOrParentNode;
let oldView: LView;
@ -460,11 +460,7 @@ export function renderEmbeddedTemplate<T>(
previousOrParentNode = null !;
let rf: RenderFlags = RenderFlags.Update;
if (viewNode == null) {
// TODO: revisit setting currentView when re-writing view containers
const directives = currentView && currentView.tView.directiveRegistry;
const pipes = currentView && currentView.tView.pipeRegistry;
const tView = getOrCreateTView(template, directives, pipes);
const tView = getOrCreateTView(template, directives || null, pipes || null);
const lView = createLView(-1, renderer, tView, template, context, LViewFlags.CheckAlways);
viewNode = createLNode(null, LNodeType.View, null, lView);
@ -1474,6 +1470,7 @@ function refreshDynamicChildren() {
const container = current as LContainer;
for (let i = 0; i < container.views.length; i++) {
const view = container.views[i];
// The directives and pipes are not needed here as an existing view is only being refreshed.
renderEmbeddedTemplate(view, view.data.template !, view.data.context !, renderer);
}
}

View File

@ -6,11 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Directive, TemplateRef, ViewContainerRef} from '../../src/core';
import {Component, Directive, Pipe, PipeTransform, TemplateRef, ViewContainerRef} from '../../src/core';
import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
import {defineComponent, defineDirective, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
import {defineComponent, defineDirective, definePipe, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, load, loadDirective, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {pipe, pipeBind1} from '../../src/render3/pipe';
import {ComponentFixture, TemplateFixture} from './render_util';
@ -334,6 +335,89 @@ describe('ViewContainerRef', () => {
fixture.update();
expect(fixture.html).toEqual('before|AAB|after');
});
it('should apply directives and pipes of the host view to the TemplateRef', () => {
@Component({selector: 'child', template: `{{name}}`})
class Child {
name: string;
static ngComponentDef = defineComponent({
type: Child,
selectors: [['child']],
factory: () => new Child(),
template: (rf: RenderFlags, cmp: Child) => {
if (rf & RenderFlags.Create) {
text(0);
}
if (rf & RenderFlags.Update) {
textBinding(0, interpolation1('', cmp.name, ''));
}
},
inputs: {name: 'name'}
});
}
@Pipe({name: 'starPipe'})
class StarPipe implements PipeTransform {
transform(value: any) { return `**${value}**`; }
static ngPipeDef = definePipe({
name: 'starPipe',
type: StarPipe,
factory: function StarPipe_Factory() { return new StarPipe(); },
});
}
@Component({
template: `
<ng-template #foo>
<child [name]="'C' | starPipe"></child>
</ng-template>
<child vcref [tplRef]="foo" [name]="'A' | starPipe"></child>
<child [name]="'B' | starPipe"></child>
`
})
class SomeComponent {
static ngComponentDef = defineComponent({
type: SomeComponent,
selectors: [['some-comp']],
factory: () => new SomeComponent(),
template: (rf: RenderFlags, cmp: SomeComponent) => {
if (rf & RenderFlags.Create) {
container(0, (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
elementStart(0, 'child');
elementEnd();
pipe(1, 'starPipe');
}
if (rf & RenderFlags.Create) {
elementProperty(0, 'name', bind(pipeBind1(1, 'C')));
}
});
pipe(1, 'starPipe');
elementStart(2, 'child', ['vcref', '']);
elementEnd();
elementStart(3, 'child');
elementEnd();
}
if (rf & RenderFlags.Update) {
const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
elementProperty(2, 'tplRef', bind(tplRef));
elementProperty(2, 'name', bind(pipeBind1(1, 'A')));
elementProperty(3, 'name', bind(pipeBind1(1, 'B')));
}
},
directives: [Child, DirectiveWithVCRef],
pipes: [StarPipe]
});
}
const fixture = new ComponentFixture(SomeComponent);
directiveInstance !.vcref.createEmbeddedView(directiveInstance !.tplRef, fixture.component);
fixture.update();
expect(fixture.html)
.toEqual('<child vcref="">**A**</child><child>**C**</child><child>**B**</child>');
});
});
describe('detach', () => {