diff --git a/goldens/material/bottom-sheet/index.api.md b/goldens/material/bottom-sheet/index.api.md index 8c960231b9ac..94225e8a2317 100644 --- a/goldens/material/bottom-sheet/index.api.md +++ b/goldens/material/bottom-sheet/index.api.md @@ -15,6 +15,7 @@ import * as i1 from '@angular/cdk/dialog'; import * as i2$1 from '@angular/cdk/bidi'; import * as i2 from '@angular/cdk/portal'; import { InjectionToken } from '@angular/core'; +import { Injector } from '@angular/core'; import { Observable } from 'rxjs'; import { OnDestroy } from '@angular/core'; import { ScrollStrategy } from '@angular/cdk/overlay'; @@ -58,6 +59,7 @@ export class MatBottomSheetConfig { disableClose?: boolean; hasBackdrop?: boolean; height?: string; + injector?: Injector; maxHeight?: number | string; minHeight?: number | string; panelClass?: string | string[]; diff --git a/src/material/bottom-sheet/bottom-sheet-config.ts b/src/material/bottom-sheet/bottom-sheet-config.ts index c7ffe34d14d5..07cb81942778 100644 --- a/src/material/bottom-sheet/bottom-sheet-config.ts +++ b/src/material/bottom-sheet/bottom-sheet-config.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ +import {InjectionToken, Injector, ViewContainerRef} from '@angular/core'; import {Direction} from '@angular/cdk/bidi'; import {ScrollStrategy} from '@angular/cdk/overlay'; -import {InjectionToken, ViewContainerRef} from '@angular/core'; /** Options for where to set focus to automatically on dialog open */ export type AutoFocusTarget = 'dialog' | 'first-tabbable' | 'first-heading'; @@ -23,6 +23,12 @@ export class MatBottomSheetConfig { /** The view container to place the overlay for the bottom sheet into. */ viewContainerRef?: ViewContainerRef; + /** + * Injector used for the instantiation of the component to be attached. If provided, + * takes precedence over the injector indirectly provided by `ViewContainerRef`. + */ + injector?: Injector; + /** Extra CSS classes to be added to the bottom sheet container. */ panelClass?: string | string[]; diff --git a/src/material/bottom-sheet/bottom-sheet.spec.ts b/src/material/bottom-sheet/bottom-sheet.spec.ts index dd344711a9fc..e17deec2a6f2 100644 --- a/src/material/bottom-sheet/bottom-sheet.spec.ts +++ b/src/material/bottom-sheet/bottom-sheet.spec.ts @@ -13,12 +13,16 @@ import {SpyLocation} from '@angular/common/testing'; import { Component, ComponentRef, + createNgModuleRef, Directive, + Injectable, Injector, + NgModule, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation, + forwardRef, inject, } from '@angular/core'; import { @@ -987,6 +991,25 @@ describe('MatBottomSheet with default options', () => { })); }); +describe('MatBottomSheet with explicit injector provided', () => { + let overlayContainerElement: HTMLElement; + let fixture: ComponentFixture; + + beforeEach(fakeAsync(() => { + overlayContainerElement = TestBed.inject(OverlayContainer).getContainerElement(); + fixture = TestBed.createComponent(ModuleBoundBottomSheetParentComponent); + })); + + it('should use the standalone injector and render the bottom sheet successfully', () => { + fixture.componentInstance.openBottomSheet(); + fixture.detectChanges(); + + expect( + overlayContainerElement.querySelector('module-bound-bottom-sheet-child-component')!.innerHTML, + ).toEqual('

Pasta

'); + }); +}); + @Directive({ selector: 'dir-with-view-container', }) @@ -1066,3 +1089,46 @@ class BottomSheetWithInjectedData { encapsulation: ViewEncapsulation.ShadowDom, }) class ShadowDomComponent {} + +@Component({ + template: '', +}) +class ModuleBoundBottomSheetParentComponent { + private _injector = inject(Injector); + private _bottomSheet = inject(MatBottomSheet); + + openBottomSheet(): void { + const ngModuleRef = createNgModuleRef( + ModuleBoundBottomSheetModule, + /* parentInjector */ this._injector, + ); + + this._bottomSheet.open(ModuleBoundBottomSheetComponent, {injector: ngModuleRef.injector}); + } +} + +@Injectable() +class ModuleBoundBottomSheetService { + name = 'Pasta'; +} + +@Component({ + template: + '', + imports: [forwardRef(() => ModuleBoundBottomSheetChildComponent)], +}) +class ModuleBoundBottomSheetComponent {} + +@Component({ + selector: 'module-bound-bottom-sheet-child-component', + template: '

{{service.name}}

', +}) +class ModuleBoundBottomSheetChildComponent { + service = inject(ModuleBoundBottomSheetService); +} + +@NgModule({ + imports: [ModuleBoundBottomSheetComponent, ModuleBoundBottomSheetChildComponent], + providers: [ModuleBoundBottomSheetService], +}) +class ModuleBoundBottomSheetModule {}