Skip to content

Commit

Permalink
refactor(animations): Ensure async animations applies changes when lo…
Browse files Browse the repository at this point in the history
…aded in zoneless

Async animations currently works in Zones because the render factory
promise resolve causes change detection to happen.

fixes angular#54919
  • Loading branch information
atscott committed Apr 22, 2024
1 parent b9503c5 commit d215fb0
Show file tree
Hide file tree
Showing 7 changed files with 10 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ import {
normalizeKeyframes,
optimizeGroupPlayer,
} from './shared';
import {NotificationType} from '@angular/core/src/change_detection/scheduling/zoneless_scheduling';

const QUEUED_CLASSNAME = 'ng-animate-queued';
const QUEUED_SELECTOR = '.ng-animate-queued';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ const DEFAULT_NAMESPACE_ID = 'id';
getBodyNode(),
driver,
normalizer || new NoopAnimationStyleNormalizer(),
null,
);
engine.createNamespace(DEFAULT_NAMESPACE_ID, element);
return engine;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/core_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export {detectChangesInViewIfRequired as ɵdetectChangesInViewIfRequired, whenSt
export {IMAGE_CONFIG as ɵIMAGE_CONFIG, IMAGE_CONFIG_DEFAULTS as ɵIMAGE_CONFIG_DEFAULTS, ImageConfig as ɵImageConfig} from './application/application_tokens';
export {internalCreateApplication as ɵinternalCreateApplication} from './application/create_application';
export {defaultIterableDiffers as ɵdefaultIterableDiffers, defaultKeyValueDiffers as ɵdefaultKeyValueDiffers} from './change_detection/change_detection';
export {ChangeDetectionScheduler as ɵChangeDetectionScheduler, ZONELESS_ENABLED as ɵZONELESS_ENABLED} from './change_detection/scheduling/zoneless_scheduling';
export {ChangeDetectionScheduler as ɵChangeDetectionScheduler, NotificationType as ɵNotificationType, ZONELESS_ENABLED as ɵZONELESS_ENABLED} from './change_detection/scheduling/zoneless_scheduling';
export {Console as ɵConsole} from './console';
export {DeferBlockDetails as ɵDeferBlockDetails, getDeferBlocks as ɵgetDeferBlocks} from './defer/discovery';
export {renderDeferBlockState as ɵrenderDeferBlockState, triggerResourceLoading as ɵtriggerResourceLoading} from './defer/instructions';
Expand Down
6 changes: 3 additions & 3 deletions packages/core/test/acceptance/renderer_factory_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,20 @@ describe('renderer factory lifecycle', () => {

it('should work with a component', () => {
const fixture = TestBed.createComponent(SomeComponent);
fixture.componentRef.changeDetectorRef.detectChanges();
fixture.changeDetectorRef.detectChanges();
expect(logs).toEqual([
'create', 'create', 'begin', 'end', 'begin', 'some_component create', 'some_component update',
'end'
]);
logs = [];
fixture.componentRef.changeDetectorRef.detectChanges();
fixture.changeDetectorRef.detectChanges();
expect(logs).toEqual(['begin', 'some_component update', 'end']);
});

it('should work with a component which throws', () => {
expect(() => {
const fixture = TestBed.createComponent(SomeComponentWhichThrows);
fixture.componentRef.changeDetectorRef.detectChanges();
fixture.changeDetectorRef.detectChanges();
}).toThrow();
expect(logs).toEqual(['create', 'create', 'begin', 'end', 'begin', 'end']);
});
Expand Down
6 changes: 2 additions & 4 deletions packages/core/test/render3/change_detection_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,18 @@ describe('change detection', () => {
}
}

const fixture = TestBed.createComponent(MyComponent);
const rendererFactory = TestBed.inject(RendererFactory2);
rendererFactory.begin = () => log.push('begin');
rendererFactory.end = () => log.push('end');

const fixture = TestBed.createComponent(MyComponent);
fixture.detectChanges();
fixture.changeDetectorRef.detectChanges();

expect(fixture.nativeElement.innerHTML).toEqual('works');

expect(log).toEqual([
'begin',
'detect changes', // regular change detection cycle
'end',
'detect changes' // check no changes cycle
]);
}));
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
RendererType2,
ɵAnimationRendererType as AnimationRendererType,
ɵChangeDetectionScheduler as ChangeDetectionScheduler,
ɵNotificationType as NotificationType,
ɵRuntimeError as RuntimeError,
} from '@angular/core';
import {ɵRuntimeErrorCode as RuntimeErrorCode} from '@angular/platform-browser';
Expand Down Expand Up @@ -127,6 +128,8 @@ export class AsyncAnimationRendererFactory implements OnDestroy, RendererFactory
rendererType,
);
dynamicRenderer.use(animationRenderer);
// Applying animations might result in new DOM state and should rerun render hooks
this.scheduler?.notify(NotificationType.AfterRenderHooks);
})
.catch((e) => {
// Permanently use regular renderer when loading fails.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,7 @@ type AnimationBrowserModule = typeof import('@angular/animations/browser');
engine: MockAnimationEngine,
) => {
const animationModule = {
ɵcreateEngine: (
_: 'animations' | 'noop',
_2: Document,
_3: ChangeDetectionScheduler | null,
): AnimationEngine => engine,
ɵcreateEngine: (_: 'animations' | 'noop', _2: Document): AnimationEngine => engine,
ɵAnimationEngine: MockAnimationEngine as any,
ɵAnimationRenderer: AnimationRenderer,
ɵBaseAnimationRenderer: BaseAnimationRenderer,
Expand Down

0 comments on commit d215fb0

Please sign in to comment.