diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index f0b7be0af2..4b6553986b 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -96,14 +96,21 @@ export const CIRCULAR = '__CIRCULAR__'; * Renderer2. */ let renderer: Renderer3; -let rendererFactory: RendererFactory3; -let currentElementNode: LElementNode|null = null; export function getRenderer(): Renderer3 { // top level variables should not be exported for performance reasons (PERF_NOTES.md) return renderer; } +let rendererFactory: RendererFactory3; + +export function getRendererFactory(): RendererFactory3 { + // top level variables should not be exported for performance reasons (PERF_NOTES.md) + return rendererFactory; +} + +let currentElementNode: LElementNode|null = null; + export function getCurrentSanitizer(): Sanitizer|null { return viewData && viewData[SANITIZER]; } diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index a28614ec73..8b57d6a546 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -11,11 +11,12 @@ 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, checkNoChangesInRootView, detectChanges, detectChangesInRootView, markViewDirty, storeCleanupFn, viewAttached} from './instructions'; +import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, getRendererFactory, markViewDirty, storeCleanupFn, viewAttached} from './instructions'; import {LViewNode} from './interfaces/node'; import {FLAGS, LViewData, LViewFlags} from './interfaces/view'; import {destroyLView} from './node_manipulation'; + // Needed due to tsickle downleveling where multiple `implements` with classes creates // multiple @extends in Closure annotations, which is illegal. This workaround fixes // the multiple @extends by making the annotation @implements instead @@ -227,7 +228,16 @@ export class ViewRef implements viewEngine_EmbeddedViewRef, viewEngine_Int * * See {@link ChangeDetectorRef#detach detach} for more information. */ - detectChanges(): void { detectChanges(this.context); } + detectChanges(): void { + const rendererFactory = getRendererFactory(); + if (rendererFactory.begin) { + rendererFactory.begin(); + } + detectChanges(this.context); + if (rendererFactory.end) { + rendererFactory.end(); + } + } /** * Checks the change detector and its children, and throws if any changes are detected. diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 70f606c52a..6a5eec93f7 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -596,6 +596,9 @@ { "name": "getRenderer" }, + { + "name": "getRendererFactory" + }, { "name": "getRootView" }, diff --git a/packages/core/test/render3/change_detection_spec.ts b/packages/core/test/render3/change_detection_spec.ts index 1a9f2b3d1c..bde9dd461b 100644 --- a/packages/core/test/render3/change_detection_spec.ts +++ b/packages/core/test/render3/change_detection_spec.ts @@ -8,11 +8,12 @@ import {withBody} from '@angular/private/testing'; -import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck} from '../../src/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core'; import {getRenderedText, whenRendered} from '../../src/render3/component'; import {LifecycleHooksFeature, defineComponent, defineDirective, injectChangeDetectorRef} from '../../src/render3/index'; import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, text, textBinding, tick} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; +import {RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer'; import {containerEl, createComponent, renderComponent, requestAnimationFrame} from './render_util'; @@ -1043,4 +1044,40 @@ describe('change detection', () => { }); + it('should call begin and end when the renderer factory implements them', () => { + const log: string[] = []; + + const testRendererFactory: RendererFactory3 = { + createRenderer: (hostElement: RElement | null, rendererType: RendererType2 | null): + Renderer3 => { return document; }, + begin: () => log.push('begin'), + end: () => log.push('end'), + }; + + class MyComponent { + get value(): string { + log.push('detect changes'); + return 'works'; + } + + static ngComponentDef = defineComponent({ + type: MyComponent, + selectors: [['my-comp']], + factory: () => new MyComponent(), + template: (rf: RenderFlags, ctx: MyComponent) => { + if (rf & RenderFlags.Create) { + text(0); + } + if (rf & RenderFlags.Update) { + textBinding(0, bind(ctx.value)); + } + } + }); + } + + const myComp = renderComponent(MyComponent, {rendererFactory: testRendererFactory}); + expect(getRenderedText(myComp)).toEqual('works'); + expect(log).toEqual(['begin', 'detect changes', 'end']); + }); + });