Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): allow custom selector when bootstrapping components #15668

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 10 additions & 3 deletions packages/core/src/application_ref.ts
Expand Up @@ -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<C>(componentFactory: ComponentFactory<C>|Type<C>): ComponentRef<C>;
abstract bootstrap<C>(
componentFactory: ComponentFactory<C>|Type<C>,
rootSelectorOrNode?: string|any): ComponentRef<C>;

/**
* Invoke this method to explicitly process change detection and its side-effects.
Expand Down Expand Up @@ -491,7 +496,8 @@ export class ApplicationRef_ extends ApplicationRef {
view.detachFromAppRef();
}

bootstrap<C>(componentOrFactory: ComponentFactory<C>|Type<C>): ComponentRef<C> {
bootstrap<C>(componentOrFactory: ComponentFactory<C>|Type<C>, rootSelectorOrNode?: string|any):
ComponentRef<C> {
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.');
Expand All @@ -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);
Expand Down
34 changes: 31 additions & 3 deletions packages/core/test/application_ref_spec.ts
Expand Up @@ -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 = <HTMLElement>getDOM().firstChild(
getDOM().content(getDOM().createTemplate(`<bootstrap-app></bootstrap-app>`)));
const oldRoots = getDOM().querySelectorAll(doc, 'bootstrap-app');
getDOM().content(getDOM().createTemplate(`<${selector}></${selector}>`)));
const oldRoots = getDOM().querySelectorAll(doc, selector);
for (let i = 0; i < oldRoots.length; i++) {
getDOM().remove(oldRoots[i]);
}
Expand Down Expand Up @@ -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()]}); });

Expand Down
2 changes: 1 addition & 1 deletion tools/public_api_guard/core/core.d.ts
Expand Up @@ -131,7 +131,7 @@ export declare abstract class ApplicationRef {
readonly abstract isStable: Observable<boolean>;
readonly abstract viewCount: number;
abstract attachView(view: ViewRef): void;
abstract bootstrap<C>(componentFactory: ComponentFactory<C> | Type<C>): ComponentRef<C>;
abstract bootstrap<C>(componentFactory: ComponentFactory<C> | Type<C>, rootSelectorOrNode?: string | any): ComponentRef<C>;
abstract detachView(view: ViewRef): void;
abstract tick(): void;
}
Expand Down