Skip to content

Commit a0aa830

Browse files
JeanMecheatscott
authored andcommitted
feat(core): bootstrap via ApplicationRef with config
This is to align the shape of the method with `createComponent` BREAKING CHANGE:The second arguement of appRef.bootstrap does not accept `any` anymore. Make sure the element you pass is not nullable. fixes #67946
1 parent 77f1ca0 commit a0aa830

13 files changed

Lines changed: 112 additions & 11 deletions

File tree

goldens/public-api/core/index.api.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,13 @@ export class ApplicationModule {
133133
export class ApplicationRef {
134134
constructor();
135135
attachView(viewRef: ViewRef): void;
136-
bootstrap<C>(component: Type<C>, rootSelectorOrNode?: string | any): ComponentRef<C>;
136+
bootstrap<C>(component: Type<C>, options?: {
137+
hostElement?: Element | string;
138+
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
139+
bindings?: Binding[];
140+
}): ComponentRef<C>;
141+
// (undocumented)
142+
bootstrap<C>(component: Type<C>, hostElement?: Element | string): ComponentRef<C>;
137143
readonly components: ComponentRef<any>[];
138144
readonly componentTypes: Type<any>[];
139145
destroy(): void;
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"dist/browser/main-[hash].js": 227093,
3-
"dist/browser/polyfills-[hash].js": 34544,
2+
"dist/browser/main-[hash].js": 232126,
3+
"dist/browser/polyfills-[hash].js": 35726,
44
"dist/browser/event-dispatch-contract.min.js": 476
55
}

packages/core/src/application/application_ref.ts

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {RendererFactory2} from '../render/api';
3737
import {AfterRenderManager} from '../render3/after_render/manager';
3838
import {ComponentFactory as R3ComponentFactory} from '../render3/component_ref';
3939
import {isStandalone} from '../render3/def_getters';
40+
import type {Binding, DirectiveWithBindings} from '../render3/dynamic_bindings';
4041
import {ChangeDetectionMode, detectChangesInternal} from '../render3/instructions/change_detection';
4142
import {publishDefaultGlobalUtils as _publishDefaultGlobalUtils} from '../render3/util/global_utils';
4243
import {requiresRefreshOrTraversal} from '../render3/util/view_utils';
@@ -375,7 +376,7 @@ export class ApplicationRef {
375376
*
376377
* When bootstrapping a component, Angular mounts it onto a target DOM element
377378
* and kicks off automatic change detection. The target DOM element can be
378-
* provided using the `rootSelectorOrNode` argument.
379+
* provided using the `hostElement` argument.
379380
*
380381
* If the target DOM element is not provided, Angular tries to find one on a page
381382
* using the `selector` of the component that is being bootstrapped
@@ -403,7 +404,16 @@ export class ApplicationRef {
403404
*
404405
* {@example core/ts/platform/platform.ts region='domNode'}
405406
*/
406-
bootstrap<C>(component: Type<C>, rootSelectorOrNode?: string | any): ComponentRef<C>;
407+
bootstrap<C>(
408+
component: Type<C>,
409+
options?: {
410+
hostElement?: Element | string;
411+
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
412+
bindings?: Binding[];
413+
},
414+
): ComponentRef<C>;
415+
416+
bootstrap<C>(component: Type<C>, hostElement?: Element | string): ComponentRef<C>;
407417

408418
/**
409419
* Bootstrap a component onto the element identified by its selector or, optionally, to a
@@ -414,7 +424,7 @@ export class ApplicationRef {
414424
*
415425
* When bootstrapping a component, Angular mounts it onto a target DOM element
416426
* and kicks off automatic change detection. The target DOM element can be
417-
* provided using the `rootSelectorOrNode` argument.
427+
* provided using the `hostElement` argument.
418428
*
419429
* If the target DOM element is not provided, Angular tries to find one on a page
420430
* using the `selector` of the component that is being bootstrapped
@@ -448,7 +458,15 @@ export class ApplicationRef {
448458

449459
private bootstrapImpl<C>(
450460
component: Type<C>,
451-
rootSelectorOrNode?: string | any,
461+
hostElementOrOptions?:
462+
| Element
463+
| string
464+
| undefined
465+
| {
466+
hostElement?: Element | string | undefined;
467+
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
468+
bindings?: Binding[];
469+
},
452470
injector: Injector = Injector.NULL,
453471
): ComponentRef<C> {
454472
const ngZone = this._injector.get(NgZone);
@@ -479,8 +497,16 @@ export class ApplicationRef {
479497
const ngModule = isBoundToModule(componentFactory)
480498
? undefined
481499
: this._injector.get(NgModuleRef);
482-
const selectorOrNode = rootSelectorOrNode || componentFactory.selector;
483-
const compRef = componentFactory.create(injector, [], selectorOrNode, ngModule);
500+
const {hostElement, directives, bindings} = normalizeBootstrapOptions(hostElementOrOptions);
501+
const selectorOrNode = hostElement || componentFactory.selector;
502+
const compRef = componentFactory.create(
503+
injector,
504+
[],
505+
selectorOrNode,
506+
ngModule,
507+
directives,
508+
bindings,
509+
);
484510
const nativeElement = compRef.location.nativeElement;
485511
const testability = compRef.injector.get(TESTABILITY, null);
486512
testability?.registerApplication(nativeElement);
@@ -803,6 +829,32 @@ export class ApplicationRef {
803829
}
804830
}
805831

832+
function normalizeBootstrapOptions(
833+
hostElementOrOptions:
834+
| Element
835+
| string
836+
| undefined
837+
| {
838+
hostElement?: Element | string | undefined;
839+
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
840+
bindings?: Binding[];
841+
},
842+
): {
843+
hostElement?: Element | string | undefined;
844+
directives?: (Type<unknown> | DirectiveWithBindings<unknown>)[];
845+
bindings?: Binding[];
846+
} {
847+
if (
848+
hostElementOrOptions === undefined ||
849+
typeof hostElementOrOptions === 'string' ||
850+
hostElementOrOptions instanceof Element
851+
) {
852+
return {hostElement: hostElementOrOptions};
853+
}
854+
855+
return hostElementOrOptions;
856+
}
857+
806858
function warnIfDestroyed(destroyed: boolean): void {
807859
if (destroyed) {
808860
console.warn(

packages/core/test/application_ref_spec.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ import {
2121
ChangeDetectionStrategy,
2222
Component,
2323
DestroyRef,
24+
Directive,
2425
EnvironmentInjector,
2526
Injector,
27+
Input,
28+
inputBinding,
2629
LOCALE_ID,
2730
NgModule,
2831
NgZone,
@@ -213,6 +216,38 @@ describe('bootstrap', () => {
213216
const comp = ref.bootstrap(ZoneComp);
214217
expect(comp.instance.inNgZone).toBeTrue();
215218
}));
219+
220+
it('supports passing bootstrap options object', inject(
221+
[ApplicationRef],
222+
(ref: ApplicationRef) => {
223+
@Directive({
224+
host: {'[attr.data-dir]': 'value'},
225+
})
226+
class HostDir {
227+
@Input()
228+
value = 'unset';
229+
}
230+
231+
@Component({
232+
selector: 'bootstrap-app',
233+
template: `{{ name }}`,
234+
})
235+
class StandaloneBootComp {
236+
@Input()
237+
name = 'default';
238+
}
239+
240+
createRootEl('custom-selector');
241+
const comp = ref.bootstrap(StandaloneBootComp, {
242+
hostElement: 'custom-selector',
243+
directives: [{type: HostDir, bindings: [inputBinding('value', () => 'bound')]}],
244+
bindings: [inputBinding('name', () => 'hello from binding')],
245+
});
246+
247+
expect(comp.location.nativeElement.getAttribute('data-dir')).toBe('bound');
248+
expect(comp.location.nativeElement.textContent.trim()).toBe('hello from binding');
249+
},
250+
));
216251
});
217252

218253
describe('bootstrapImpl', () => {
@@ -236,7 +271,7 @@ describe('bootstrap', () => {
236271
const appRef = ref as unknown as {bootstrapImpl: ApplicationRef['bootstrapImpl']};
237272
const compRef = appRef.bootstrapImpl(
238273
InjectingComponent,
239-
/* rootSelectorOrNode */ undefined,
274+
/* hostElement */ undefined,
240275
injector,
241276
);
242277
expect(compRef.instance.myService).toBe(myService);

packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@
713713
"noop2",
714714
"normalizeAnimationEntry",
715715
"normalizeAnimationOptions",
716+
"normalizeBootstrapOptions",
716717
"normalizeKeyframes",
717718
"normalizeKeyframes",
718719
"normalizeParams",

packages/core/test/bundling/create_component/bundle.golden_symbols.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,7 @@
587587
"noSideEffects",
588588
"noop",
589589
"noop2",
590+
"normalizeBootstrapOptions",
590591
"notFoundValueOrThrow",
591592
"observable",
592593
"onEnter",

packages/core/test/bundling/defer/bundle.golden_symbols.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@
631631
"noSideEffects",
632632
"noop",
633633
"noop2",
634+
"normalizeBootstrapOptions",
634635
"notFoundValueOrThrow",
635636
"observable",
636637
"onEnter",

packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@
860860
"noSideEffects",
861861
"noop",
862862
"noop2",
863+
"normalizeBootstrapOptions",
863864
"normalizeSuffix",
864865
"normalizeValidators",
865866
"notFoundValueOrThrow",

packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@
857857
"noSideEffects",
858858
"noop",
859859
"noop2",
860+
"normalizeBootstrapOptions",
860861
"normalizeSuffix",
861862
"normalizeValidators",
862863
"notFoundValueOrThrow",

packages/core/test/bundling/hydration/bundle.golden_symbols.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@
665665
"noSideEffects",
666666
"noop",
667667
"noop2",
668+
"normalizeBootstrapOptions",
668669
"notFoundValueOrThrow",
669670
"observable",
670671
"observeOn",

0 commit comments

Comments
 (0)