fix(ivy): execute the optional begin and end methods of the rendererFactory (#25273)

This is required to i.e. flush animations when using a Renderer2.
`rf.begin()` and `rf.end()` around the change detection.

PR Close #25273
This commit is contained in:
Victor Berchet 2018-08-02 16:53:34 -07:00 committed by Kara Erickson
parent 0822dc70f2
commit afa6b9e794
4 changed files with 62 additions and 5 deletions

View File

@ -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];
}

View File

@ -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<T> implements viewEngine_EmbeddedViewRef<T>, 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.

View File

@ -596,6 +596,9 @@
{
"name": "getRenderer"
},
{
"name": "getRendererFactory"
},
{
"name": "getRootView"
},

View File

@ -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']);
});
});