diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 3f7ba268da..6c1a79c8af 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -23,7 +23,7 @@ import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/def import {LElementNode, TNode, TNodeType} from './interfaces/node'; import {RElement, domRendererFactory3} from './interfaces/renderer'; 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 { resolveComponentFactory(component: Type): viewEngine_ComponentFactory { @@ -190,16 +190,7 @@ export class ComponentRef extends viewEngine_ComponentRef { hostNode: RElement) { super(); this.instance = instance; - /* TODO(jasonaden): This is incomplete, to be adjusted in follow-up PR. Notes from Kara:When - * 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 = this.changeDetectorRef = new RootViewRef(rootView); this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView); this.injector = injector; this.location = new ElementRef(hostNode); diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 228929bc4d..d6686250b3 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -2263,6 +2263,15 @@ export function detectChanges(component: T): void { 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. @@ -2279,6 +2288,24 @@ export function checkNoChanges(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. */ export function detectChangesInternal( hostView: LViewData, hostNode: LElementNode, component: T) { diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index 1b23cf216c..a28614ec73 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -11,7 +11,7 @@ import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detec import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_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 {FLAGS, LViewData, LViewFlags} from './interfaces/view'; import {destroyLView} from './node_manipulation'; @@ -243,3 +243,12 @@ export class ViewRef implements viewEngine_EmbeddedViewRef, viewEngine_Int attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; } } + +/** @internal */ +export class RootViewRef extends ViewRef { + constructor(protected _view: LViewData) { super(_view, null); } + + detectChanges(): void { detectChangesInRootView(this._view); } + + checkNoChanges(): void { checkNoChangesInRootView(this._view); } +} diff --git a/packages/core/test/application_ref_integration_spec.ts b/packages/core/test/application_ref_integration_spec.ts index 31b57aaa24..5000f8c365 100644 --- a/packages/core/test/application_ref_integration_spec.ts +++ b/packages/core/test/application_ref_integration_spec.ts @@ -52,13 +52,12 @@ describe('ApplicationRef bootstrap', () => { const appRef = moduleRef.injector.get(ApplicationRef); const helloWorldComponent = appRef.components[0].instance as HelloWorldComponent; expect(document.body.innerHTML).toEqual('
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'; appRef.tick(); expect(document.body.innerHTML).toEqual('
Hello Mundo
'); - // TODO(jasonaden): Get with Kara on lifecycle hooks - // expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']); + expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']); // Cleanup TestabilityRegistry const registry: TestabilityRegistry = getTestBed().get(TestabilityRegistry);