fix(ivy): convert context code into a tree-shakable instruction (#24943)

PR Close #24943
This commit is contained in:
Kara Erickson
2018-07-25 17:25:22 -07:00
committed by Igor Minar
parent fe14f180a6
commit 2ef777b0b2
24 changed files with 1058 additions and 519 deletions

View File

@ -32,6 +32,7 @@ export {
NgModuleFactory as ɵNgModuleFactory,
NC as ɵNC,
C as ɵC,
x as ɵx,
E as ɵE,
NH as ɵNH,
NM as ɵNM,

View File

@ -22,7 +22,7 @@ import {baseDirectiveCreate, createLNode, createLViewData, createTView, elementC
import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition';
import {LElementNode, TNode, TNodeType} from './interfaces/node';
import {RElement, domRendererFactory3} from './interfaces/renderer';
import {FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {ViewRef} from './view_ref';
export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver {
@ -120,6 +120,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
rootContext.components.push(
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef) as T);
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
(elementNode.data as LViewData)[CONTEXT] = component;
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
// executed here?

View File

@ -738,8 +738,8 @@ class TemplateRef<T> implements viewEngine.TemplateRef<T> {
readonly elementRef: viewEngine.ElementRef;
constructor(
private _declarationParentView: LViewData, elementRef: viewEngine.ElementRef, private _tView: TView, private _renderer: Renderer3,
private _queries: LQueries|null) {
private _declarationParentView: LViewData, elementRef: viewEngine.ElementRef,
private _tView: TView, private _renderer: Renderer3, private _queries: LQueries|null) {
this.elementRef = elementRef;
}

View File

@ -49,6 +49,8 @@ export {
containerRefreshStart as cR,
containerRefreshEnd as cr,
nextContext as x,
element as Ee,
elementAttribute as a,
elementClassProp as cp,

View File

@ -16,7 +16,7 @@ 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, EmbeddedTemplate, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, 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';
@ -165,6 +165,14 @@ export function getCreationMode(): boolean {
*/
let viewData: LViewData;
/**
* The last viewData retrieved by nextContext().
* Allows building nextContext() and reference() calls.
*
* e.g. const inner = x().$implicit; const outer = x().$implicit;
*/
let contextViewData: LViewData = null !;
/**
* An array of directive instances in the current view.
*
@ -223,7 +231,7 @@ export function enterView(newView: LViewData, host: LElementNode | LViewNode | n
isParent = true;
}
viewData = newView;
viewData = contextViewData = newView;
currentQueries = newView && newView[QUERIES];
return oldView;
@ -547,7 +555,7 @@ export function renderEmbeddedTemplate<T>(
oldView = enterView(viewNode.data !, viewNode);
namespaceHTML();
callTemplateWithContexts(rf, context, tView.template !, viewNode.data ![DECLARATION_VIEW] !);
tView.template !(rf, context);
if (rf & RenderFlags.Update) {
refreshDescendantViews();
} else {
@ -566,113 +574,18 @@ export function renderEmbeddedTemplate<T>(
}
/**
* This function calls the template function of a dynamically created view with
* all of its declaration parent contexts (up the view tree) until it reaches the
* component boundary.
* Retrieves a context at the level specified and saves it as the global, contextViewData.
* Will get the next level up if level is not specified.
*
* Example:
* This is used to save contexts of parent views so they can be bound in embedded views, or
* in conjunction with reference() to bind a ref from a parent view.
*
* AppComponent template:
* <ul *ngFor="let list of lists">
* <li *ngFor="let item of list"> {{ item }} </li>
* </ul>
*
* 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
* parent (the ul), and the ul's parent (AppComponent).
*
* Note that a declaration parent is NOT always the same as the insertion parent. Templates
* can be declared in different views than they are used.
*
* @param rf The RenderFlags for this template invocation
* @param currentContext The context for this template
* @param template The template function to call
* @param parentView The declaration view of the dynamic view
* @param level The relative level of the view from which to grab context compared to contextVewData
* @returns context
*/
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 (parentView2 === null) {
return template(rf, currentContext, parentContext);
}
const parentContext2 = parentView2[CONTEXT];
const parentView3 = parentView2[DECLARATION_VIEW];
if (parentView3 === null) {
return template(rf, currentContext, parentContext, parentContext2);
}
const parentContext3 = parentView3[CONTEXT];
const parentView4 = parentView3[DECLARATION_VIEW];
if (parentView4 === null) {
return template(rf, currentContext, parentContext, parentContext2, parentContext3);
}
const parentContext4 = parentView4[CONTEXT];
const parentView5 = parentView4[DECLARATION_VIEW];
if (parentView5 === null) {
return template(
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4);
}
const parentContext5 = parentView5[CONTEXT];
const parentView6 = parentView5[DECLARATION_VIEW];
if (parentView6 === null) {
return template(
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
parentContext5);
}
const parentContext6 = parentView6[CONTEXT];
const parentView7 = parentView6[DECLARATION_VIEW];
if (parentView7 === null) {
return template(
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 templateArgs = [
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
parentContext5, parentContext6, parentContext7, parentContext8, parentView9[CONTEXT]
];
let currentDeclarationView: LViewData|null = parentView9[DECLARATION_VIEW];
while (currentDeclarationView) {
templateArgs.push(currentDeclarationView[CONTEXT]);
currentDeclarationView = currentDeclarationView[DECLARATION_VIEW] !;
}
template.apply(null, templateArgs);
export function nextContext(level: number = 1): any {
contextViewData = walkUpViews(level, contextViewData !);
return contextViewData[CONTEXT];
}
export function renderComponentOrTemplate<T>(
@ -1007,7 +920,7 @@ function getOrCreateTView(
* @param pipes Registry of pipes for this view
*/
export function createTView(
viewIndex: number, template: ComponentTemplate<any>| EmbeddedTemplate<any>| null,
viewIndex: number, template: ComponentTemplate<any>| null,
directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null,
viewQuery: ComponentQuery<any>| null): TView {
ngDevMode && ngDevMode.tView++;
@ -1828,7 +1741,7 @@ export function createLContainer(
* @param localRefs A set of local reference bindings on the element.
*/
export function container(
index: number, template?: EmbeddedTemplate<any>, tagName?: string | null, attrs?: TAttributes,
index: number, template?: ComponentTemplate<any>, tagName?: string | null, attrs?: TAttributes,
localRefs?: string[] | null): void {
ngDevMode &&
assertEqual(
@ -2653,9 +2566,19 @@ 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;
/**
* Retrieves a local reference from the current contextViewData.
*
* If the reference to retrieve is in a parent view, this instruction is used in conjunction
* with a nextContext() call, which walks up the tree and updates the contextViewData instance.
*
* @param index The index of the local ref in contextViewData.
*/
export function reference<T>(index: number) {
return loadInternal<T>(index, contextViewData);
}
function walkUpViews(nestingLevel: number, currentView: LViewData): LViewData {
while (nestingLevel > 0) {
ngDevMode && assertDefined(
currentView[DECLARATION_VIEW],
@ -2663,8 +2586,7 @@ export function reference<T>(nestingLevel: number, index: number) {
currentView = currentView[DECLARATION_VIEW] !;
nestingLevel--;
}
return loadInternal<T>(index, currentView);
return currentView;
}
/** Retrieves a value from the `directives` array. */

View File

@ -19,13 +19,6 @@ export type ComponentTemplate<T> = {
(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;
};
/**
* Definition of what a query function should look like.
*/

View File

@ -36,6 +36,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵa': r3.a,
'ɵb': r3.b,
'ɵC': r3.C,
'ɵx': r3.x,
'ɵcR': r3.cR,
'ɵcr': r3.cr,
'ɵd': r3.d,

View File

@ -12,7 +12,6 @@ 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';
@ -244,30 +243,3 @@ 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