Skip to content

Commit

Permalink
fix(animations): prevent the AsyncAnimationRenderer from calling the …
Browse files Browse the repository at this point in the history
…delegate when there is no element. (#52570)

This happens when `issueAnimationCommand` is invoked

fixes #52538

PR Close #52570
  • Loading branch information
JeanMeche authored and jessicajaniuk committed Nov 13, 2023
1 parent 7c066a4 commit f5872c9
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 12 deletions.
3 changes: 3 additions & 0 deletions packages/animations/test/BUILD.bazel
Expand Up @@ -18,10 +18,13 @@ ts_library(
"//packages/animations",
"//packages/animations/browser",
"//packages/animations/browser/testing",
"//packages/common",
"//packages/core",
"//packages/core/testing",
"//packages/platform-browser",
"//packages/platform-browser-dynamic/testing",
"//packages/platform-browser/animations",
"//packages/platform-browser/animations/async",
"//packages/platform-browser/testing",
],
)
Expand Down
49 changes: 47 additions & 2 deletions packages/animations/test/browser_animation_builder_spec.ts
Expand Up @@ -8,11 +8,13 @@
import {animate, AnimationBuilder, style, ɵBrowserAnimationBuilder as BrowserAnimationBuilder} from '@angular/animations';
import {AnimationDriver} from '@angular/animations/browser';
import {MockAnimationDriver} from '@angular/animations/browser/testing';
import {Component, ViewChild} from '@angular/core';
import {DOCUMENT} from '@angular/common';
import {Component, NgZone, RendererFactory2, ViewChild} from '@angular/core';
import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing';
import {ɵDomRendererFactory2 as DomRendererFactory2} from '@angular/platform-browser';
import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';

import {ɵAsyncAnimationRendererFactory as AsyncAnimationRendererFactory} from '@angular/platform-browser/animations/async';

describe('BrowserAnimationBuilder', () => {
if (isNode) {
Expand Down Expand Up @@ -236,4 +238,47 @@ describe('BrowserAnimationBuilder', () => {
[BrowserDynamicTestingModule, NoopAnimationsModule], platformBrowserDynamicTesting());
});
});

describe('with Animations async', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: RendererFactory2,
useFactory: (doc: Document, renderer: DomRendererFactory2, zone: NgZone) => {
// Using a empty promise to prevent switching to the delegate to AnimationRenderer
return new AsyncAnimationRendererFactory(
doc, renderer, zone, 'noop', new Promise<any>(() => {}));
},
deps: [DOCUMENT, DomRendererFactory2, NgZone],
},
]
});
});

it('should be able to build', () => {
@Component({
selector: 'ani-cmp',
template: '...',
})
class Cmp {
@ViewChild('target') public target: any;

constructor(public builder: AnimationBuilder) {}

build() {
const definition = this.builder.build([style({'transform': `rotate(0deg)`})]);

return definition.create(this.target);
}
}

TestBed.configureTestingModule({declarations: [Cmp]});

const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
fixture.detectChanges();
cmp.build();
});
});
});
Expand Up @@ -12,3 +12,4 @@
* Entry point for all animation APIs of the animation browser package.
*/
export {provideAnimationsAsync} from './providers';
export * from './private_export';
Expand Up @@ -6,15 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/

import type {ɵAnimationRendererFactory as AnimationRendererFactory, ɵAnimationRenderer as AnimationRenderer, ɵBaseAnimationRenderer as BaseAnimationRenderer} from '@angular/animations/browser';
import {NgZone, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2, ɵRuntimeError as RuntimeError, ɵAnimationRendererType as AnimationRendererType} from '@angular/core';
import {ɵAnimationEngine as AnimationEngine, ɵAnimationRenderer as AnimationRenderer, ɵAnimationRendererFactory as AnimationRendererFactory} from '@angular/animations/browser';
import {NgZone, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2, ɵAnimationRendererType as AnimationRendererType, ɵRuntimeError as RuntimeError} from '@angular/core';
import {ɵRuntimeErrorCode as RuntimeErrorCode} from '@angular/platform-browser';
/**
* This alias narrows down to only the properties we need when lazy loading (or mock) the module
*/
type AnimationBrowserModuleImports =
Pick<typeof import('@angular/animations/browser'), 'ɵcreateEngine'|'ɵAnimationRendererFactory'>;


const ANIMATION_PREFIX = '@';

Expand All @@ -27,8 +21,10 @@ export class AsyncAnimationRendererFactory implements RendererFactory2 {
*/
constructor(
private doc: Document, private delegate: RendererFactory2, private zone: NgZone,
private animationType: 'animations'|'noop',
private moduleImpl?: Promise<AnimationBrowserModuleImports>) {}
private animationType: 'animations'|'noop', private moduleImpl?: Promise<{
ɵcreateEngine: (type: 'animations'|'noop', doc: Document) => AnimationEngine,
ɵAnimationRendererFactory: typeof AnimationRendererFactory
}>) {}

/**
* @internal
Expand Down
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

export {AsyncAnimationRendererFactory as ɵAsyncAnimationRendererFactory} from './async_animation_renderer';
4 changes: 4 additions & 0 deletions packages/platform-browser/src/dom/dom_renderer.ts
Expand Up @@ -279,6 +279,10 @@ class DefaultDomRenderer2 implements Renderer2 {
}

setProperty(el: any, name: string, value: any): void {
if (el == null) {
return;
}

(typeof ngDevMode === 'undefined' || ngDevMode) && this.throwOnSyntheticProps &&
checkNoSyntheticProp(name, 'property');
el[name] = value;
Expand Down

0 comments on commit f5872c9

Please sign in to comment.