diff --git a/packages/core/src/application_ref.ts b/packages/core/src/application_ref.ts index 5657f358d3..0d3b395547 100644 --- a/packages/core/src/application_ref.ts +++ b/packages/core/src/application_ref.ts @@ -358,10 +358,15 @@ export abstract class ApplicationRef { * specified application component onto DOM elements identified by the [componentType]'s * selector and kicks off automatic change detection to finish initializing the component. * + * Optionally, a component can be mounted onto a DOM element that does not match the + * [componentType]'s selector. + * * ### Example * {@example core/ts/platform/platform.ts region='longform'} */ - abstract bootstrap(componentFactory: ComponentFactory|Type): ComponentRef; + abstract bootstrap( + componentFactory: ComponentFactory|Type, + rootSelectorOrNode?: string|any): ComponentRef; /** * Invoke this method to explicitly process change detection and its side-effects. @@ -491,7 +496,8 @@ export class ApplicationRef_ extends ApplicationRef { view.detachFromAppRef(); } - bootstrap(componentOrFactory: ComponentFactory|Type): ComponentRef { + bootstrap(componentOrFactory: ComponentFactory|Type, rootSelectorOrNode?: string|any): + ComponentRef { if (!this._initStatus.done) { throw new Error( 'Cannot bootstrap as there are still asynchronous initializers running. Bootstrap components in the `ngDoBootstrap` method of the root module.'); @@ -509,7 +515,8 @@ export class ApplicationRef_ extends ApplicationRef { const ngModule = componentFactory instanceof ComponentFactoryBoundToModule ? null : this._injector.get(NgModuleRef); - const compRef = componentFactory.create(Injector.NULL, [], componentFactory.selector, ngModule); + const selectorOrNode = rootSelectorOrNode || componentFactory.selector; + const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule); compRef.onDestroy(() => { this._unloadComponent(compRef); }); const testability = compRef.injector.get(Testability, null); diff --git a/packages/core/test/application_ref_spec.ts b/packages/core/test/application_ref_spec.ts index 01deb95a60..b3e3c1009e 100644 --- a/packages/core/test/application_ref_spec.ts +++ b/packages/core/test/application_ref_spec.ts @@ -30,11 +30,11 @@ export function main() { beforeEach(() => { mockConsole = new MockConsole(); }); - function createRootEl() { + function createRootEl(selector = 'bootstrap-app') { const doc = TestBed.get(DOCUMENT); const rootEl = getDOM().firstChild( - getDOM().content(getDOM().createTemplate(``))); - const oldRoots = getDOM().querySelectorAll(doc, 'bootstrap-app'); + getDOM().content(getDOM().createTemplate(`<${selector}>`))); + const oldRoots = getDOM().querySelectorAll(doc, selector); for (let i = 0; i < oldRoots.length; i++) { getDOM().remove(oldRoots[i]); } @@ -100,6 +100,34 @@ export function main() { expect(component.injector.get('hello')).toEqual('component'); }))); + it('should bootstrap a component with a custom selector', + async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => { + @Component({ + selector: 'bootstrap-app', + template: '', + }) + class SomeComponent { + } + + @NgModule({ + providers: [{provide: 'hello', useValue: 'component'}], + declarations: [SomeComponent], + entryComponents: [SomeComponent], + }) + class SomeModule { + } + + createRootEl('custom-selector'); + const modFactory = compiler.compileModuleSync(SomeModule); + const module = modFactory.create(TestBed); + const cmpFactory = + module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !; + const component = app.bootstrap(cmpFactory, 'custom-selector'); + + // The component should see the child module providers + expect(component.injector.get('hello')).toEqual('component'); + }))); + describe('ApplicationRef', () => { beforeEach(() => { TestBed.configureTestingModule({imports: [createModule()]}); }); diff --git a/tools/public_api_guard/core/core.d.ts b/tools/public_api_guard/core/core.d.ts index 84d2401ef9..56471d2bb2 100644 --- a/tools/public_api_guard/core/core.d.ts +++ b/tools/public_api_guard/core/core.d.ts @@ -131,7 +131,7 @@ export declare abstract class ApplicationRef { readonly abstract isStable: Observable; readonly abstract viewCount: number; abstract attachView(view: ViewRef): void; - abstract bootstrap(componentFactory: ComponentFactory | Type): ComponentRef; + abstract bootstrap(componentFactory: ComponentFactory | Type, rootSelectorOrNode?: string | any): ComponentRef; abstract detachView(view: ViewRef): void; abstract tick(): void; }