Skip to content

Commit 8c5db3c

Browse files
crisbetojosephperrott
authored andcommitted
fix(platform-browser): avoid circular DI error in async renderer (angular#59256)
In angular/components#30179 the CDK overlay started depending on the `Renderer2Factory`. Since the overlay is used in the `MatSnackbar` which is commonly used in error handlers, `Overlay` can end up being injected as a part of the app initialization. Because `AsyncAnimationRendererFactory` depends on the `ChangeDetectionScheduler`, it may cause a circular dependency. These changes inject the `ChangeDetectionScheduler` lazily to avoid the error. Note: this will also be resolved by angular#58984, but I decided to send it out, because: 1. angular#58984 seems to be stuck on some internal cleanup. 2. The `AsyncAnimationRendererFactory` doesn't need the `scheduler` eagerly anyway so the change is fairly safe. Fixes angular#59255. PR Close angular#59256
1 parent 2c3630a commit 8c5db3c

File tree

2 files changed

+25
-1
lines changed

2 files changed

+25
-1
lines changed

packages/platform-browser/animations/async/src/async_animation_renderer.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
ɵRuntimeError as RuntimeError,
2727
InjectionToken,
2828
type ListenerOptions,
29+
Injector,
2930
} from '@angular/core';
3031
import {ɵRuntimeErrorCode as RuntimeErrorCode} from '@angular/platform-browser';
3132

@@ -34,7 +35,8 @@ const ANIMATION_PREFIX = '@';
3435
@Injectable()
3536
export class AsyncAnimationRendererFactory implements OnDestroy, RendererFactory2 {
3637
private _rendererFactoryPromise: Promise<AnimationRendererFactory> | null = null;
37-
private readonly scheduler = inject(ChangeDetectionScheduler, {optional: true});
38+
private scheduler: ChangeDetectionScheduler | null = null;
39+
private readonly injector = inject(Injector);
3840
private readonly loadingSchedulerFn = inject(ɵASYNC_ANIMATION_LOADING_SCHEDULER_FN, {
3941
optional: true,
4042
});
@@ -143,6 +145,7 @@ export class AsyncAnimationRendererFactory implements OnDestroy, RendererFactory
143145
rendererType,
144146
);
145147
dynamicRenderer.use(animationRenderer);
148+
this.scheduler ??= this.injector.get(ChangeDetectionScheduler, null, {optional: true});
146149
this.scheduler?.notify(NotificationSource.AsyncAnimationsLoaded);
147150
})
148151
.catch((e) => {

packages/platform-browser/animations/async/test/animation_renderer_spec.ts

+21
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
afterNextRender,
2525
ANIMATION_MODULE_TYPE,
2626
Component,
27+
ErrorHandler,
2728
inject,
2829
Injectable,
2930
Injector,
@@ -447,6 +448,26 @@ type AnimationBrowserModule = typeof import('@angular/animations/browser');
447448
}
448449
});
449450
});
451+
452+
it('should be able to inject the renderer factory in an ErrorHandler', async () => {
453+
@Injectable({providedIn: 'root'})
454+
class CustomErrorHandler {
455+
renderer = inject(RendererFactory2).createRenderer(null, null);
456+
}
457+
458+
@Component({template: ''})
459+
class App {}
460+
461+
TestBed.resetTestingModule();
462+
TestBed.configureTestingModule({
463+
providers: [
464+
provideAnimationsAsync(),
465+
{provide: ErrorHandler, useClass: CustomErrorHandler},
466+
],
467+
});
468+
469+
expect(() => TestBed.createComponent(App)).not.toThrow();
470+
});
450471
});
451472
})();
452473

0 commit comments

Comments
 (0)