fix(compiler): update compiler to flatten nested template fns (#24943)
PR Close #24943
This commit is contained in:

committed by
Igor Minar

parent
87419097da
commit
fe14f180a6
@ -76,6 +76,7 @@ export {
|
||||
e as ɵe,
|
||||
p as ɵp,
|
||||
pD as ɵpD,
|
||||
r as ɵr,
|
||||
rS as ɵrS,
|
||||
a as ɵa,
|
||||
s as ɵs,
|
||||
|
@ -24,7 +24,7 @@ import {LInjector} from './interfaces/injector';
|
||||
import {AttributeMarker, LContainerNode, LElementNode, LNode, LViewNode, TContainerNode, TElementNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {LQueries, QueryReadType} from './interfaces/query';
|
||||
import {Renderer3} from './interfaces/renderer';
|
||||
import {DECLARATION_PARENT, DIRECTIVES, HOST_NODE, INJECTOR, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
|
||||
import {DECLARATION_VIEW, DIRECTIVES, HOST_NODE, INJECTOR, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {addRemoveViewFromContainer, appendChild, detachView, getChildLNode, getParentLNode, insertView, removeView} from './node_manipulation';
|
||||
import {ViewRef} from './view_ref';
|
||||
|
@ -76,6 +76,8 @@ export {
|
||||
text as T,
|
||||
textBinding as t,
|
||||
|
||||
reference as r,
|
||||
|
||||
reserveSlots as rS,
|
||||
|
||||
embeddedViewStart as V,
|
||||
|
@ -16,13 +16,13 @@ import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotE
|
||||
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
||||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||
import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, EmbeddedTemplate, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {LInjector} from './interfaces/injector';
|
||||
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||
import {LQueries} from './interfaces/query';
|
||||
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_PARENT, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view';
|
||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation';
|
||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||
@ -334,7 +334,7 @@ export function createLViewData<T>(
|
||||
null, // tail
|
||||
-1, // containerIndex
|
||||
null, // contentQueries
|
||||
null // declarationParent
|
||||
null // declarationView
|
||||
];
|
||||
}
|
||||
|
||||
@ -500,7 +500,7 @@ export function renderTemplate<T>(
|
||||
* Such lViewNode will then be renderer with renderEmbeddedTemplate() (see below).
|
||||
*/
|
||||
export function createEmbeddedViewNode<T>(
|
||||
tView: TView, context: T, declarationParent: LViewData, renderer: Renderer3,
|
||||
tView: TView, context: T, declarationView: LViewData, renderer: Renderer3,
|
||||
queries?: LQueries | null): LViewNode {
|
||||
const _isParent = isParent;
|
||||
const _previousOrParentNode = previousOrParentNode;
|
||||
@ -509,7 +509,7 @@ export function createEmbeddedViewNode<T>(
|
||||
|
||||
const lView =
|
||||
createLViewData(renderer, tView, context, LViewFlags.CheckAlways, getCurrentSanitizer());
|
||||
lView[DECLARATION_PARENT] = declarationParent;
|
||||
lView[DECLARATION_VIEW] = declarationView;
|
||||
|
||||
if (queries) {
|
||||
lView[QUERIES] = queries.createView();
|
||||
@ -547,7 +547,7 @@ export function renderEmbeddedTemplate<T>(
|
||||
|
||||
oldView = enterView(viewNode.data !, viewNode);
|
||||
namespaceHTML();
|
||||
callTemplateWithContexts(rf, context, tView.template !, viewNode.data ![DECLARATION_PARENT] !);
|
||||
callTemplateWithContexts(rf, context, tView.template !, viewNode.data ![DECLARATION_VIEW] !);
|
||||
if (rf & RenderFlags.Update) {
|
||||
refreshDescendantViews();
|
||||
} else {
|
||||
@ -577,11 +577,14 @@ export function renderEmbeddedTemplate<T>(
|
||||
* <li *ngFor="let item of list"> {{ item }} </li>
|
||||
* </ul>
|
||||
*
|
||||
* function AppComponentTemplate(rf, ctx) {
|
||||
* // instructions
|
||||
* function ulTemplate(rf, ulCtx, appCtx) {...}
|
||||
* function liTemplate(rf, liCtx, ulCtx, appCtx) {...}
|
||||
* }
|
||||
* function ulTemplate(rf, ulCtx, appCtx) {...}
|
||||
* function liTemplate(rf, liCtx, ulCtx, appCtx) {...}
|
||||
*
|
||||
* class AppComponent {...}
|
||||
* AppComponent.ngComponentDef = defineComponent({
|
||||
* template: function AppComponentTemplate(rf, ctx) {...}
|
||||
* });
|
||||
*
|
||||
*
|
||||
* The ul view's template must be called with its own context and its declaration
|
||||
* parent, AppComponent. The li view's template must be called with its own context, its
|
||||
@ -591,71 +594,85 @@ export function renderEmbeddedTemplate<T>(
|
||||
* can be declared in different views than they are used.
|
||||
*
|
||||
* @param rf The RenderFlags for this template invocation
|
||||
* @param context The context for this template
|
||||
* @param currentContext The context for this template
|
||||
* @param template The template function to call
|
||||
* @param parent1 The declaration parent of the dynamic view
|
||||
* @param parentView The declaration view of the dynamic view
|
||||
*/
|
||||
function callTemplateWithContexts(
|
||||
rf: RenderFlags, context: any, template: ComponentTemplate<any>, parent1: LViewData): void {
|
||||
const parent2 = parent1[DECLARATION_PARENT];
|
||||
function callTemplateWithContexts<T>(
|
||||
rf: RenderFlags, currentContext: T, template: EmbeddedTemplate<T>,
|
||||
parentView: LViewData): void {
|
||||
const parentContext = parentView[CONTEXT];
|
||||
const parentView2 = parentView[DECLARATION_VIEW];
|
||||
|
||||
// Calling a function with extra arguments has a VM cost, so only call with necessary args
|
||||
if (!parent2) return template(rf, context, parent1[CONTEXT]);
|
||||
|
||||
const parent3 = parent2[DECLARATION_PARENT];
|
||||
if (!parent3) return template(rf, context, parent1[CONTEXT], parent2[CONTEXT]);
|
||||
|
||||
const parent4 = parent3[DECLARATION_PARENT];
|
||||
if (!parent4) {
|
||||
return template(rf, context, parent1[CONTEXT], parent2[CONTEXT], parent3[CONTEXT]);
|
||||
if (parentView2 === null) {
|
||||
return template(rf, currentContext, parentContext);
|
||||
}
|
||||
|
||||
const parent5 = parent4[DECLARATION_PARENT];
|
||||
if (!parent5) {
|
||||
return template(
|
||||
rf, context, parent1[CONTEXT], parent2[CONTEXT], parent3[CONTEXT], parent4[CONTEXT]);
|
||||
const parentContext2 = parentView2[CONTEXT];
|
||||
const parentView3 = parentView2[DECLARATION_VIEW];
|
||||
if (parentView3 === null) {
|
||||
return template(rf, currentContext, parentContext, parentContext2);
|
||||
}
|
||||
|
||||
const parent6 = parent5[DECLARATION_PARENT];
|
||||
if (!parent6) {
|
||||
return template(
|
||||
rf, context, parent1[CONTEXT], parent2[CONTEXT], parent3[CONTEXT], parent4[CONTEXT],
|
||||
parent5[CONTEXT]);
|
||||
const parentContext3 = parentView3[CONTEXT];
|
||||
const parentView4 = parentView3[DECLARATION_VIEW];
|
||||
if (parentView4 === null) {
|
||||
return template(rf, currentContext, parentContext, parentContext2, parentContext3);
|
||||
}
|
||||
|
||||
const parent7 = parent6[DECLARATION_PARENT];
|
||||
if (!parent7) {
|
||||
const parentContext4 = parentView4[CONTEXT];
|
||||
const parentView5 = parentView4[DECLARATION_VIEW];
|
||||
if (parentView5 === null) {
|
||||
return template(
|
||||
rf, context, parent1[CONTEXT], parent2[CONTEXT], parent3[CONTEXT], parent4[CONTEXT],
|
||||
parent5[CONTEXT], parent6[CONTEXT]);
|
||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4);
|
||||
}
|
||||
|
||||
const parent8 = parent7[DECLARATION_PARENT];
|
||||
if (!parent8) {
|
||||
const parentContext5 = parentView5[CONTEXT];
|
||||
const parentView6 = parentView5[DECLARATION_VIEW];
|
||||
if (parentView6 === null) {
|
||||
return template(
|
||||
rf, context, parent1[CONTEXT], parent2[CONTEXT], parent3[CONTEXT], parent4[CONTEXT],
|
||||
parent5[CONTEXT], parent6[CONTEXT], parent7[CONTEXT]);
|
||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
||||
parentContext5);
|
||||
}
|
||||
|
||||
const parent9 = parent8[DECLARATION_PARENT];
|
||||
if (!parent9) {
|
||||
const parentContext6 = parentView6[CONTEXT];
|
||||
const parentView7 = parentView6[DECLARATION_VIEW];
|
||||
if (parentView7 === null) {
|
||||
return template(
|
||||
rf, context, parent1[CONTEXT], parent2[CONTEXT], parent3[CONTEXT], parent4[CONTEXT],
|
||||
parent5[CONTEXT], parent6[CONTEXT], parent7[CONTEXT], parent8[CONTEXT]);
|
||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
||||
parentContext5, parentContext6);
|
||||
}
|
||||
|
||||
const parentContext7 = parentView7[CONTEXT];
|
||||
const parentView8 = parentView7[DECLARATION_VIEW];
|
||||
if (parentView8 === null) {
|
||||
return template(
|
||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
||||
parentContext5, parentContext6, parentContext7);
|
||||
}
|
||||
|
||||
const parentContext8 = parentView8[CONTEXT];
|
||||
const parentView9 = parentView8[DECLARATION_VIEW];
|
||||
if (parentView9 === null) {
|
||||
return template(
|
||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
||||
parentContext5, parentContext6, parentContext7, parentContext8);
|
||||
}
|
||||
|
||||
// We support up to 8 nesting levels in embedded views before we give up and call apply()
|
||||
const contexts = [
|
||||
parent1[CONTEXT], parent2[CONTEXT], parent3[CONTEXT], parent4[CONTEXT], parent5[CONTEXT],
|
||||
parent6[CONTEXT], parent7[CONTEXT], parent8[CONTEXT], parent9[CONTEXT]
|
||||
const templateArgs = [
|
||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
||||
parentContext5, parentContext6, parentContext7, parentContext8, parentView9[CONTEXT]
|
||||
];
|
||||
|
||||
let currentView: LViewData = parent9;
|
||||
while (currentView[DECLARATION_PARENT]) {
|
||||
contexts.push(currentView[DECLARATION_PARENT] ![CONTEXT]);
|
||||
currentView = currentView[DECLARATION_PARENT] !;
|
||||
let currentDeclarationView: LViewData|null = parentView9[DECLARATION_VIEW];
|
||||
while (currentDeclarationView) {
|
||||
templateArgs.push(currentDeclarationView[CONTEXT]);
|
||||
currentDeclarationView = currentDeclarationView[DECLARATION_VIEW] !;
|
||||
}
|
||||
|
||||
tView.template !(rf, context, ...contexts);
|
||||
template.apply(null, templateArgs);
|
||||
}
|
||||
|
||||
export function renderComponentOrTemplate<T>(
|
||||
@ -990,7 +1007,7 @@ function getOrCreateTView(
|
||||
* @param pipes Registry of pipes for this view
|
||||
*/
|
||||
export function createTView(
|
||||
viewIndex: number, template: ComponentTemplate<any>| null,
|
||||
viewIndex: number, template: ComponentTemplate<any>| EmbeddedTemplate<any>| null,
|
||||
directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null,
|
||||
viewQuery: ComponentQuery<any>| null): TView {
|
||||
ngDevMode && ngDevMode.tView++;
|
||||
@ -1811,7 +1828,7 @@ export function createLContainer(
|
||||
* @param localRefs A set of local reference bindings on the element.
|
||||
*/
|
||||
export function container(
|
||||
index: number, template?: ComponentTemplate<any>, tagName?: string | null, attrs?: TAttributes,
|
||||
index: number, template?: EmbeddedTemplate<any>, tagName?: string | null, attrs?: TAttributes,
|
||||
localRefs?: string[] | null): void {
|
||||
ngDevMode &&
|
||||
assertEqual(
|
||||
@ -2636,6 +2653,20 @@ export function store<T>(index: number, value: T): void {
|
||||
viewData[adjustedIndex] = value;
|
||||
}
|
||||
|
||||
/** Retrieves a value from an LViewData at the given nesting level. */
|
||||
export function reference<T>(nestingLevel: number, index: number) {
|
||||
let currentView = viewData;
|
||||
while (nestingLevel > 0) {
|
||||
ngDevMode && assertDefined(
|
||||
currentView[DECLARATION_VIEW],
|
||||
'Declaration view should be defined if nesting level is greater than 0.');
|
||||
currentView = currentView[DECLARATION_VIEW] !;
|
||||
nestingLevel--;
|
||||
}
|
||||
|
||||
return loadInternal<T>(index, currentView);
|
||||
}
|
||||
|
||||
/** Retrieves a value from the `directives` array. */
|
||||
export function loadDirective<T>(index: number): T {
|
||||
ngDevMode && assertDefined(directives, 'Directives array should be defined if reading a dir.');
|
||||
|
@ -11,11 +11,19 @@ import {RendererType2} from '../../render/api';
|
||||
import {Type} from '../../type';
|
||||
import {CssSelectorList} from './projection';
|
||||
|
||||
|
||||
/**
|
||||
* Definition of what a template rendering function should look like.
|
||||
* Definition of what a template rendering function should look like for a component.
|
||||
*/
|
||||
export type ComponentTemplate<T> = {
|
||||
(rf: RenderFlags, ctx: T, ...parentCtx: ({} | null)[]): void; ngPrivateData?: never;
|
||||
(rf: RenderFlags, ctx: T): void; ngPrivateData?: never;
|
||||
};
|
||||
|
||||
/**
|
||||
* Definition of what a template rendering function should look like for an embedded view.
|
||||
*/
|
||||
export type EmbeddedTemplate<T> = {
|
||||
(rf: RenderFlags, ctx: T, ...parentCtx: any[]): void;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ export const SANITIZER = 12;
|
||||
export const TAIL = 13;
|
||||
export const CONTAINER_INDEX = 14;
|
||||
export const CONTENT_QUERIES = 15;
|
||||
export const DECLARATION_PARENT = 16;
|
||||
export const DECLARATION_VIEW = 16;
|
||||
|
||||
/**
|
||||
* `LViewData` stores all of the information needed to process the instructions as
|
||||
@ -63,7 +63,7 @@ export interface LViewData extends Array<any> {
|
||||
* `LViewData`. Without this, the render method would have to keep a stack of
|
||||
* views as it is recursively rendering templates.
|
||||
*
|
||||
* This is also the "insertion" parent for embedded views. This allows us to properly
|
||||
* This is the "insertion" view for embedded views. This allows us to properly
|
||||
* destroy embedded views.
|
||||
*/
|
||||
[PARENT]: LViewData|null;
|
||||
@ -167,16 +167,16 @@ export interface LViewData extends Array<any> {
|
||||
[CONTENT_QUERIES]: QueryList<any>[]|null;
|
||||
|
||||
/**
|
||||
* Parent view where this view's template was declared.
|
||||
* View where this view's template was declared.
|
||||
*
|
||||
* Only applicable for dynamically created views. Will be null for inline/component views.
|
||||
*
|
||||
* The template for a dynamically created view may be declared in a different view than
|
||||
* it is inserted. We already track the "insertion parent" (view where the template was
|
||||
* inserted) in LViewData[PARENT], but we also need access to the "declaration parent"
|
||||
* it is inserted. We already track the "insertion view" (view where the template was
|
||||
* inserted) in LViewData[PARENT], but we also need access to the "declaration view"
|
||||
* (view where the template was declared). Otherwise, we wouldn't be able to call the
|
||||
* view's template function with the proper contexts. Context should be inherited from
|
||||
* the declaration parent tree, not the insertion parent tree.
|
||||
* the declaration view tree, not the insertion view tree.
|
||||
*
|
||||
* Example (AppComponent template):
|
||||
*
|
||||
@ -184,13 +184,13 @@ export interface LViewData extends Array<any> {
|
||||
* <some-comp [tpl]="foo"></some-comp> <-- inserted inside this component -->
|
||||
*
|
||||
* The <ng-template> above is declared in the AppComponent template, but it will be passed into
|
||||
* SomeComp and inserted there. In this case, the declaration parent would be the AppComponent,
|
||||
* but the insertion parent would be SomeComp. When we are removing views, we would want to
|
||||
* traverse through the insertion parent to clean up listeners. When we are calling the
|
||||
* template function during change detection, we need the declaration parent to get inherited
|
||||
* SomeComp and inserted there. In this case, the declaration view would be the AppComponent,
|
||||
* but the insertion view would be SomeComp. When we are removing views, we would want to
|
||||
* traverse through the insertion view to clean up listeners. When we are calling the
|
||||
* template function during change detection, we need the declaration view to get inherited
|
||||
* context.
|
||||
*/
|
||||
[DECLARATION_PARENT]: LViewData|null;
|
||||
[DECLARATION_VIEW]: LViewData|null;
|
||||
}
|
||||
|
||||
/** Flags associated with an LView (saved in LViewData[FLAGS]) */
|
||||
|
@ -81,6 +81,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
||||
'ɵqR': r3.qR,
|
||||
'ɵQr': r3.Qr,
|
||||
'ɵrS': r3.rS,
|
||||
'ɵr': r3.r,
|
||||
'ɵs': r3.s,
|
||||
'ɵsm': r3.sm,
|
||||
'ɵsp': r3.sp,
|
||||
|
@ -12,6 +12,7 @@ import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_co
|
||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
||||
|
||||
import {checkNoChanges, detectChanges, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
||||
import {EmbeddedTemplate} from './interfaces/definition';
|
||||
import {LViewNode} from './interfaces/node';
|
||||
import {FLAGS, LViewData, LViewFlags} from './interfaces/view';
|
||||
import {destroyLView} from './node_manipulation';
|
||||
@ -243,3 +244,30 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
||||
|
||||
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
|
||||
export class EmbeddedViewRef<T> extends ViewRef<T> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_lViewNode: LViewNode;
|
||||
private _viewContainerRef: viewEngine_ViewContainerRef|null = null;
|
||||
|
||||
constructor(viewNode: LViewNode, template: EmbeddedTemplate<T>, context: T) {
|
||||
super(viewNode.data, context);
|
||||
this._lViewNode = viewNode;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
if (this._viewContainerRef && viewAttached(this._view)) {
|
||||
this._viewContainerRef.detach(this._viewContainerRef.indexOf(this));
|
||||
this._viewContainerRef = null;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
attachToViewContainerRef(vcRef: viewEngine_ViewContainerRef) { this._viewContainerRef = vcRef; }
|
||||
}
|
||||
>>>>>>> fixup! fix(ivy): flatten template fns for nested views
|
||||
|
Reference in New Issue
Block a user