perf(ivy): split view processing into render (create) and refresh (update) pass (#32020)
PR Close #32020
This commit is contained in:
parent
4d96cf5197
commit
b9dfe66028
|
@ -17,17 +17,17 @@ import {assertComponentType} from './assert';
|
|||
import {getComponentDef} from './definition';
|
||||
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
||||
import {registerPostOrderHooks, registerPreOrderHooks} from './hooks';
|
||||
import {CLEAN_PROMISE, addToViewTree, createLView, createTView, getOrCreateTNode, getOrCreateTView, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshDescendantViews} from './instructions/shared';
|
||||
import {CLEAN_PROMISE, addToViewTree, createLView, createTView, getOrCreateTNode, getOrCreateTView, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, renderView} from './instructions/shared';
|
||||
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
|
||||
import {TElementNode, TNode, TNodeType} from './interfaces/node';
|
||||
import {PlayerHandler} from './interfaces/player';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
import {CONTEXT, FLAGS, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setActiveHostElement} from './state';
|
||||
import {publishDefaultGlobalUtils} from './util/global_utils';
|
||||
import {defaultScheduler, stringifyForError} from './util/misc_utils';
|
||||
import {getRootContext} from './util/view_traversal_utils';
|
||||
import {readPatchedLView, resetPreOrderHookFlags} from './util/view_utils';
|
||||
import {readPatchedLView} from './util/view_utils';
|
||||
|
||||
|
||||
|
||||
|
@ -128,15 +128,14 @@ export function renderComponent<T>(
|
|||
const rootContext = createRootContext(opts.scheduler, opts.playerHandler);
|
||||
|
||||
const renderer = rendererFactory.createRenderer(hostRNode, componentDef);
|
||||
const rootTView = createTView(-1, null, 1, 0, null, null, null, null);
|
||||
const rootView: LView = createLView(
|
||||
null, createTView(-1, null, 1, 0, null, null, null, null), rootContext, rootFlags, null, null,
|
||||
rendererFactory, renderer, undefined, opts.injector || null);
|
||||
null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, undefined,
|
||||
opts.injector || null);
|
||||
|
||||
const oldView = enterView(rootView, null);
|
||||
let component: T;
|
||||
|
||||
// Will become true if the `try` block executes with no errors.
|
||||
let safeToRunHooks = false;
|
||||
try {
|
||||
if (rendererFactory.begin) rendererFactory.begin();
|
||||
const componentView = createRootComponentView(
|
||||
|
@ -144,13 +143,13 @@ export function renderComponent<T>(
|
|||
component = createRootComponent(
|
||||
componentView, componentDef, rootView, rootContext, opts.hostFeatures || null);
|
||||
|
||||
refreshDescendantViews(rootView); // creation mode pass
|
||||
rootView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
resetPreOrderHookFlags(rootView);
|
||||
refreshDescendantViews(rootView); // update mode pass
|
||||
safeToRunHooks = true;
|
||||
// create mode pass
|
||||
renderView(rootView, rootTView, null);
|
||||
// update mode pass
|
||||
refreshView(rootView, rootTView, null, null);
|
||||
|
||||
} finally {
|
||||
leaveView(oldView, safeToRunHooks);
|
||||
leaveView(oldView);
|
||||
if (rendererFactory.end) rendererFactory.end();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import {assertComponentType} from './assert';
|
|||
import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component';
|
||||
import {getComponentDef} from './definition';
|
||||
import {NodeInjector} from './di';
|
||||
import {addToViewTree, assignTViewNodeToLView, createLView, createTView, elementCreate, locateHostElement, refreshDescendantViews} from './instructions/shared';
|
||||
import {assignTViewNodeToLView, createLView, createTView, elementCreate, locateHostElement, renderView} from './instructions/shared';
|
||||
import {ComponentDef} from './interfaces/definition';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode} from './interfaces/node';
|
||||
import {RNode, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
||||
|
@ -161,9 +161,10 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
|||
}
|
||||
|
||||
// Create the root view. Uses empty TView and ContentTemplate.
|
||||
const rootTView = createTView(-1, null, 1, 0, null, null, null, null);
|
||||
const rootLView = createLView(
|
||||
null, createTView(-1, null, 1, 0, null, null, null, null), rootContext, rootFlags, null,
|
||||
null, rendererFactory, renderer, sanitizer, rootViewInjector);
|
||||
null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, sanitizer,
|
||||
rootViewInjector);
|
||||
|
||||
// rootView is the parent when bootstrapping
|
||||
const oldLView = enterView(rootLView, null);
|
||||
|
@ -171,8 +172,6 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
|||
let component: T;
|
||||
let tElementNode: TElementNode;
|
||||
|
||||
// Will become true if the `try` block executes with no errors.
|
||||
let safeToRunHooks = false;
|
||||
try {
|
||||
const componentView = createRootComponentView(
|
||||
hostRNode, this.componentDef, rootLView, rendererFactory, renderer);
|
||||
|
@ -193,10 +192,9 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
|||
component = createRootComponent(
|
||||
componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);
|
||||
|
||||
refreshDescendantViews(rootLView);
|
||||
safeToRunHooks = true;
|
||||
renderView(rootLView, rootTView, null);
|
||||
} finally {
|
||||
leaveView(oldLView, safeToRunHooks);
|
||||
leaveView(oldLView);
|
||||
}
|
||||
|
||||
const componentRef = new ComponentRef(
|
||||
|
|
|
@ -705,7 +705,7 @@ function createDynamicNodeAtIndex(
|
|||
|
||||
// We are creating a dynamic node, the previous tNode might not be pointing at this node.
|
||||
// We will link ourselves into the tree later with `appendI18nNode`.
|
||||
if (previousOrParentTNode.next === tNode) {
|
||||
if (previousOrParentTNode && previousOrParentTNode.next === tNode) {
|
||||
previousOrParentTNode.next = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,12 +11,14 @@ import {assertLContainerOrUndefined} from '../assert';
|
|||
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/container';
|
||||
import {RenderFlags} from '../interfaces/definition';
|
||||
import {TContainerNode, TNodeType} from '../interfaces/node';
|
||||
import {FLAGS, LView, LViewFlags, PARENT, TVIEW, TView, T_HOST} from '../interfaces/view';
|
||||
import {CONTEXT, LView, LViewFlags, PARENT, TVIEW, TView, T_HOST} from '../interfaces/view';
|
||||
import {assertNodeType} from '../node_assert';
|
||||
import {insertView, removeView} from '../node_manipulation';
|
||||
import {enterView, getIsParent, getLView, getPreviousOrParentTNode, leaveView, setIsParent, setPreviousOrParentTNode} from '../state';
|
||||
import {isCreationMode, resetPreOrderHookFlags} from '../util/view_utils';
|
||||
import {assignTViewNodeToLView, createLView, createTView, refreshDescendantViews} from './shared';
|
||||
import {isCreationMode} from '../util/view_utils';
|
||||
|
||||
import {assignTViewNodeToLView, createLView, createTView, refreshView, renderView} from './shared';
|
||||
|
||||
|
||||
/**
|
||||
* Marks the start of an embedded view.
|
||||
|
@ -126,19 +128,17 @@ function scanForView(lContainer: LContainer, startIdx: number, viewBlockId: numb
|
|||
*/
|
||||
export function ɵɵembeddedViewEnd(): void {
|
||||
const lView = getLView();
|
||||
const tView = lView[TVIEW];
|
||||
const viewHost = lView[T_HOST];
|
||||
const context = lView[CONTEXT];
|
||||
|
||||
if (isCreationMode(lView)) {
|
||||
refreshDescendantViews(lView); // creation mode pass
|
||||
lView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
renderView(lView, tView, context); // creation mode pass
|
||||
}
|
||||
resetPreOrderHookFlags(lView);
|
||||
refreshDescendantViews(lView); // update mode pass
|
||||
refreshView(lView, tView, tView.template, context); // update mode pass
|
||||
|
||||
const lContainer = lView[PARENT] as LContainer;
|
||||
ngDevMode && assertLContainerOrUndefined(lContainer);
|
||||
// It's always safe to run hooks here, as `leaveView` is not called during the 'finally' block
|
||||
// of a try-catch-finally statement, so it can never be reached while unwinding the stack due to
|
||||
// an error being thrown.
|
||||
leaveView(lContainer[PARENT] !, /* safeToRunHooks */ true);
|
||||
leaveView(lContainer[PARENT] !);
|
||||
setPreviousOrParentTNode(viewHost !, false);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import {RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRend
|
|||
import {SanitizerFn} from '../interfaces/sanitization';
|
||||
import {isComponent, isComponentDef, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks';
|
||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from '../node_assert';
|
||||
import {assertNodeOfPossibleTypes} from '../node_assert';
|
||||
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
||||
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, getSelectedIndex, incrementActiveDirectiveId, leaveView, namespaceHTMLInternal, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
||||
import {renderStylingMap} from '../styling_next/bindings';
|
||||
|
@ -52,69 +52,6 @@ export const enum BindingDirection {
|
|||
Output,
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the view, executing the following steps in that order:
|
||||
* triggers init hooks, refreshes dynamic embedded views, triggers content hooks, sets host
|
||||
* bindings, refreshes child components.
|
||||
* Note: view hooks are triggered later when leaving the view.
|
||||
*/
|
||||
export function refreshDescendantViews(lView: LView) {
|
||||
const tView = lView[TVIEW];
|
||||
const creationMode = isCreationMode(lView);
|
||||
|
||||
if (!creationMode) {
|
||||
// Resetting the bindingIndex of the current LView as the next steps may trigger change
|
||||
// detection.
|
||||
lView[BINDING_INDEX] = tView.bindingStartIndex;
|
||||
|
||||
const checkNoChangesMode = getCheckNoChangesMode();
|
||||
|
||||
executePreOrderHooks(lView, tView, checkNoChangesMode, undefined);
|
||||
|
||||
refreshDynamicEmbeddedViews(lView);
|
||||
|
||||
// Content query results must be refreshed before content hooks are called.
|
||||
if (tView.contentQueries !== null) {
|
||||
refreshContentQueries(tView, lView);
|
||||
}
|
||||
|
||||
resetPreOrderHookFlags(lView);
|
||||
executeHooks(
|
||||
lView, tView.contentHooks, tView.contentCheckHooks, checkNoChangesMode,
|
||||
InitPhaseState.AfterContentInitHooksToBeRun, undefined);
|
||||
|
||||
setHostBindings(tView, lView);
|
||||
} else {
|
||||
// This needs to be set before children are processed to support recursive components.
|
||||
// This must be set to false immediately after the first creation run because in an
|
||||
// ngFor loop, all the views will be created together before update mode runs and turns
|
||||
// off firstTemplatePass. If we don't set it here, instances will perform directive
|
||||
// matching, etc again and again.
|
||||
tView.firstTemplatePass = false;
|
||||
|
||||
// We resolve content queries specifically marked as `static` in creation mode. Dynamic
|
||||
// content queries are resolved during change detection (i.e. update mode), after embedded
|
||||
// views are refreshed (see block above).
|
||||
if (tView.staticContentQueries) {
|
||||
refreshContentQueries(tView, lView);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We must materialize query results before child components are processed
|
||||
// in case a child component has projected a container. The LContainer needs
|
||||
// to exist so the embedded views are properly attached by the container.
|
||||
if (!creationMode || tView.staticViewQueries) {
|
||||
executeViewQueryFn(RenderFlags.Update, tView, lView[CONTEXT]);
|
||||
}
|
||||
|
||||
const components = tView.components;
|
||||
if (components !== null) {
|
||||
refreshChildComponents(lView, components);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Sets the host bindings for the current view. */
|
||||
export function setHostBindings(tView: TView, viewData: LView): void {
|
||||
const selectedIndex = getSelectedIndex();
|
||||
|
@ -186,13 +123,19 @@ function refreshContentQueries(tView: TView, lView: LView): void {
|
|||
}
|
||||
}
|
||||
|
||||
/** Refreshes child components in the current view. */
|
||||
/** Refreshes child components in the current view (update mode). */
|
||||
function refreshChildComponents(hostLView: LView, components: number[]): void {
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
componentRefresh(hostLView, components[i]);
|
||||
refreshComponent(hostLView, components[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Renders child components in the current view (creation mode). */
|
||||
function renderChildComponents(hostLView: LView, components: number[]): void {
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
renderComponent(hostLView, components[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a native element from a tag name, using a renderer.
|
||||
|
@ -380,70 +323,145 @@ export function createEmbeddedViewAndNode<T>(
|
|||
}
|
||||
|
||||
/**
|
||||
* Used for rendering views in a LContainer (embedded views or root component views for dynamically
|
||||
* created components).
|
||||
*
|
||||
* Dynamically created views must store/retrieve their TViews differently from component views
|
||||
* because their template functions are nested in the template functions of their hosts, creating
|
||||
* closures. If their host template happens to be an embedded template in a loop (e.g. ngFor
|
||||
* inside
|
||||
* an ngFor), the nesting would mean we'd have multiple instances of the template function, so we
|
||||
* can't store TViews in the template function itself (as we do for comps). Instead, we store the
|
||||
* TView for dynamically created views on their host TNode, which only has one instance.
|
||||
* Processes a view in the creation mode. This includes a number of steps in a specific order:
|
||||
* - creating view query functions (if any);
|
||||
* - executing a template function in the creation mode;
|
||||
* - updating static queries (if any);
|
||||
* - creating child components defined in a given view.
|
||||
*/
|
||||
export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, context: T) {
|
||||
const _isParent = getIsParent();
|
||||
const _previousOrParentTNode = getPreviousOrParentTNode();
|
||||
let oldView: LView;
|
||||
// Will become true if the `try` block executes with no errors.
|
||||
let safeToRunHooks = false;
|
||||
export function renderView<T>(lView: LView, tView: TView, context: T): void {
|
||||
ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
|
||||
const oldView = enterView(lView, lView[T_HOST]);
|
||||
try {
|
||||
oldView = enterView(viewToRender, viewToRender[T_HOST]);
|
||||
resetPreOrderHookFlags(viewToRender);
|
||||
const viewQuery = tView.viewQuery;
|
||||
if (viewQuery !== null) {
|
||||
executeViewQueryFn(RenderFlags.Create, viewQuery, context);
|
||||
}
|
||||
|
||||
// Execute a template associated with this view, if it exists. A template function might not be
|
||||
// defined for the root component views.
|
||||
const templateFn = tView.template;
|
||||
if (templateFn !== null) {
|
||||
executeTemplate(viewToRender, templateFn, getRenderFlags(viewToRender), context);
|
||||
executeTemplate(lView, templateFn, RenderFlags.Create, context);
|
||||
}
|
||||
refreshDescendantViews(viewToRender);
|
||||
safeToRunHooks = true;
|
||||
|
||||
// This needs to be set before children are processed to support recursive components.
|
||||
// This must be set to false immediately after the first creation run because in an
|
||||
// ngFor loop, all the views will be created together before update mode runs and turns
|
||||
// off firstTemplatePass. If we don't set it here, instances will perform directive
|
||||
// matching, etc again and again.
|
||||
if (tView.firstTemplatePass) {
|
||||
tView.firstTemplatePass = false;
|
||||
}
|
||||
|
||||
// We resolve content queries specifically marked as `static` in creation mode. Dynamic
|
||||
// content queries are resolved during change detection (i.e. update mode), after embedded
|
||||
// views are refreshed (see block above).
|
||||
if (tView.staticContentQueries) {
|
||||
refreshContentQueries(tView, lView);
|
||||
}
|
||||
|
||||
// We must materialize query results before child components are processed
|
||||
// in case a child component has projected a container. The LContainer needs
|
||||
// to exist so the embedded views are properly attached by the container.
|
||||
if (tView.staticViewQueries) {
|
||||
executeViewQueryFn(RenderFlags.Update, tView.viewQuery !, context);
|
||||
}
|
||||
|
||||
// Render child component views.
|
||||
const components = tView.components;
|
||||
if (components !== null) {
|
||||
renderChildComponents(lView, components);
|
||||
}
|
||||
|
||||
} finally {
|
||||
leaveView(oldView !, safeToRunHooks);
|
||||
setPreviousOrParentTNode(_previousOrParentTNode, _isParent);
|
||||
lView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
leaveView(oldView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a view in update mode. This includes a number of steps in a specific order:
|
||||
* - executing a template function in update mode;
|
||||
* - executing hooks;
|
||||
* - refreshing queries;
|
||||
* - setting host bindings;
|
||||
* - refreshing child (embedded and component) views.
|
||||
*/
|
||||
export function refreshView<T>(
|
||||
lView: LView, tView: TView, templateFn: ComponentTemplate<{}>| null, context: T) {
|
||||
ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
|
||||
let oldView = enterView(lView, lView[T_HOST]);
|
||||
try {
|
||||
resetPreOrderHookFlags(lView);
|
||||
|
||||
if (templateFn !== null) {
|
||||
executeTemplate(lView, templateFn, RenderFlags.Update, context);
|
||||
}
|
||||
|
||||
// Resetting the bindingIndex of the current LView as the next steps may trigger change
|
||||
// detection.
|
||||
lView[BINDING_INDEX] = tView.bindingStartIndex;
|
||||
|
||||
const checkNoChangesMode = getCheckNoChangesMode();
|
||||
|
||||
executePreOrderHooks(lView, tView, checkNoChangesMode, undefined);
|
||||
|
||||
refreshDynamicEmbeddedViews(lView);
|
||||
|
||||
// Content query results must be refreshed before content hooks are called.
|
||||
if (tView.contentQueries !== null) {
|
||||
refreshContentQueries(tView, lView);
|
||||
}
|
||||
|
||||
resetPreOrderHookFlags(lView);
|
||||
executeHooks(
|
||||
lView, tView.contentHooks, tView.contentCheckHooks, checkNoChangesMode,
|
||||
InitPhaseState.AfterContentInitHooksToBeRun, undefined);
|
||||
|
||||
setHostBindings(tView, lView);
|
||||
|
||||
const viewQuery = tView.viewQuery;
|
||||
if (viewQuery !== null) {
|
||||
executeViewQueryFn(RenderFlags.Update, viewQuery, context);
|
||||
}
|
||||
|
||||
// Refresh child component views.
|
||||
const components = tView.components;
|
||||
if (components !== null) {
|
||||
refreshChildComponents(lView, components);
|
||||
}
|
||||
|
||||
resetPreOrderHookFlags(lView);
|
||||
executeHooks(
|
||||
lView, tView.viewHooks, tView.viewCheckHooks, checkNoChangesMode,
|
||||
InitPhaseState.AfterViewInitHooksToBeRun, undefined);
|
||||
|
||||
} finally {
|
||||
lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass);
|
||||
lView[BINDING_INDEX] = tView.bindingStartIndex;
|
||||
leaveView(oldView);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderComponentOrTemplate<T>(
|
||||
hostView: LView, context: T, templateFn?: ComponentTemplate<T>) {
|
||||
hostView: LView, templateFn: ComponentTemplate<{}>| null, context: T) {
|
||||
const rendererFactory = hostView[RENDERER_FACTORY];
|
||||
const oldView = enterView(hostView, hostView[T_HOST]);
|
||||
const normalExecutionPath = !getCheckNoChangesMode();
|
||||
const creationModeIsActive = isCreationMode(hostView);
|
||||
|
||||
// Will become true if the `try` block executes with no errors.
|
||||
let safeToRunHooks = false;
|
||||
try {
|
||||
if (normalExecutionPath && !creationModeIsActive && rendererFactory.begin) {
|
||||
rendererFactory.begin();
|
||||
}
|
||||
|
||||
const tView = hostView[TVIEW];
|
||||
if (creationModeIsActive) {
|
||||
// creation mode pass
|
||||
templateFn && executeTemplate(hostView, templateFn, RenderFlags.Create, context);
|
||||
|
||||
refreshDescendantViews(hostView);
|
||||
hostView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
renderView(hostView, tView, context);
|
||||
}
|
||||
|
||||
// update mode pass
|
||||
resetPreOrderHookFlags(hostView);
|
||||
templateFn && executeTemplate(hostView, templateFn, RenderFlags.Update, context);
|
||||
refreshDescendantViews(hostView);
|
||||
safeToRunHooks = true;
|
||||
refreshView(hostView, tView, templateFn, context);
|
||||
} finally {
|
||||
if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) {
|
||||
rendererFactory.end();
|
||||
}
|
||||
leaveView(oldView, safeToRunHooks);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,15 +482,6 @@ function executeTemplate<T>(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the default configuration of rendering flags depending on when the
|
||||
* template is in creation mode or update mode. Update block and create block are
|
||||
* always run separately.
|
||||
*/
|
||||
function getRenderFlags(view: LView): RenderFlags {
|
||||
return isCreationMode(view) ? RenderFlags.Create : RenderFlags.Update;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
//// Element
|
||||
//////////////////////////
|
||||
|
@ -1469,8 +1478,7 @@ export function createLContainer(
|
|||
|
||||
/**
|
||||
* Goes over dynamic embedded views (ones created through ViewContainerRef APIs) and refreshes
|
||||
* them
|
||||
* by executing an associated template function.
|
||||
* them by executing an associated template function.
|
||||
*/
|
||||
function refreshDynamicEmbeddedViews(lView: LView) {
|
||||
let viewOrContainer = lView[CHILD_HEAD];
|
||||
|
@ -1480,10 +1488,9 @@ function refreshDynamicEmbeddedViews(lView: LView) {
|
|||
if (isLContainer(viewOrContainer) && viewOrContainer[ACTIVE_INDEX] === -1) {
|
||||
for (let i = CONTAINER_HEADER_OFFSET; i < viewOrContainer.length; i++) {
|
||||
const embeddedLView = viewOrContainer[i];
|
||||
// The directives and pipes are not needed here as an existing view is only being
|
||||
// refreshed.
|
||||
ngDevMode && assertDefined(embeddedLView[TVIEW], 'TView must be allocated');
|
||||
renderEmbeddedTemplate(embeddedLView, embeddedLView[TVIEW], embeddedLView[CONTEXT] !);
|
||||
const embeddedTView = embeddedLView[TVIEW];
|
||||
ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
|
||||
refreshView(embeddedLView, embeddedTView, embeddedTView.template, embeddedLView[CONTEXT] !);
|
||||
}
|
||||
}
|
||||
viewOrContainer = viewOrContainer[NEXT];
|
||||
|
@ -1497,23 +1504,26 @@ function refreshDynamicEmbeddedViews(lView: LView) {
|
|||
/**
|
||||
* Refreshes components by entering the component view and processing its bindings, queries, etc.
|
||||
*
|
||||
* @param adjustedElementIndex Element index in LView[] (adjusted for HEADER_OFFSET)
|
||||
* @param componentHostIdx Element index in LView[] (adjusted for HEADER_OFFSET)
|
||||
*/
|
||||
export function componentRefresh(hostLView: LView, adjustedElementIndex: number): void {
|
||||
ngDevMode && assertDataInRange(hostLView, adjustedElementIndex);
|
||||
const componentView = getComponentViewByIndex(adjustedElementIndex, hostLView);
|
||||
ngDevMode &&
|
||||
assertNodeType(hostLView[TVIEW].data[adjustedElementIndex] as TNode, TNodeType.Element);
|
||||
|
||||
// Only components in creation mode, attached CheckAlways
|
||||
// components or attached, dirty OnPush components should be checked
|
||||
if ((viewAttachedToChangeDetector(componentView) || isCreationMode(hostLView)) &&
|
||||
function refreshComponent(hostLView: LView, componentHostIdx: number): void {
|
||||
ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode');
|
||||
const componentView = getComponentViewByIndex(componentHostIdx, hostLView);
|
||||
// Only attached components that are CheckAlways or OnPush and dirty should be refreshed
|
||||
if (viewAttachedToChangeDetector(componentView) &&
|
||||
componentView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
|
||||
syncViewWithBlueprint(componentView);
|
||||
checkView(componentView, componentView[CONTEXT]);
|
||||
const tView = componentView[TVIEW];
|
||||
refreshView(componentView, tView, tView.template, componentView[CONTEXT]);
|
||||
}
|
||||
}
|
||||
|
||||
function renderComponent(hostLView: LView, componentHostIdx: number) {
|
||||
ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
|
||||
const componentView = getComponentViewByIndex(componentHostIdx, hostLView);
|
||||
syncViewWithBlueprint(componentView);
|
||||
renderView(componentView, componentView[TVIEW], componentView[CONTEXT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs an LView instance with its blueprint if they have gotten out of sync.
|
||||
*
|
||||
|
@ -1647,7 +1657,9 @@ export function scheduleTick(rootContext: RootContext, flags: RootContextFlags)
|
|||
export function tickRootContext(rootContext: RootContext) {
|
||||
for (let i = 0; i < rootContext.components.length; i++) {
|
||||
const rootComponent = rootContext.components[i];
|
||||
renderComponentOrTemplate(readPatchedLView(rootComponent) !, rootComponent);
|
||||
const lView = readPatchedLView(rootComponent) !;
|
||||
const tView = lView[TVIEW];
|
||||
renderComponentOrTemplate(lView, tView.template, rootComponent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1655,12 +1667,9 @@ export function detectChangesInternal<T>(view: LView, context: T) {
|
|||
const rendererFactory = view[RENDERER_FACTORY];
|
||||
|
||||
if (rendererFactory.begin) rendererFactory.begin();
|
||||
|
||||
try {
|
||||
if (isCreationMode(view)) {
|
||||
checkView(view, context); // creation mode pass
|
||||
}
|
||||
checkView(view, context); // update mode pass
|
||||
const tView = view[TVIEW];
|
||||
refreshView(view, tView, tView.template, context);
|
||||
} catch (error) {
|
||||
handleError(view, error);
|
||||
throw error;
|
||||
|
@ -1717,33 +1726,11 @@ export function checkNoChangesInRootView(lView: LView): void {
|
|||
}
|
||||
}
|
||||
|
||||
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck.
|
||||
*/
|
||||
export function checkView<T>(hostView: LView, component: T) {
|
||||
const hostTView = hostView[TVIEW];
|
||||
const oldView = enterView(hostView, hostView[T_HOST]);
|
||||
const templateFn = hostTView.template !;
|
||||
const creationMode = isCreationMode(hostView);
|
||||
|
||||
// Will become true if the `try` block executes with no errors.
|
||||
let safeToRunHooks = false;
|
||||
try {
|
||||
resetPreOrderHookFlags(hostView);
|
||||
creationMode && executeViewQueryFn(RenderFlags.Create, hostTView, component);
|
||||
executeTemplate(hostView, templateFn, getRenderFlags(hostView), component);
|
||||
refreshDescendantViews(hostView);
|
||||
safeToRunHooks = true;
|
||||
} finally {
|
||||
leaveView(oldView, safeToRunHooks);
|
||||
}
|
||||
}
|
||||
|
||||
function executeViewQueryFn<T>(flags: RenderFlags, tView: TView, component: T): void {
|
||||
const viewQuery = tView.viewQuery;
|
||||
if (viewQuery !== null) {
|
||||
setCurrentQueryIndex(0);
|
||||
viewQuery(flags, component);
|
||||
}
|
||||
function executeViewQueryFn<T>(
|
||||
flags: RenderFlags, viewQueryFn: ViewQueriesFunction<{}>, component: T): void {
|
||||
ngDevMode && assertDefined(viewQueryFn, 'View queries function to execute must be defined.');
|
||||
setCurrentQueryIndex(0);
|
||||
viewQueryFn(flags, component);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,13 +10,10 @@ import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
|
|||
import {assertDefined} from '../util/assert';
|
||||
|
||||
import {assertLViewOrUndefined} from './assert';
|
||||
import {executeHooks} from './hooks';
|
||||
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
||||
import {TElementNode, TNode, TViewNode} from './interfaces/node';
|
||||
import {BINDING_INDEX, CONTEXT, DECLARATION_VIEW, FLAGS, InitPhaseState, LView, LViewFlags, OpaqueViewState, TVIEW} from './interfaces/view';
|
||||
import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TVIEW} from './interfaces/view';
|
||||
import {resetAllStylingState, resetStylingState} from './styling_next/state';
|
||||
import {isCreationMode, resetPreOrderHookFlags} from './util/view_utils';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -461,27 +458,9 @@ export function resetComponentState() {
|
|||
* Used in lieu of enterView to make it clear when we are exiting a child view. This makes
|
||||
* the direction of traversal (up or down the view tree) a bit clearer.
|
||||
*
|
||||
* @param newView New state to become active
|
||||
* @param safeToRunHooks Whether the runtime is in a state where running lifecycle hooks is valid.
|
||||
* This is not always the case (for example, the application may have crashed and `leaveView` is
|
||||
* being executed while unwinding the call stack).
|
||||
* @param newView New LView to become active
|
||||
*/
|
||||
export function leaveView(newView: LView, safeToRunHooks: boolean): void {
|
||||
const tView = lView[TVIEW];
|
||||
if (isCreationMode(lView)) {
|
||||
lView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
} else {
|
||||
try {
|
||||
resetPreOrderHookFlags(lView);
|
||||
safeToRunHooks && executeHooks(
|
||||
lView, tView.viewHooks, tView.viewCheckHooks, checkNoChangesMode,
|
||||
InitPhaseState.AfterViewInitHooksToBeRun, undefined);
|
||||
} finally {
|
||||
// Views are clean and in update mode after being checked, so these bits are cleared
|
||||
lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass);
|
||||
lView[BINDING_INDEX] = tView.bindingStartIndex;
|
||||
}
|
||||
}
|
||||
export function leaveView(newView: LView): void {
|
||||
enterView(newView, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -135,6 +135,7 @@ export function load<T>(view: LView | TData, index: number): T {
|
|||
|
||||
export function getComponentViewByIndex(nodeIndex: number, hostView: LView): LView {
|
||||
// Could be an LView or an LContainer. If LContainer, unwrap to find LView.
|
||||
ngDevMode && assertDataInRange(hostView, nodeIndex);
|
||||
const slotValue = hostView[nodeIndex];
|
||||
const lView = isLView(slotValue) ? slotValue : slotValue[HOST];
|
||||
return lView;
|
||||
|
|
|
@ -19,7 +19,7 @@ import {addToArray, removeFromArray} from '../util/array_utils';
|
|||
import {assertDefined, assertGreaterThan, assertLessThan} from '../util/assert';
|
||||
import {assertLContainer} from './assert';
|
||||
import {NodeInjector, getParentInjectorLocation} from './di';
|
||||
import {addToViewTree, createEmbeddedViewAndNode, createLContainer, renderEmbeddedTemplate} from './instructions/shared';
|
||||
import {addToViewTree, createEmbeddedViewAndNode, createLContainer, renderView} from './instructions/shared';
|
||||
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer, VIEW_REFS} from './interfaces/container';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
|
||||
import {RComment, RElement, isProceduralRenderer} from './interfaces/renderer';
|
||||
|
@ -121,7 +121,8 @@ export function createTemplateRef<T>(
|
|||
lView[QUERIES] = declarationViewLQueries.createEmbeddedView(embeddedTView);
|
||||
}
|
||||
|
||||
renderEmbeddedTemplate(lView, embeddedTView, context);
|
||||
renderView(lView, embeddedTView, context);
|
||||
|
||||
const viewRef = new ViewRef(lView, context, -1);
|
||||
viewRef._tViewNode = lView[T_HOST] as TViewNode;
|
||||
return viewRef;
|
||||
|
|
|
@ -61,6 +61,22 @@ describe('ViewContainerRef', () => {
|
|||
expect(fixture.componentInstance.foo).toBeAnInstanceOf(TemplateRef);
|
||||
});
|
||||
|
||||
it('should construct proper TNode / DOM tree when embedded views are created in a directive constructor',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'view-insertion-test-cmpt',
|
||||
template:
|
||||
`<div>before<ng-template constructorDir><span>|middle|</span></ng-template>after</div>`
|
||||
})
|
||||
class ViewInsertionTestCmpt {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [ViewInsertionTestCmpt, ConstructorDir]});
|
||||
|
||||
const fixture = TestBed.createComponent(ViewInsertionTestCmpt);
|
||||
expect(fixture.nativeElement).toHaveText('before|middle|after');
|
||||
});
|
||||
|
||||
it('should use comment node of host ng-container as insertion marker', () => {
|
||||
@Component({template: 'hello'})
|
||||
class HelloComp {
|
||||
|
|
|
@ -203,12 +203,6 @@
|
|||
{
|
||||
"name": "checkNoChangesMode"
|
||||
},
|
||||
{
|
||||
"name": "checkView"
|
||||
},
|
||||
{
|
||||
"name": "componentRefresh"
|
||||
},
|
||||
{
|
||||
"name": "concatString"
|
||||
},
|
||||
|
@ -398,9 +392,6 @@
|
|||
{
|
||||
"name": "getPreviousOrParentTNode"
|
||||
},
|
||||
{
|
||||
"name": "getRenderFlags"
|
||||
},
|
||||
{
|
||||
"name": "getRenderParent"
|
||||
},
|
||||
|
@ -464,9 +455,6 @@
|
|||
{
|
||||
"name": "isContentQueryHost"
|
||||
},
|
||||
{
|
||||
"name": "isCreationMode"
|
||||
},
|
||||
{
|
||||
"name": "isCssClassMatching"
|
||||
},
|
||||
|
@ -545,14 +533,17 @@
|
|||
{
|
||||
"name": "refreshChildComponents"
|
||||
},
|
||||
{
|
||||
"name": "refreshComponent"
|
||||
},
|
||||
{
|
||||
"name": "refreshContentQueries"
|
||||
},
|
||||
{
|
||||
"name": "refreshDescendantViews"
|
||||
"name": "refreshDynamicEmbeddedViews"
|
||||
},
|
||||
{
|
||||
"name": "refreshDynamicEmbeddedViews"
|
||||
"name": "refreshView"
|
||||
},
|
||||
{
|
||||
"name": "registerInitialStylingOnTNode"
|
||||
|
@ -563,11 +554,14 @@
|
|||
{
|
||||
"name": "registerPreOrderHooks"
|
||||
},
|
||||
{
|
||||
"name": "renderChildComponents"
|
||||
},
|
||||
{
|
||||
"name": "renderComponent"
|
||||
},
|
||||
{
|
||||
"name": "renderEmbeddedTemplate"
|
||||
"name": "renderComponent"
|
||||
},
|
||||
{
|
||||
"name": "renderInitialStyling"
|
||||
|
@ -578,6 +572,9 @@
|
|||
{
|
||||
"name": "renderStylingMap"
|
||||
},
|
||||
{
|
||||
"name": "renderView"
|
||||
},
|
||||
{
|
||||
"name": "resetAllStylingState"
|
||||
},
|
||||
|
|
|
@ -173,12 +173,6 @@
|
|||
{
|
||||
"name": "checkNoChangesMode"
|
||||
},
|
||||
{
|
||||
"name": "checkView"
|
||||
},
|
||||
{
|
||||
"name": "componentRefresh"
|
||||
},
|
||||
{
|
||||
"name": "createLView"
|
||||
},
|
||||
|
@ -320,9 +314,6 @@
|
|||
{
|
||||
"name": "getPreviousOrParentTNode"
|
||||
},
|
||||
{
|
||||
"name": "getRenderFlags"
|
||||
},
|
||||
{
|
||||
"name": "getRenderParent"
|
||||
},
|
||||
|
@ -356,9 +347,6 @@
|
|||
{
|
||||
"name": "isComponentDef"
|
||||
},
|
||||
{
|
||||
"name": "isCreationMode"
|
||||
},
|
||||
{
|
||||
"name": "isFactory"
|
||||
},
|
||||
|
@ -411,23 +399,32 @@
|
|||
"name": "refreshChildComponents"
|
||||
},
|
||||
{
|
||||
"name": "refreshContentQueries"
|
||||
"name": "refreshComponent"
|
||||
},
|
||||
{
|
||||
"name": "refreshDescendantViews"
|
||||
"name": "refreshContentQueries"
|
||||
},
|
||||
{
|
||||
"name": "refreshDynamicEmbeddedViews"
|
||||
},
|
||||
{
|
||||
"name": "refreshView"
|
||||
},
|
||||
{
|
||||
"name": "renderChildComponents"
|
||||
},
|
||||
{
|
||||
"name": "renderComponent"
|
||||
},
|
||||
{
|
||||
"name": "renderEmbeddedTemplate"
|
||||
"name": "renderComponent"
|
||||
},
|
||||
{
|
||||
"name": "renderStringify"
|
||||
},
|
||||
{
|
||||
"name": "renderView"
|
||||
},
|
||||
{
|
||||
"name": "resetAllStylingState"
|
||||
},
|
||||
|
|
|
@ -509,18 +509,12 @@
|
|||
{
|
||||
"name": "checkNoChangesMode"
|
||||
},
|
||||
{
|
||||
"name": "checkView"
|
||||
},
|
||||
{
|
||||
"name": "cleanUpView"
|
||||
},
|
||||
{
|
||||
"name": "collectNativeNodes"
|
||||
},
|
||||
{
|
||||
"name": "componentRefresh"
|
||||
},
|
||||
{
|
||||
"name": "concatString"
|
||||
},
|
||||
|
@ -890,9 +884,6 @@
|
|||
{
|
||||
"name": "getPropValuesStartPosition"
|
||||
},
|
||||
{
|
||||
"name": "getRenderFlags"
|
||||
},
|
||||
{
|
||||
"name": "getRenderParent"
|
||||
},
|
||||
|
@ -1184,14 +1175,17 @@
|
|||
{
|
||||
"name": "refreshChildComponents"
|
||||
},
|
||||
{
|
||||
"name": "refreshComponent"
|
||||
},
|
||||
{
|
||||
"name": "refreshContentQueries"
|
||||
},
|
||||
{
|
||||
"name": "refreshDescendantViews"
|
||||
"name": "refreshDynamicEmbeddedViews"
|
||||
},
|
||||
{
|
||||
"name": "refreshDynamicEmbeddedViews"
|
||||
"name": "refreshView"
|
||||
},
|
||||
{
|
||||
"name": "registerBinding"
|
||||
|
@ -1214,6 +1208,12 @@
|
|||
{
|
||||
"name": "removeView"
|
||||
},
|
||||
{
|
||||
"name": "renderChildComponents"
|
||||
},
|
||||
{
|
||||
"name": "renderComponent"
|
||||
},
|
||||
{
|
||||
"name": "renderComponent"
|
||||
},
|
||||
|
@ -1223,9 +1223,6 @@
|
|||
{
|
||||
"name": "renderDetachView"
|
||||
},
|
||||
{
|
||||
"name": "renderEmbeddedTemplate"
|
||||
},
|
||||
{
|
||||
"name": "renderInitialStyling"
|
||||
},
|
||||
|
@ -1235,6 +1232,9 @@
|
|||
{
|
||||
"name": "renderStylingMap"
|
||||
},
|
||||
{
|
||||
"name": "renderView"
|
||||
},
|
||||
{
|
||||
"name": "resetAllStylingState"
|
||||
},
|
||||
|
|
|
@ -607,7 +607,6 @@ describe('di', () => {
|
|||
null, createTView(-1, null, 1, 0, null, null, null, null), null, LViewFlags.CheckAlways,
|
||||
null, null, {} as any, {} as any);
|
||||
const oldView = enterView(contentView, null);
|
||||
let safeToRunHooks = false;
|
||||
try {
|
||||
const parentTNode =
|
||||
getOrCreateTNode(contentView[TVIEW], null, 0, TNodeType.Element, null, null);
|
||||
|
@ -618,9 +617,8 @@ describe('di', () => {
|
|||
|
||||
const injector = getOrCreateNodeInjectorForNode(parentTNode, contentView);
|
||||
expect(injector).not.toEqual(-1);
|
||||
safeToRunHooks = true;
|
||||
} finally {
|
||||
leaveView(oldView, safeToRunHooks);
|
||||
leaveView(oldView);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -277,7 +277,7 @@ export function renderTemplate<T>(
|
|||
hostLView, componentTView, context, LViewFlags.CheckAlways, hostNode, hostTNode,
|
||||
providedRendererFactory, renderer, sanitizer);
|
||||
}
|
||||
renderComponentOrTemplate(componentView, context, templateFn);
|
||||
renderComponentOrTemplate(componentView, templateFn, context);
|
||||
return componentView;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue