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 {
|
||||
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 {
|
||||
|
@ -265,8 +266,6 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
|||
throw new Error('Cannot insert a destroyed View in a ViewContainer!');
|
||||
}
|
||||
|
||||
this.allocateContainerIfNeeded();
|
||||
|
||||
if (viewAttachedToContainer(lView)) {
|
||||
// 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);
|
||||
addToArray(lContainer[VIEW_REFS]!, adjustedIdx, viewRef);
|
||||
addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef);
|
||||
|
||||
return viewRef;
|
||||
}
|
||||
|
@ -322,12 +321,11 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
|||
}
|
||||
|
||||
indexOf(viewRef: ViewRef): number {
|
||||
const viewRefsArr = this._lContainer[VIEW_REFS];
|
||||
const viewRefsArr = getViewRefs(this._lContainer);
|
||||
return viewRefsArr !== null ? viewRefsArr.indexOf(viewRef) : -1;
|
||||
}
|
||||
|
||||
remove(index?: number): void {
|
||||
this.allocateContainerIfNeeded();
|
||||
const adjustedIdx = this._adjustIndex(index, -1);
|
||||
const detachedView = detachView(this._lContainer, adjustedIdx);
|
||||
|
||||
|
@ -338,17 +336,17 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
|||
// rely on an accurate container length.
|
||||
// (e.g. a method on this view container being called by a child directive's OnDestroy
|
||||
// lifecycle hook)
|
||||
removeFromArray(this._lContainer[VIEW_REFS]!, adjustedIdx);
|
||||
removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx);
|
||||
destroyLView(detachedView[TVIEW], detachedView);
|
||||
}
|
||||
}
|
||||
|
||||
detach(index?: number): ViewRef|null {
|
||||
this.allocateContainerIfNeeded();
|
||||
const adjustedIdx = this._adjustIndex(index, -1);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -363,14 +361,16 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
|
|||
}
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
||||
import {ViewRef} from '../../linker/view_ref';
|
||||
|
||||
import {TNode} from './node';
|
||||
import {RComment, RElement} from './renderer';
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -10,19 +10,10 @@ import {ApplicationRef} from '../application_ref';
|
|||
import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_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 {assertDefined} from '../util/assert';
|
||||
import {icuContainerIterate} from './i18n/i18n_tree_shaking';
|
||||
|
||||
import {collectNativeNodes} from './collect_native_nodes';
|
||||
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupWithContext} from './instructions/shared';
|
||||
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 {CONTEXT, DECLARATION_COMPONENT_VIEW, FLAGS, LView, LViewFlags, T_HOST, TVIEW, TView} from './interfaces/view';
|
||||
import {assertTNodeType} from './node_assert';
|
||||
import {CONTEXT, FLAGS, LView, LViewFlags, TVIEW} from './interfaces/view';
|
||||
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!;
|
||||
}
|
||||
}
|
||||
|
||||
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": "getOrCreateViewRefs"
|
||||
},
|
||||
{
|
||||
"name": "getOriginalError"
|
||||
},
|
||||
|
@ -1106,6 +1109,9 @@
|
|||
{
|
||||
"name": "getTView"
|
||||
},
|
||||
{
|
||||
"name": "getViewRefs"
|
||||
},
|
||||
{
|
||||
"name": "handleError"
|
||||
},
|
||||
|
|
|
@ -1370,6 +1370,9 @@
|
|||
{
|
||||
"name": "getOrCreateTNode"
|
||||
},
|
||||
{
|
||||
"name": "getOrCreateViewRefs"
|
||||
},
|
||||
{
|
||||
"name": "getOriginalError"
|
||||
},
|
||||
|
@ -1439,6 +1442,9 @@
|
|||
{
|
||||
"name": "getToken"
|
||||
},
|
||||
{
|
||||
"name": "getViewRefs"
|
||||
},
|
||||
{
|
||||
"name": "handleError"
|
||||
},
|
||||
|
|
|
@ -410,6 +410,9 @@
|
|||
{
|
||||
"name": "getOrCreateTNode"
|
||||
},
|
||||
{
|
||||
"name": "getOrCreateViewRefs"
|
||||
},
|
||||
{
|
||||
"name": "getOriginalError"
|
||||
},
|
||||
|
@ -446,6 +449,9 @@
|
|||
{
|
||||
"name": "getTView"
|
||||
},
|
||||
{
|
||||
"name": "getViewRefs"
|
||||
},
|
||||
{
|
||||
"name": "handleError"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue