fix(ivy): ensure renderer begin/end methods are only called during change detection (#28192)

In VE the renderer.begin() and renderer.end() methods are only called
when CD is called on an element. This patch ensures that Ivy does the
same thing.

Jira issue: FW-945

PR Close #28192
This commit is contained in:
Matias Niemelä 2019-01-16 12:22:10 -08:00 committed by Alex Rickabaugh
parent 1f7d3b9a57
commit 896cf35afb
4 changed files with 45 additions and 49 deletions

View File

@ -169,8 +169,6 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
let component: T; let component: T;
let tElementNode: TElementNode; let tElementNode: TElementNode;
try { try {
if (rendererFactory.begin) rendererFactory.begin();
const componentView = createRootComponentView( const componentView = createRootComponentView(
hostRNode, this.componentDef, rootLView, rendererFactory, renderer); hostRNode, this.componentDef, rootLView, rendererFactory, renderer);
@ -216,7 +214,6 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
refreshDescendantViews(rootLView); refreshDescendantViews(rootLView);
} finally { } finally {
leaveView(oldLView); leaveView(oldLView);
if (rendererFactory.end) rendererFactory.end();
} }
const componentRef = new ComponentRef( const componentRef = new ComponentRef(

View File

@ -395,12 +395,13 @@ function renderComponentOrTemplate<T>(
const rendererFactory = hostView[RENDERER_FACTORY]; const rendererFactory = hostView[RENDERER_FACTORY];
const oldView = enterView(hostView, hostView[HOST_NODE]); const oldView = enterView(hostView, hostView[HOST_NODE]);
const normalExecutionPath = !getCheckNoChangesMode(); const normalExecutionPath = !getCheckNoChangesMode();
const creationModeIsActive = isCreationMode(hostView);
try { try {
if (normalExecutionPath && rendererFactory.begin) { if (normalExecutionPath && !creationModeIsActive && rendererFactory.begin) {
rendererFactory.begin(); rendererFactory.begin();
} }
if (isCreationMode(hostView)) { if (creationModeIsActive) {
// creation mode pass // creation mode pass
if (templateFn) { if (templateFn) {
namespaceHTML(); namespaceHTML();
@ -415,7 +416,7 @@ function renderComponentOrTemplate<T>(
templateFn && templateFn(RenderFlags.Update, context !); templateFn && templateFn(RenderFlags.Update, context !);
refreshDescendantViews(hostView); refreshDescendantViews(hostView);
} finally { } finally {
if (normalExecutionPath && rendererFactory.end) { if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) {
rendererFactory.end(); rendererFactory.end();
} }
leaveView(oldView); leaveView(oldView);

View File

@ -803,58 +803,56 @@ import {HostListener} from '../../src/metadata/directives';
expect(player.element.style.height).toEqual('444px'); expect(player.element.style.height).toEqual('444px');
}); });
fixmeIvy( it('should find newly inserted items in the component via :enter', () => {
'FW-945 - Ivy createComponent calls CD while VE waits for CD to be explicitly called') @Component({
.it('should find newly inserted items in the component via :enter', () => { selector: 'ani-cmp',
@Component({ template: `
selector: 'ani-cmp',
template: `
<div @myAnimation> <div @myAnimation>
<div *ngFor="let item of items" class="child"> <div *ngFor="let item of items" class="child">
{{ item }} {{ item }}
</div> </div>
</div> </div>
`, `,
animations: [trigger( animations: [trigger(
'myAnimation', 'myAnimation',
[ [
transition( transition(
':enter', ':enter',
[ [
query( query(
':enter', ':enter',
[ [
style({opacity: 0}), style({opacity: 0}),
animate(1000, style({opacity: .5})), animate(1000, style({opacity: .5})),
]), ]),
]), ]),
])] ])]
}) })
class Cmp { class Cmp {
public items: any[] = [0, 1, 2]; public items: any[] = [0, 1, 2];
} }
TestBed.configureTestingModule({declarations: [Cmp]}); TestBed.configureTestingModule({declarations: [Cmp]});
const engine = TestBed.get(ɵAnimationEngine); const engine = TestBed.get(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp); const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance; const cmp = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
engine.flush(); engine.flush();
const players = getLog(); const players = getLog();
expect(players.length).toEqual(3); expect(players.length).toEqual(3);
const [p1, p2, p3] = players; const [p1, p2, p3] = players;
expect(p1.element.innerText.trim()).toEqual('0'); expect(p1.element.innerText.trim()).toEqual('0');
expect(p2.element.innerText.trim()).toEqual('1'); expect(p2.element.innerText.trim()).toEqual('1');
expect(p3.element.innerText.trim()).toEqual('2'); expect(p3.element.innerText.trim()).toEqual('2');
players.forEach(p => { players.forEach(p => {
expect(p.keyframes).toEqual([{opacity: '0', offset: 0}, {opacity: '0.5', offset: 1}]); expect(p.keyframes).toEqual([{opacity: '0', offset: 0}, {opacity: '0.5', offset: 1}]);
}); });
}); });
it('should cleanup :enter and :leave artifacts from nodes when any animation sequences fail to be built', it('should cleanup :enter and :leave artifacts from nodes when any animation sequences fail to be built',
() => { () => {

View File

@ -105,7 +105,7 @@ describe('renderer factory lifecycle', () => {
it('should work with a template', () => { it('should work with a template', () => {
renderToHtml(Template, {}, 1, 0, null, null, rendererFactory); renderToHtml(Template, {}, 1, 0, null, null, rendererFactory);
expect(logs).toEqual(['create', 'begin', 'function create', 'function update', 'end']); expect(logs).toEqual(['create', 'function create', 'function update']);
logs = []; logs = [];
renderToHtml(Template, {}); renderToHtml(Template, {});
@ -115,8 +115,8 @@ describe('renderer factory lifecycle', () => {
it('should work with a template which contains a component', () => { it('should work with a template which contains a component', () => {
renderToHtml(TemplateWithComponent, {}, 2, 0, directives, null, rendererFactory); renderToHtml(TemplateWithComponent, {}, 2, 0, directives, null, rendererFactory);
expect(logs).toEqual([ expect(logs).toEqual([
'create', 'begin', 'function_with_component create', 'create', 'component create', 'create', 'function_with_component create', 'create', 'component create',
'function_with_component update', 'component update', 'end' 'function_with_component update', 'component update'
]); ]);
logs = []; logs = [];