fix(compiler): update compiler to flatten nested template fns (#24943)

PR Close #24943
This commit is contained in:
Kara Erickson
2018-07-18 01:59:49 +00:00
committed by Igor Minar
parent 87419097da
commit fe14f180a6
19 changed files with 863 additions and 557 deletions

View File

@ -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,

View File

@ -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';

View File

@ -76,6 +76,8 @@ export {
text as T,
textBinding as t,
reference as r,
reserveSlots as rS,
embeddedViewStart as V,

View File

@ -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.');

View File

@ -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;
};
/**

View File

@ -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]) */

View File

@ -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,

View File

@ -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