fix(ivy): local directives and pipes should be applied to TemplateRef (#23312)
PR Close #23312
This commit is contained in:
parent
d5e7f60f04
commit
e7ef02722d
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
Loading…
Reference in New Issue