fix(core): projected views should be dirty checked when the declaring component is dirty checked. (#16592)
Previously a projected view was only dirty checked when the component in which it was inserted was dirty checked. This fix changes the behavior so that a view is also dirty checked if the declaring component is dirty checked. Note: This does not change the order of change detection, only the fact whether a projected view is dirty checked or not. Fixes #14321
This commit is contained in:

committed by
Miško Hevery

parent
3887d8a429
commit
fcc91d862f
@ -17,7 +17,8 @@ import {checkAndUpdateQuery, createQuery} from './query';
|
||||
import {createTemplateData, createViewContainerData} from './refs';
|
||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||
import {ArgumentType, CheckType, ElementData, NodeData, NodeDef, NodeFlags, ProviderData, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asQueryList, asTextData} from './types';
|
||||
import {NOOP, checkBindingNoChanges, isComponentView, resolveViewDefinition} from './util';
|
||||
import {NOOP, checkBindingNoChanges, isComponentView, markParentViewsForCheckProjectedViews, resolveViewDefinition} from './util';
|
||||
import {detachProjectedView} from './view_attach';
|
||||
|
||||
export function viewDef(
|
||||
flags: ViewFlags, nodes: NodeDef[], updateDirectives?: ViewUpdateFn,
|
||||
@ -314,12 +315,14 @@ function createViewNodes(view: ViewData) {
|
||||
}
|
||||
|
||||
export function checkNoChangesView(view: ViewData) {
|
||||
markProjectedViewsForCheck(view);
|
||||
Services.updateDirectives(view, CheckType.CheckNoChanges);
|
||||
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
||||
Services.updateRenderer(view, CheckType.CheckNoChanges);
|
||||
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
||||
// Note: We don't check queries for changes as we didn't do this in v2.x.
|
||||
// TODO(tbosch): investigate if we can enable the check again in v5.x with a nicer error message.
|
||||
view.state &= ~(ViewState.CheckProjectedViews | ViewState.CheckProjectedView);
|
||||
}
|
||||
|
||||
export function checkAndUpdateView(view: ViewData) {
|
||||
@ -329,6 +332,7 @@ export function checkAndUpdateView(view: ViewData) {
|
||||
} else {
|
||||
view.state &= ~ViewState.FirstCheck;
|
||||
}
|
||||
markProjectedViewsForCheck(view);
|
||||
Services.updateDirectives(view, CheckType.CheckAndUpdate);
|
||||
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
execQueriesAction(
|
||||
@ -343,7 +347,6 @@ export function checkAndUpdateView(view: ViewData) {
|
||||
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
execQueriesAction(
|
||||
view, NodeFlags.TypeViewQuery, NodeFlags.DynamicQuery, CheckType.CheckAndUpdate);
|
||||
|
||||
callLifecycleHooksChildrenFirst(
|
||||
view, NodeFlags.AfterViewChecked |
|
||||
(view.state & ViewState.FirstCheck ? NodeFlags.AfterViewInit : 0));
|
||||
@ -351,6 +354,7 @@ export function checkAndUpdateView(view: ViewData) {
|
||||
if (view.def.flags & ViewFlags.OnPush) {
|
||||
view.state &= ~ViewState.ChecksEnabled;
|
||||
}
|
||||
view.state &= ~(ViewState.CheckProjectedViews | ViewState.CheckProjectedView);
|
||||
}
|
||||
|
||||
export function checkAndUpdateNode(
|
||||
@ -363,6 +367,31 @@ export function checkAndUpdateNode(
|
||||
}
|
||||
}
|
||||
|
||||
function markProjectedViewsForCheck(view: ViewData) {
|
||||
const def = view.def;
|
||||
if (!(def.nodeFlags & NodeFlags.ProjectedTemplate)) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < def.nodes.length; i++) {
|
||||
const nodeDef = def.nodes[i];
|
||||
if (nodeDef.flags & NodeFlags.ProjectedTemplate) {
|
||||
const projectedViews = asElementData(view, i).template._projectedViews;
|
||||
if (projectedViews) {
|
||||
for (let i = 0; i < projectedViews.length; i++) {
|
||||
const projectedView = projectedViews[i];
|
||||
projectedView.state |= ViewState.CheckProjectedView;
|
||||
markParentViewsForCheckProjectedViews(projectedView, view);
|
||||
}
|
||||
}
|
||||
} else if ((nodeDef.childFlags & NodeFlags.ProjectedTemplate) === 0) {
|
||||
// a parent with leafs
|
||||
// no child is a component,
|
||||
// then skip the children
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkAndUpdateNodeInline(
|
||||
view: ViewData, nodeDef: NodeDef, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any,
|
||||
v6?: any, v7?: any, v8?: any, v9?: any): boolean {
|
||||
@ -474,6 +503,7 @@ export function destroyView(view: ViewData) {
|
||||
view.disposables[i]();
|
||||
}
|
||||
}
|
||||
detachProjectedView(view);
|
||||
if (view.renderer.destroyNode) {
|
||||
destroyViewNodes(view);
|
||||
}
|
||||
@ -498,7 +528,9 @@ function destroyViewNodes(view: ViewData) {
|
||||
enum ViewAction {
|
||||
CreateViewNodes,
|
||||
CheckNoChanges,
|
||||
CheckNoChangesProjectedViews,
|
||||
CheckAndUpdate,
|
||||
CheckAndUpdateProjectedViews,
|
||||
Destroy
|
||||
}
|
||||
|
||||
@ -547,18 +579,44 @@ function callViewAction(view: ViewData, action: ViewAction) {
|
||||
const viewState = view.state;
|
||||
switch (action) {
|
||||
case ViewAction.CheckNoChanges:
|
||||
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges &&
|
||||
(viewState & ViewState.Destroyed) === 0) {
|
||||
checkNoChangesView(view);
|
||||
if ((viewState & ViewState.Destroyed) === 0) {
|
||||
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges) {
|
||||
checkNoChangesView(view);
|
||||
} else if (viewState & ViewState.CheckProjectedViews) {
|
||||
execProjectedViewsAction(view, ViewAction.CheckNoChangesProjectedViews);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ViewAction.CheckNoChangesProjectedViews:
|
||||
if ((viewState & ViewState.Destroyed) === 0) {
|
||||
if (viewState & ViewState.CheckProjectedView) {
|
||||
checkNoChangesView(view);
|
||||
} else if (viewState & ViewState.CheckProjectedViews) {
|
||||
execProjectedViewsAction(view, action);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ViewAction.CheckAndUpdate:
|
||||
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges &&
|
||||
(viewState & ViewState.Destroyed) === 0) {
|
||||
checkAndUpdateView(view);
|
||||
if ((viewState & ViewState.Destroyed) === 0) {
|
||||
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges) {
|
||||
checkAndUpdateView(view);
|
||||
} else if (viewState & ViewState.CheckProjectedViews) {
|
||||
execProjectedViewsAction(view, ViewAction.CheckAndUpdateProjectedViews);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ViewAction.CheckAndUpdateProjectedViews:
|
||||
if ((viewState & ViewState.Destroyed) === 0) {
|
||||
if (viewState & ViewState.CheckProjectedView) {
|
||||
checkAndUpdateView(view);
|
||||
} else if (viewState & ViewState.CheckProjectedViews) {
|
||||
execProjectedViewsAction(view, action);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ViewAction.Destroy:
|
||||
// Note: destroyView recurses over all views,
|
||||
// so we don't need to special case projected views here.
|
||||
destroyView(view);
|
||||
break;
|
||||
case ViewAction.CreateViewNodes:
|
||||
@ -567,6 +625,11 @@ function callViewAction(view: ViewData, action: ViewAction) {
|
||||
}
|
||||
}
|
||||
|
||||
function execProjectedViewsAction(view: ViewData, action: ViewAction) {
|
||||
execEmbeddedViewsAction(view, action);
|
||||
execComponentViewsAction(view, action);
|
||||
}
|
||||
|
||||
function execQueriesAction(
|
||||
view: ViewData, queryFlags: NodeFlags, staticDynamicQueryFlag: NodeFlags,
|
||||
checkType: CheckType) {
|
||||
|
Reference in New Issue
Block a user