refactor(core): Remove circular dependency between `LContainer` and `ViewRef`. (#39621)
`LContainer` stores `ViewRef`s this is not quite right as it creates circular dependency between the two types. Also `LContainer` should not be aware of `ViewRef` which iv ViewEngine specific construct. PR Close #39621
This commit is contained in:
parent
621c34ddec
commit
e6ae0c5349
File diff suppressed because it is too large
Load Diff
|
@ -222,7 +222,8 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
get(index: number): ViewRef|null {
|
get(index: number): ViewRef|null {
|
||||||
return this._lContainer[VIEW_REFS] !== null && this._lContainer[VIEW_REFS]![index] || null;
|
const viewRefs = getViewRefs(this._lContainer);
|
||||||
|
return viewRefs !== null && viewRefs[index] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get length(): number {
|
get length(): number {
|
||||||
|
@ -265,8 +266,6 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
||||||
throw new Error('Cannot insert a destroyed View in a ViewContainer!');
|
throw new Error('Cannot insert a destroyed View in a ViewContainer!');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.allocateContainerIfNeeded();
|
|
||||||
|
|
||||||
if (viewAttachedToContainer(lView)) {
|
if (viewAttachedToContainer(lView)) {
|
||||||
// If view is already attached, detach it first so we clean up references appropriately.
|
// If view is already attached, detach it first so we clean up references appropriately.
|
||||||
|
|
||||||
|
@ -309,7 +308,7 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
(viewRef as R3ViewRef<any>).attachToViewContainerRef(this);
|
(viewRef as R3ViewRef<any>).attachToViewContainerRef(this);
|
||||||
addToArray(lContainer[VIEW_REFS]!, adjustedIdx, viewRef);
|
addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef);
|
||||||
|
|
||||||
return viewRef;
|
return viewRef;
|
||||||
}
|
}
|
||||||
|
@ -322,12 +321,11 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
indexOf(viewRef: ViewRef): number {
|
indexOf(viewRef: ViewRef): number {
|
||||||
const viewRefsArr = this._lContainer[VIEW_REFS];
|
const viewRefsArr = getViewRefs(this._lContainer);
|
||||||
return viewRefsArr !== null ? viewRefsArr.indexOf(viewRef) : -1;
|
return viewRefsArr !== null ? viewRefsArr.indexOf(viewRef) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(index?: number): void {
|
remove(index?: number): void {
|
||||||
this.allocateContainerIfNeeded();
|
|
||||||
const adjustedIdx = this._adjustIndex(index, -1);
|
const adjustedIdx = this._adjustIndex(index, -1);
|
||||||
const detachedView = detachView(this._lContainer, adjustedIdx);
|
const detachedView = detachView(this._lContainer, adjustedIdx);
|
||||||
|
|
||||||
|
@ -338,17 +336,17 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
||||||
// rely on an accurate container length.
|
// rely on an accurate container length.
|
||||||
// (e.g. a method on this view container being called by a child directive's OnDestroy
|
// (e.g. a method on this view container being called by a child directive's OnDestroy
|
||||||
// lifecycle hook)
|
// lifecycle hook)
|
||||||
removeFromArray(this._lContainer[VIEW_REFS]!, adjustedIdx);
|
removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx);
|
||||||
destroyLView(detachedView[TVIEW], detachedView);
|
destroyLView(detachedView[TVIEW], detachedView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
detach(index?: number): ViewRef|null {
|
detach(index?: number): ViewRef|null {
|
||||||
this.allocateContainerIfNeeded();
|
|
||||||
const adjustedIdx = this._adjustIndex(index, -1);
|
const adjustedIdx = this._adjustIndex(index, -1);
|
||||||
const view = detachView(this._lContainer, adjustedIdx);
|
const view = detachView(this._lContainer, adjustedIdx);
|
||||||
|
|
||||||
const wasDetached = view && removeFromArray(this._lContainer[VIEW_REFS]!, adjustedIdx) != null;
|
const wasDetached =
|
||||||
|
view && removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx) != null;
|
||||||
return wasDetached ? new R3ViewRef(view!) : null;
|
return wasDetached ? new R3ViewRef(view!) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,14 +361,16 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private allocateContainerIfNeeded(): void {
|
|
||||||
if (this._lContainer[VIEW_REFS] === null) {
|
|
||||||
this._lContainer[VIEW_REFS] = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getViewRefs(lContainer: LContainer): ViewRef[]|null {
|
||||||
|
return lContainer[VIEW_REFS];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOrCreateViewRefs(lContainer: LContainer): ViewRef[] {
|
||||||
|
return lContainer[VIEW_REFS] || (lContainer[VIEW_REFS] = []);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a ViewContainerRef and stores it on the injector.
|
* Creates a ViewContainerRef and stores it on the injector.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google LLC All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {assertDefined} from '../util/assert';
|
||||||
|
|
||||||
|
import {icuContainerIterate} from './i18n/i18n_tree_shaking';
|
||||||
|
import {CONTAINER_HEADER_OFFSET} from './interfaces/container';
|
||||||
|
import {TElementNode, TIcuContainerNode, TNode, TNodeType} from './interfaces/node';
|
||||||
|
import {RNode} from './interfaces/renderer';
|
||||||
|
import {isLContainer} from './interfaces/type_checks';
|
||||||
|
import {DECLARATION_COMPONENT_VIEW, LView, T_HOST, TVIEW, TView} from './interfaces/view';
|
||||||
|
import {assertTNodeType} from './node_assert';
|
||||||
|
import {getLViewParent} from './util/view_traversal_utils';
|
||||||
|
import {unwrapRNode} from './util/view_utils';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function collectNativeNodes(
|
||||||
|
tView: TView, lView: LView, tNode: TNode|null, result: any[],
|
||||||
|
isProjection: boolean = false): any[] {
|
||||||
|
while (tNode !== null) {
|
||||||
|
ngDevMode &&
|
||||||
|
assertTNodeType(
|
||||||
|
tNode,
|
||||||
|
TNodeType.AnyRNode | TNodeType.AnyContainer | TNodeType.Projection | TNodeType.Icu);
|
||||||
|
|
||||||
|
const lNode = lView[tNode.index];
|
||||||
|
if (lNode !== null) {
|
||||||
|
result.push(unwrapRNode(lNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A given lNode can represent either a native node or a LContainer (when it is a host of a
|
||||||
|
// ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
|
||||||
|
// from the views in this container.
|
||||||
|
if (isLContainer(lNode)) {
|
||||||
|
for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) {
|
||||||
|
const lViewInAContainer = lNode[i];
|
||||||
|
const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
|
||||||
|
if (lViewFirstChildTNode !== null) {
|
||||||
|
collectNativeNodes(
|
||||||
|
lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tNodeType = tNode.type;
|
||||||
|
if (tNodeType & TNodeType.ElementContainer) {
|
||||||
|
collectNativeNodes(tView, lView, tNode.child, result);
|
||||||
|
} else if (tNodeType & TNodeType.Icu) {
|
||||||
|
const nextRNode = icuContainerIterate(tNode as TIcuContainerNode, lView);
|
||||||
|
let rNode: RNode|null;
|
||||||
|
while (rNode = nextRNode()) {
|
||||||
|
result.push(rNode);
|
||||||
|
}
|
||||||
|
} else if (tNodeType & TNodeType.Projection) {
|
||||||
|
const componentView = lView[DECLARATION_COMPONENT_VIEW];
|
||||||
|
const componentHost = componentView[T_HOST] as TElementNode;
|
||||||
|
const slotIdx = tNode.projection as number;
|
||||||
|
ngDevMode &&
|
||||||
|
assertDefined(
|
||||||
|
componentHost.projection,
|
||||||
|
'Components with projection nodes (<ng-content>) must have projection slots defined.');
|
||||||
|
|
||||||
|
const nodesInSlot = componentHost.projection![slotIdx];
|
||||||
|
if (Array.isArray(nodesInSlot)) {
|
||||||
|
result.push(...nodesInSlot);
|
||||||
|
} else {
|
||||||
|
const parentView = getLViewParent(componentView)!;
|
||||||
|
ngDevMode &&
|
||||||
|
assertDefined(
|
||||||
|
parentView,
|
||||||
|
'Component views should always have a parent view (component\'s host view)');
|
||||||
|
collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tNode = isProjection ? tNode.projectionNext : tNode.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -6,8 +6,6 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ViewRef} from '../../linker/view_ref';
|
|
||||||
|
|
||||||
import {TNode} from './node';
|
import {TNode} from './node';
|
||||||
import {RComment, RElement} from './renderer';
|
import {RComment, RElement} from './renderer';
|
||||||
import {HOST, LView, NEXT, PARENT, T_HOST, TRANSPLANTED_VIEWS_TO_REFRESH} from './view';
|
import {HOST, LView, NEXT, PARENT, T_HOST, TRANSPLANTED_VIEWS_TO_REFRESH} from './view';
|
||||||
|
@ -127,8 +125,11 @@ export interface LContainer extends Array<any> {
|
||||||
* Array of `ViewRef`s used by any `ViewContainerRef`s that point to this container.
|
* Array of `ViewRef`s used by any `ViewContainerRef`s that point to this container.
|
||||||
*
|
*
|
||||||
* This is lazily initialized by `ViewContainerRef` when the first view is inserted.
|
* This is lazily initialized by `ViewContainerRef` when the first view is inserted.
|
||||||
|
*
|
||||||
|
* NOTE: This is stored as `any[]` because render3 should really not be aware of `ViewRef` and
|
||||||
|
* doing so creates circular dependency.
|
||||||
*/
|
*/
|
||||||
[VIEW_REFS]: ViewRef[]|null;
|
[VIEW_REFS]: any[]|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: This hack is necessary so we don't erroneously get a circular dependency
|
// Note: This hack is necessary so we don't erroneously get a circular dependency
|
||||||
|
|
|
@ -10,19 +10,10 @@ import {ApplicationRef} from '../application_ref';
|
||||||
import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
||||||
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
|
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
|
||||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
||||||
import {assertDefined} from '../util/assert';
|
import {collectNativeNodes} from './collect_native_nodes';
|
||||||
import {icuContainerIterate} from './i18n/i18n_tree_shaking';
|
|
||||||
|
|
||||||
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupWithContext} from './instructions/shared';
|
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupWithContext} from './instructions/shared';
|
||||||
import {CONTAINER_HEADER_OFFSET} from './interfaces/container';
|
import {CONTEXT, FLAGS, LView, LViewFlags, TVIEW} from './interfaces/view';
|
||||||
import {TElementNode, TIcuContainerNode, TNode, TNodeType} from './interfaces/node';
|
|
||||||
import {RNode} from './interfaces/renderer';
|
|
||||||
import {isLContainer} from './interfaces/type_checks';
|
|
||||||
import {CONTEXT, DECLARATION_COMPONENT_VIEW, FLAGS, LView, LViewFlags, T_HOST, TVIEW, TView} from './interfaces/view';
|
|
||||||
import {assertTNodeType} from './node_assert';
|
|
||||||
import {destroyLView, renderDetachView} from './node_manipulation';
|
import {destroyLView, renderDetachView} from './node_manipulation';
|
||||||
import {getLViewParent} from './util/view_traversal_utils';
|
|
||||||
import {unwrapRNode} from './util/view_utils';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -319,67 +310,3 @@ export class RootViewRef<T> extends ViewRef<T> {
|
||||||
return null!;
|
return null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectNativeNodes(
|
|
||||||
tView: TView, lView: LView, tNode: TNode|null, result: any[],
|
|
||||||
isProjection: boolean = false): any[] {
|
|
||||||
while (tNode !== null) {
|
|
||||||
ngDevMode &&
|
|
||||||
assertTNodeType(
|
|
||||||
tNode,
|
|
||||||
TNodeType.AnyRNode | TNodeType.AnyContainer | TNodeType.Projection | TNodeType.Icu);
|
|
||||||
|
|
||||||
const lNode = lView[tNode.index];
|
|
||||||
if (lNode !== null) {
|
|
||||||
result.push(unwrapRNode(lNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
// A given lNode can represent either a native node or a LContainer (when it is a host of a
|
|
||||||
// ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
|
|
||||||
// from the views in this container.
|
|
||||||
if (isLContainer(lNode)) {
|
|
||||||
for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) {
|
|
||||||
const lViewInAContainer = lNode[i];
|
|
||||||
const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
|
|
||||||
if (lViewFirstChildTNode !== null) {
|
|
||||||
collectNativeNodes(
|
|
||||||
lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const tNodeType = tNode.type;
|
|
||||||
if (tNodeType & TNodeType.ElementContainer) {
|
|
||||||
collectNativeNodes(tView, lView, tNode.child, result);
|
|
||||||
} else if (tNodeType & TNodeType.Icu) {
|
|
||||||
const nextRNode = icuContainerIterate(tNode as TIcuContainerNode, lView);
|
|
||||||
let rNode: RNode|null;
|
|
||||||
while (rNode = nextRNode()) {
|
|
||||||
result.push(rNode);
|
|
||||||
}
|
|
||||||
} else if (tNodeType & TNodeType.Projection) {
|
|
||||||
const componentView = lView[DECLARATION_COMPONENT_VIEW];
|
|
||||||
const componentHost = componentView[T_HOST] as TElementNode;
|
|
||||||
const slotIdx = tNode.projection as number;
|
|
||||||
ngDevMode &&
|
|
||||||
assertDefined(
|
|
||||||
componentHost.projection,
|
|
||||||
'Components with projection nodes (<ng-content>) must have projection slots defined.');
|
|
||||||
|
|
||||||
const nodesInSlot = componentHost.projection![slotIdx];
|
|
||||||
if (Array.isArray(nodesInSlot)) {
|
|
||||||
result.push(...nodesInSlot);
|
|
||||||
} else {
|
|
||||||
const parentView = getLViewParent(componentView)!;
|
|
||||||
ngDevMode &&
|
|
||||||
assertDefined(
|
|
||||||
parentView,
|
|
||||||
'Component views should always have a parent view (component\'s host view)');
|
|
||||||
collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tNode = isProjection ? tNode.projectionNext : tNode.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1055,6 +1055,9 @@
|
||||||
{
|
{
|
||||||
"name": "getOrCreateTNode"
|
"name": "getOrCreateTNode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getOrCreateViewRefs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getOriginalError"
|
"name": "getOriginalError"
|
||||||
},
|
},
|
||||||
|
@ -1106,6 +1109,9 @@
|
||||||
{
|
{
|
||||||
"name": "getTView"
|
"name": "getTView"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getViewRefs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "handleError"
|
"name": "handleError"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1370,6 +1370,9 @@
|
||||||
{
|
{
|
||||||
"name": "getOrCreateTNode"
|
"name": "getOrCreateTNode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getOrCreateViewRefs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getOriginalError"
|
"name": "getOriginalError"
|
||||||
},
|
},
|
||||||
|
@ -1439,6 +1442,9 @@
|
||||||
{
|
{
|
||||||
"name": "getToken"
|
"name": "getToken"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getViewRefs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "handleError"
|
"name": "handleError"
|
||||||
},
|
},
|
||||||
|
|
|
@ -410,6 +410,9 @@
|
||||||
{
|
{
|
||||||
"name": "getOrCreateTNode"
|
"name": "getOrCreateTNode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getOrCreateViewRefs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getOriginalError"
|
"name": "getOriginalError"
|
||||||
},
|
},
|
||||||
|
@ -446,6 +449,9 @@
|
||||||
{
|
{
|
||||||
"name": "getTView"
|
"name": "getTView"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getViewRefs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "handleError"
|
"name": "handleError"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue