feat(ivy): support change detection on the root view (#25085)
PR Close #25085
This commit is contained in:
parent
36648293a8
commit
e3834b7001
|
@ -23,7 +23,7 @@ import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/def
|
||||||
import {LElementNode, TNode, TNodeType} from './interfaces/node';
|
import {LElementNode, TNode, TNodeType} from './interfaces/node';
|
||||||
import {RElement, domRendererFactory3} from './interfaces/renderer';
|
import {RElement, domRendererFactory3} from './interfaces/renderer';
|
||||||
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
|
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
|
||||||
import {ViewRef} from './view_ref';
|
import {RootViewRef, ViewRef} from './view_ref';
|
||||||
|
|
||||||
export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver {
|
export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver {
|
||||||
resolveComponentFactory<T>(component: Type<T>): viewEngine_ComponentFactory<T> {
|
resolveComponentFactory<T>(component: Type<T>): viewEngine_ComponentFactory<T> {
|
||||||
|
@ -190,16 +190,7 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
|
||||||
hostNode: RElement) {
|
hostNode: RElement) {
|
||||||
super();
|
super();
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
/* TODO(jasonaden): This is incomplete, to be adjusted in follow-up PR. Notes from Kara:When
|
this.hostView = this.changeDetectorRef = new RootViewRef<T>(rootView);
|
||||||
* ViewRef.detectChanges is called from ApplicationRef.tick, it will call detectChanges at the
|
|
||||||
* component instance level. I suspect this means that lifecycle hooks and host bindings on the
|
|
||||||
* given component won't work (as these are always called at the level above a component).
|
|
||||||
*
|
|
||||||
* In render2, ViewRef.detectChanges uses the root view instance for view checks, not the
|
|
||||||
* component instance. So passing in the root view (1 level above the component) is sufficient.
|
|
||||||
* We might want to think about creating a fake component for the top level? Or overwrite
|
|
||||||
* detectChanges with a function that calls tickRootContext? */
|
|
||||||
this.hostView = this.changeDetectorRef = new ViewRef(rootView, instance);
|
|
||||||
this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView);
|
this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView);
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.location = new ElementRef(hostNode);
|
this.location = new ElementRef(hostNode);
|
||||||
|
|
|
@ -2263,6 +2263,15 @@ export function detectChanges<T>(component: T): void {
|
||||||
detectChangesInternal(hostNode.data as LViewData, hostNode, component);
|
detectChangesInternal(hostNode.data as LViewData, hostNode, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronously perform change detection on a root view and its components.
|
||||||
|
*
|
||||||
|
* @param lViewData The view which the change detection should be performed on.
|
||||||
|
*/
|
||||||
|
export function detectChangesInRootView(lViewData: LViewData): void {
|
||||||
|
tickRootContext(lViewData[CONTEXT] as RootContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the change detector and its children, and throws if any changes are detected.
|
* Checks the change detector and its children, and throws if any changes are detected.
|
||||||
|
@ -2279,6 +2288,24 @@ export function checkNoChanges<T>(component: T): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the change detector on a root view and its components, and throws if any changes are
|
||||||
|
* detected.
|
||||||
|
*
|
||||||
|
* This is used in development mode to verify that running change detection doesn't
|
||||||
|
* introduce other changes.
|
||||||
|
*
|
||||||
|
* @param lViewData The view which the change detection should be checked on.
|
||||||
|
*/
|
||||||
|
export function checkNoChangesInRootView(lViewData: LViewData): void {
|
||||||
|
checkNoChangesMode = true;
|
||||||
|
try {
|
||||||
|
detectChangesInRootView(lViewData);
|
||||||
|
} finally {
|
||||||
|
checkNoChangesMode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
|
/** Checks the view of the component provided. Does not gate on dirty checks or execute doCheck. */
|
||||||
export function detectChangesInternal<T>(
|
export function detectChangesInternal<T>(
|
||||||
hostView: LViewData, hostNode: LElementNode, component: T) {
|
hostView: LViewData, hostNode: LElementNode, component: T) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detec
|
||||||
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 {checkNoChanges, detectChanges, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
||||||
import {LViewNode} from './interfaces/node';
|
import {LViewNode} from './interfaces/node';
|
||||||
import {FLAGS, LViewData, LViewFlags} from './interfaces/view';
|
import {FLAGS, LViewData, LViewFlags} from './interfaces/view';
|
||||||
import {destroyLView} from './node_manipulation';
|
import {destroyLView} from './node_manipulation';
|
||||||
|
@ -243,3 +243,12 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
||||||
|
|
||||||
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
|
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export class RootViewRef<T> extends ViewRef<T> {
|
||||||
|
constructor(protected _view: LViewData) { super(_view, null); }
|
||||||
|
|
||||||
|
detectChanges(): void { detectChangesInRootView(this._view); }
|
||||||
|
|
||||||
|
checkNoChanges(): void { checkNoChangesInRootView(this._view); }
|
||||||
|
}
|
||||||
|
|
|
@ -52,13 +52,12 @@ describe('ApplicationRef bootstrap', () => {
|
||||||
const appRef = moduleRef.injector.get(ApplicationRef);
|
const appRef = moduleRef.injector.get(ApplicationRef);
|
||||||
const helloWorldComponent = appRef.components[0].instance as HelloWorldComponent;
|
const helloWorldComponent = appRef.components[0].instance as HelloWorldComponent;
|
||||||
expect(document.body.innerHTML).toEqual('<hello-world><div>Hello World</div></hello-world>');
|
expect(document.body.innerHTML).toEqual('<hello-world><div>Hello World</div></hello-world>');
|
||||||
// TODO(jasonaden): Get with Kara on lifecycle hooks
|
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck']);
|
||||||
// expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck']);
|
|
||||||
helloWorldComponent.name = 'Mundo';
|
helloWorldComponent.name = 'Mundo';
|
||||||
appRef.tick();
|
appRef.tick();
|
||||||
expect(document.body.innerHTML).toEqual('<hello-world><div>Hello Mundo</div></hello-world>');
|
expect(document.body.innerHTML).toEqual('<hello-world><div>Hello Mundo</div></hello-world>');
|
||||||
// TODO(jasonaden): Get with Kara on lifecycle hooks
|
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']);
|
||||||
// expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']);
|
|
||||||
|
|
||||||
// Cleanup TestabilityRegistry
|
// Cleanup TestabilityRegistry
|
||||||
const registry: TestabilityRegistry = getTestBed().get(TestabilityRegistry);
|
const registry: TestabilityRegistry = getTestBed().get(TestabilityRegistry);
|
||||||
|
|
Loading…
Reference in New Issue