feat(ivy): support attaching and detaching views from change detection (#22670)
PR Close #22670
This commit is contained in:
@ -16,7 +16,7 @@ import {queueLifecycleHooks} from './hooks';
|
||||
import {CLEAN_PROMISE, _getComponentHostLElementNode, createLView, createTView, directiveCreate, enterView, getDirectiveInstance, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, scheduleTick, tick} from './instructions';
|
||||
import {ComponentDef, ComponentType} from './interfaces/definition';
|
||||
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
import {LViewFlags, RootContext} from './interfaces/view';
|
||||
import {LView, LViewFlags, RootContext} from './interfaces/view';
|
||||
import {stringify} from './util';
|
||||
import {createViewRef} from './view_ref';
|
||||
|
||||
@ -74,13 +74,14 @@ export interface CreateComponentOptions {
|
||||
export function createComponentRef<T>(
|
||||
componentType: ComponentType<T>, opts: CreateComponentOptions): viewEngine_ComponentRef<T> {
|
||||
const component = renderComponent(componentType, opts);
|
||||
const hostView = createViewRef(component);
|
||||
const hostView = _getComponentHostLElementNode(component).data as LView;
|
||||
const hostViewRef = createViewRef(hostView, component);
|
||||
return {
|
||||
location: {nativeElement: getHostElement(component)},
|
||||
injector: opts.injector || NULL_INJECTOR,
|
||||
instance: component,
|
||||
hostView: hostView,
|
||||
changeDetectorRef: hostView,
|
||||
hostView: hostViewRef,
|
||||
changeDetectorRef: hostViewRef,
|
||||
componentType: componentType,
|
||||
// TODO: implement destroy and onDestroy
|
||||
destroy: () => {},
|
||||
|
@ -25,6 +25,7 @@ import {LInjector} from './interfaces/injector';
|
||||
import {LContainerNode, LElementNode, LNode, LNodeFlags, LViewNode} from './interfaces/node';
|
||||
import {QueryReadType} from './interfaces/query';
|
||||
import {Renderer3} from './interfaces/renderer';
|
||||
import {LView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {insertView} from './node_manipulation';
|
||||
import {notImplemented, stringify} from './util';
|
||||
@ -301,7 +302,7 @@ export function getOrCreateChangeDetectorRef(
|
||||
return di.changeDetectorRef = getOrCreateHostChangeDetector(currentNode.view.node);
|
||||
} else if ((currentNode.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.Element) {
|
||||
// if it's an element node with data, it's a component and context will be set later
|
||||
return di.changeDetectorRef = createViewRef(context);
|
||||
return di.changeDetectorRef = createViewRef(currentNode.data as LView, context);
|
||||
}
|
||||
return null !;
|
||||
}
|
||||
@ -313,8 +314,10 @@ function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode):
|
||||
const hostInjector = hostNode.nodeInjector;
|
||||
const existingRef = hostInjector && hostInjector.changeDetectorRef;
|
||||
|
||||
return existingRef ? existingRef :
|
||||
createViewRef(hostNode.view.data[hostNode.flags >> LNodeFlags.INDX_SHIFT]);
|
||||
return existingRef ?
|
||||
existingRef :
|
||||
createViewRef(
|
||||
hostNode.data as LView, hostNode.view.data[hostNode.flags >> LNodeFlags.INDX_SHIFT]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -210,7 +210,7 @@ export function createLView(
|
||||
const newView = {
|
||||
parent: currentView,
|
||||
id: viewId, // -1 for component views
|
||||
flags: flags | LViewFlags.CreationMode,
|
||||
flags: flags | LViewFlags.CreationMode | LViewFlags.Attached,
|
||||
node: null !, // until we initialize it in createNode.
|
||||
data: [],
|
||||
tView: tView,
|
||||
@ -1281,14 +1281,19 @@ export function directiveRefresh<T>(directiveIndex: number, elementIndex: number
|
||||
assertNotNull(element.data, `Component's host node should have an LView attached.`);
|
||||
const hostView = element.data !;
|
||||
|
||||
// Only CheckAlways components or dirty OnPush components should be checked
|
||||
if (hostView.flags & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
|
||||
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
|
||||
if (viewAttached(hostView) && hostView.flags & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
|
||||
ngDevMode && assertDataInRange(directiveIndex);
|
||||
detectChangesInternal(hostView, element, getDirectiveInstance<T>(data[directiveIndex]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a boolean for whether the view is attached */
|
||||
function viewAttached(view: LView): boolean {
|
||||
return (view.flags & LViewFlags.Attached) === LViewFlags.Attached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruction to distribute projectable nodes among <ng-content> occurrences in a given template.
|
||||
* It takes all the selectors from the entire component's template and decides where
|
||||
|
@ -183,13 +183,16 @@ export const enum LViewFlags {
|
||||
* back into the parent view, `data` will be defined and `creationMode` will be
|
||||
* improperly reported as false.
|
||||
*/
|
||||
CreationMode = 0b001,
|
||||
CreationMode = 0b0001,
|
||||
|
||||
/** Whether this view has default change detection strategy (checks always) or onPush */
|
||||
CheckAlways = 0b010,
|
||||
CheckAlways = 0b0010,
|
||||
|
||||
/** Whether or not this view is currently dirty (needing check) */
|
||||
Dirty = 0b100
|
||||
Dirty = 0b0100,
|
||||
|
||||
/** Whether or not this view is currently attached to change detection tree. */
|
||||
Attached = 0b1000,
|
||||
}
|
||||
|
||||
/** Interface necessary to work with view tree traversal */
|
||||
|
@ -7,16 +7,18 @@
|
||||
*/
|
||||
|
||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef} from '../linker/view_ref';
|
||||
|
||||
import {detectChanges} from './instructions';
|
||||
import {ComponentTemplate} from './interfaces/definition';
|
||||
import {LViewNode} from './interfaces/node';
|
||||
import {LView, LViewFlags} from './interfaces/view';
|
||||
import {notImplemented} from './util';
|
||||
|
||||
export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T> {
|
||||
context: T;
|
||||
rootNodes: any[];
|
||||
|
||||
constructor(context: T|null) { this.context = context !; }
|
||||
constructor(private _view: LView, context: T|null, ) { this.context = context !; }
|
||||
|
||||
/** @internal */
|
||||
_setComponentContext(context: T) { this.context = context; }
|
||||
@ -25,12 +27,27 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T> {
|
||||
destroyed: boolean;
|
||||
onDestroy(callback: Function) { notImplemented(); }
|
||||
markForCheck(): void { notImplemented(); }
|
||||
detach(): void { notImplemented(); }
|
||||
|
||||
/**
|
||||
* Detaches a view from the change detection tree.
|
||||
*
|
||||
* Detached views will not be checked during change detection runs, even if the view
|
||||
* is dirty. This can be used in combination with detectChanges to implement local
|
||||
* change detection checks.
|
||||
*/
|
||||
detach(): void { this._view.flags &= ~LViewFlags.Attached; }
|
||||
|
||||
/**
|
||||
* Re-attaches a view to the change detection tree.
|
||||
*
|
||||
* This can be used to re-attach views that were previously detached from the tree
|
||||
* using detach(). Views are attached to the tree by default.
|
||||
*/
|
||||
reattach(): void { this._view.flags |= LViewFlags.Attached; }
|
||||
|
||||
detectChanges(): void { detectChanges(this.context); }
|
||||
|
||||
checkNoChanges(): void { notImplemented(); }
|
||||
reattach(): void { notImplemented(); }
|
||||
}
|
||||
|
||||
|
||||
@ -41,7 +58,7 @@ export class EmbeddedViewRef<T> extends ViewRef<T> {
|
||||
_lViewNode: LViewNode;
|
||||
|
||||
constructor(viewNode: LViewNode, template: ComponentTemplate<T>, context: T) {
|
||||
super(context);
|
||||
super(viewNode.data, context);
|
||||
this._lViewNode = viewNode;
|
||||
}
|
||||
}
|
||||
@ -52,9 +69,9 @@ export class EmbeddedViewRef<T> extends ViewRef<T> {
|
||||
* @param context The context for this view
|
||||
* @returns The ViewRef
|
||||
*/
|
||||
export function createViewRef<T>(context: T): ViewRef<T> {
|
||||
export function createViewRef<T>(view: LView, context: T): ViewRef<T> {
|
||||
// TODO: add detectChanges back in when implementing ChangeDetectorRef.detectChanges
|
||||
return addDestroyable(new ViewRef(context));
|
||||
return addDestroyable(new ViewRef(view, context));
|
||||
}
|
||||
|
||||
/** Interface for destroy logic. Implemented by addDestroyable. */
|
||||
|
Reference in New Issue
Block a user