diff --git a/modules/@angular/core/test/animation/animation_integration_spec.ts b/modules/@angular/core/test/animation/animation_integration_spec.ts index a83eed562e2517..955ba9258e091d 100644 --- a/modules/@angular/core/test/animation/animation_integration_spec.ts +++ b/modules/@angular/core/test/animation/animation_integration_spec.ts @@ -25,6 +25,7 @@ import {AnimationPlayer, NoOpAnimationPlayer} from '../../src/animation/animatio import {AnimationStyles} from '../../src/animation/animation_styles'; import {AnimationTransitionEvent} from '../../src/animation/animation_transition_event'; import {AUTO_STYLE, animate, group, keyframes, sequence, state, style, transition, trigger} from '../../src/animation/metadata'; +import {Input} from '../../src/core'; import {isPresent} from '../../src/facade/lang'; import {TestBed, fakeAsync, flushMicrotasks} from '../../testing'; import {MockAnimationPlayer} from '../../testing/mock_animation_player'; @@ -2243,38 +2244,52 @@ function declareTests({useJit}: {useJit: boolean}) { }); describe('error handling', () => { - it('should recover if an animation driver or player throws an error during an animation', + if (!getDOM().supportsWebAnimation()) return; + + it('should not throw an error when an animation exists within projected content that is not bound to the DOM', fakeAsync(() => { TestBed.configureTestingModule({ - declarations: [DummyIfCmp], - providers: [{provide: AnimationDriver, useClass: ErroneousAnimationDriver}], + declarations: [DummyIfCmp, DummyLoadingCmp], + providers: [{provide: AnimationDriver, useClass: WebAnimationsDriver}], imports: [CommonModule] }); TestBed.overrideComponent(DummyIfCmp, { set: { template: ` -
+ +
world
+
`, animations: [trigger('myAnimation', [transition( '* => *', [ - animate(1000, style({transform: 'noooooo'})), + style({opacity: 0}), + animate(1000, style({opacity: 1})), ])])] } }); + TestBed.overrideComponent( + DummyLoadingCmp, {set: {template: `hello `}}); const fixture = TestBed.createComponent(DummyIfCmp); const cmp = fixture.componentInstance; - let started = false; - let done = false; - cmp.callback1 = (event: AnimationTransitionEvent) => started = true; - cmp.callback2 = (event: AnimationTransitionEvent) => done = true; + const container = fixture.nativeElement; + let animationCalls = 0; + cmp.callback = () => animationCalls++; + + cmp.exp = false; + fixture.detectChanges(); + flushMicrotasks(); + + expect(animationCalls).toBe(1); + expect(getDOM().getText(container).trim()).toEqual('hello'); + cmp.exp = true; fixture.detectChanges(); flushMicrotasks(); - expect(started).toBe(true); - expect(done).toBe(true); + expect(animationCalls).toBe(2); + expect(getDOM().getText(container).trim()).toMatch(/hello[\s\r\n]+world/m); })); }); @@ -2460,6 +2475,9 @@ class DummyIfCmp { class DummyLoadingCmp { exp: any = false; callback = () => {}; + + @Input('exp2') + exp2: any = false; } @Component({ @@ -2571,11 +2589,3 @@ class ExtendedWebAnimationsDriver extends WebAnimationsDriver { return player; } } - -class ErroneousAnimationDriver extends MockAnimationDriver { - animate( - element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], - duration: number, delay: number, easing: string): WebAnimationsPlayer { - throw new Error(); - } -} diff --git a/modules/@angular/platform-browser/src/dom/dom_renderer.ts b/modules/@angular/platform-browser/src/dom/dom_renderer.ts index 608d6a0e3002fe..68de6d4d9d951b 100644 --- a/modules/@angular/platform-browser/src/dom/dom_renderer.ts +++ b/modules/@angular/platform-browser/src/dom/dom_renderer.ts @@ -262,12 +262,11 @@ export class DomRenderer implements Renderer { element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number, easing: string, previousPlayers: AnimationPlayer[] = []): AnimationPlayer { - try { + if (this._rootRenderer.document.body.contains(element)) { return this._animationDriver.animate( element, startingStyles, keyframes, duration, delay, easing, previousPlayers); - } catch (e) { - return new NoOpAnimationPlayer(); } + return new NoOpAnimationPlayer(); } } diff --git a/modules/@angular/platform-server/src/private_import_core.ts b/modules/@angular/platform-server/src/private_import_core.ts index 772db2a59cb206..465952850e4983 100644 --- a/modules/@angular/platform-server/src/private_import_core.ts +++ b/modules/@angular/platform-server/src/private_import_core.ts @@ -21,5 +21,3 @@ export type RenderDebugInfo = typeof r._RenderDebugInfo; export const RenderDebugInfo: typeof r.RenderDebugInfo = r.RenderDebugInfo; export type DebugDomRootRenderer = typeof r._DebugDomRootRenderer; export const DebugDomRootRenderer: typeof r.DebugDomRootRenderer = r.DebugDomRootRenderer; -export type NoOpAnimationPlayer = typeof r._NoOpAnimationPlayer; -export const NoOpAnimationPlayer: typeof r.NoOpAnimationPlayer = r.NoOpAnimationPlayer; diff --git a/modules/@angular/platform-server/src/server_renderer.ts b/modules/@angular/platform-server/src/server_renderer.ts index e59b9765081d08..5a09ff65c43ce0 100644 --- a/modules/@angular/platform-server/src/server_renderer.ts +++ b/modules/@angular/platform-server/src/server_renderer.ts @@ -10,7 +10,7 @@ import {APP_ID, Inject, Injectable, NgZone, RenderComponentType, Renderer, RootR import {AnimationDriver, DOCUMENT} from '@angular/platform-browser'; import {isBlank, isPresent, stringify} from './facade/lang'; -import {AnimationKeyframe, AnimationPlayer, AnimationStyles, NoOpAnimationPlayer, RenderDebugInfo} from './private_import_core'; +import {AnimationKeyframe, AnimationPlayer, AnimationStyles, RenderDebugInfo} from './private_import_core'; import {NAMESPACE_URIS, SharedStylesHost, flattenStyles, getDOM, isNamespaced, shimContentAttribute, shimHostAttribute, splitNamespace} from './private_import_platform-browser'; const TEMPLATE_COMMENT_TEXT = 'template bindings={}'; @@ -208,12 +208,8 @@ export class ServerRenderer implements Renderer { element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number, easing: string, previousPlayers: AnimationPlayer[] = []): AnimationPlayer { - try { - return this._animationDriver.animate( - element, startingStyles, keyframes, duration, delay, easing, previousPlayers); - } catch (e) { - return new NoOpAnimationPlayer(); - } + return this._animationDriver.animate( + element, startingStyles, keyframes, duration, delay, easing, previousPlayers); } } diff --git a/modules/@angular/platform-webworker/test/web_workers/worker/renderer_animation_integration_spec.ts b/modules/@angular/platform-webworker/test/web_workers/worker/renderer_animation_integration_spec.ts index 418c4977136724..f0507b15e125df 100644 --- a/modules/@angular/platform-webworker/test/web_workers/worker/renderer_animation_integration_spec.ts +++ b/modules/@angular/platform-webworker/test/web_workers/worker/renderer_animation_integration_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AUTO_STYLE, AnimationTransitionEvent, Component, Injector, animate, state, style, transition, trigger} from '@angular/core'; +import {AUTO_STYLE, AnimationTransitionEvent, Component, Injector, ViewChild, animate, state, style, transition, trigger} from '@angular/core'; import {DebugDomRootRenderer} from '@angular/core/src/debug/debug_renderer'; import {RootRenderer} from '@angular/core/src/render/api'; import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing'; @@ -84,7 +84,7 @@ export function main() { workerRenderStore = new RenderStore(); TestBed.configureTestingModule({ - declarations: [AnimationCmp, MultiAnimationCmp], + declarations: [AnimationCmp, MultiAnimationCmp, ContainerAnimationCmp], providers: [ Serializer, {provide: RenderStore, useValue: workerRenderStore}, { provide: RootRenderer, @@ -231,10 +231,9 @@ export function main() { return (event: AnimationTransitionEvent) => { log[phaseName] = event; }; } - const f1 = TestBed.createComponent(AnimationCmp); - const f2 = TestBed.createComponent(AnimationCmp); - const cmp1 = f1.componentInstance; - const cmp2 = f2.componentInstance; + const fixture = TestBed.createComponent(ContainerAnimationCmp); + const cmp1 = fixture.componentInstance.compOne; + const cmp2 = fixture.componentInstance.compTwo; const cmp1Log: {[phaseName: string]: AnimationTransitionEvent} = {}; const cmp2Log: {[phaseName: string]: AnimationTransitionEvent} = {}; @@ -246,8 +245,7 @@ export function main() { cmp1.state = 'off'; cmp2.state = 'on'; - f1.detectChanges(); - f2.detectChanges(); + fixture.detectChanges(); flushMicrotasks(); uiDriver.log.shift()['player'].finish(); @@ -316,6 +314,18 @@ export function main() { }); } +@Component({ + selector: 'container-comp', + template: ` + + + ` +}) +class ContainerAnimationCmp { + @ViewChild('one') public compOne: AnimationCmp; + + @ViewChild('two') public compTwo: AnimationCmp; +} @Component({ selector: 'my-comp',