Skip to content

Commit 22b58a7

Browse files
jasonadenvicb
authored andcommitted
feat(ivy): add support of ApplicationRef.bootstrapModuleFactory (angular#23811)
PR Close angular#23811
1 parent 86b13cc commit 22b58a7

29 files changed

Lines changed: 580 additions & 153 deletions

packages/core/src/application_module.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@ import {APP_INITIALIZER, ApplicationInitStatus} from './application_init';
1010
import {ApplicationRef} from './application_ref';
1111
import {APP_ID_RANDOM_PROVIDER} from './application_tokens';
1212
import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection';
13-
import {forwardRef} from './di/forward_ref';
13+
import {Console} from './console';
14+
import {InjectionToken, Injector, StaticProvider} from './di';
1415
import {Inject, Optional, SkipSelf} from './di/metadata';
16+
import {ErrorHandler} from './error_handler';
1517
import {LOCALE_ID} from './i18n/tokens';
18+
import {ComponentFactoryResolver} from './linker';
1619
import {Compiler} from './linker/compiler';
1720
import {NgModule} from './metadata';
21+
import {NgZone} from './zone';
1822

1923
export function _iterableDiffersFactory() {
2024
return defaultIterableDiffers;
@@ -28,27 +32,36 @@ export function _localeFactory(locale?: string): string {
2832
return locale || 'en-US';
2933
}
3034

35+
export const APPLICATION_MODULE_PROVIDERS: StaticProvider[] = [
36+
{
37+
provide: ApplicationRef,
38+
useClass: ApplicationRef,
39+
deps:
40+
[NgZone, Console, Injector, ErrorHandler, ComponentFactoryResolver, ApplicationInitStatus]
41+
},
42+
{
43+
provide: ApplicationInitStatus,
44+
useClass: ApplicationInitStatus,
45+
deps: [[new Optional(), APP_INITIALIZER]]
46+
},
47+
{provide: Compiler, useClass: Compiler, deps: []},
48+
APP_ID_RANDOM_PROVIDER,
49+
{provide: IterableDiffers, useFactory: _iterableDiffersFactory, deps: []},
50+
{provide: KeyValueDiffers, useFactory: _keyValueDiffersFactory, deps: []},
51+
{
52+
provide: LOCALE_ID,
53+
useFactory: _localeFactory,
54+
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
55+
},
56+
];
57+
3158
/**
3259
* This module includes the providers of @angular/core that are needed
3360
* to bootstrap components via `ApplicationRef`.
3461
*
3562
* @experimental
3663
*/
37-
@NgModule({
38-
providers: [
39-
ApplicationRef,
40-
ApplicationInitStatus,
41-
Compiler,
42-
APP_ID_RANDOM_PROVIDER,
43-
{provide: IterableDiffers, useFactory: _iterableDiffersFactory},
44-
{provide: KeyValueDiffers, useFactory: _keyValueDiffersFactory},
45-
{
46-
provide: LOCALE_ID,
47-
useFactory: _localeFactory,
48-
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
49-
},
50-
],
51-
})
64+
@NgModule({providers: APPLICATION_MODULE_PROVIDERS})
5265
export class ApplicationModule {
5366
// Inject ApplicationRef to make it eager...
5467
constructor(appRef: ApplicationRef) {}

packages/core/src/core_render3_private_export.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export {
2323
injectAttribute as ɵinjectAttribute,
2424
PublicFeature as ɵPublicFeature,
2525
NgOnChangesFeature as ɵNgOnChangesFeature,
26+
NgModuleDef as ɵNgModuleDef,
27+
NgModuleType as ɵNgModuleType,
2628
CssSelectorList as ɵCssSelectorList,
2729
markDirty as ɵmarkDirty,
2830
NC as ɵNC,

packages/core/src/di.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
1818
export {Injectable, InjectableDecorator, InjectableProvider} from './di/injectable';
1919
export {inject, InjectFlags, INJECTOR, Injector} from './di/injector';
2020
export {ReflectiveInjector} from './di/reflective_injector';
21-
export {StaticProvider, ValueProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
21+
export {StaticProvider, ValueProvider, ConstructorSansProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
2222
export {createInjector} from './di/r3_injector';
2323
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './di/reflective_provider';
2424
export {ReflectiveKey} from './di/reflective_key';

packages/core/src/di/injector.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export const INJECTOR = new InjectionToken<Injector>('INJECTOR');
3232
export class NullInjector implements Injector {
3333
get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
3434
if (notFoundValue === _THROW_IF_NOT_FOUND) {
35+
// Intentionally left behind: With dev tools open the debugger will stop here. There is no
36+
// reason why correctly written application should cause this exception.
37+
debugger;
3538
throw new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
3639
}
3740
return notFoundValue;
@@ -487,11 +490,11 @@ export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): an
487490

488491
for (let j = 0; j < arg.length; j++) {
489492
const meta = arg[j];
490-
if (meta instanceof Optional || meta.__proto__.ngMetadataName === 'Optional') {
493+
if (meta instanceof Optional || meta.ngMetadataName === 'Optional') {
491494
flags |= InjectFlags.Optional;
492-
} else if (meta instanceof SkipSelf || meta.__proto__.ngMetadataName === 'SkipSelf') {
495+
} else if (meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf') {
493496
flags |= InjectFlags.SkipSelf;
494-
} else if (meta instanceof Self || meta.__proto__.ngMetadataName === 'Self') {
497+
} else if (meta instanceof Self || meta.ngMetadataName === 'Self') {
495498
flags |= InjectFlags.Self;
496499
} else if (meta instanceof Inject) {
497500
type = meta.token;

packages/core/src/di/provider.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,6 @@ export interface StaticClassProvider extends StaticClassSansProvider {
151151
*
152152
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
153153
*
154-
* ### Example
155-
*
156-
* {@example core/di/ts/provider_spec.ts region='ConstructorSansProvider'}
157-
*
158154
* @experimental
159155
*/
160156
export interface ConstructorSansProvider {
@@ -453,5 +449,5 @@ export interface ClassProvider extends ClassSansProvider {
453449
*
454450
*
455451
*/
456-
export type Provider =
457-
TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any[];
452+
export type Provider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider |
453+
ExistingProvider | FactoryProvider | any[];

packages/core/src/di/r3_injector.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {InjectableDef, InjectableType, InjectorDef, InjectorType, InjectorTypeWi
1414
import {resolveForwardRef} from './forward_ref';
1515
import {InjectableDefToken, InjectionToken} from './injection_token';
1616
import {INJECTOR, InjectFlags, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, inject, injectArgs, setCurrentInjector} from './injector';
17-
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, TypeProvider, ValueProvider} from './provider';
17+
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './provider';
1818
import {APP_ROOT} from './scope';
1919

2020

@@ -64,14 +64,15 @@ interface Record<T> {
6464
}
6565

6666
/**
67-
* Create a new `Injector` which is configured using `InjectorType`s.
67+
* Create a new `Injector` which is configured using a `defType` of `InjectorType<any>`s.
6868
*
6969
* @experimental
7070
*/
7171
export function createInjector(
72-
defType: /* InjectorType<any> */ any, parent: Injector | null = null): Injector {
72+
defType: /* InjectorType<any> */ any, parent: Injector | null = null,
73+
additionalProviders: StaticProvider[] | null = null): Injector {
7374
parent = parent || getNullInjector();
74-
return new R3Injector(defType, parent);
75+
return new R3Injector(defType, additionalProviders, parent);
7576
}
7677

7778
export class R3Injector {
@@ -101,12 +102,18 @@ export class R3Injector {
101102
*/
102103
private destroyed = false;
103104

104-
constructor(def: InjectorType<any>, readonly parent: Injector) {
105+
constructor(
106+
def: InjectorType<any>, additionalProviders: StaticProvider[]|null,
107+
readonly parent: Injector) {
105108
// Start off by creating Records for every provider declared in every InjectorType
106109
// included transitively in `def`.
107110
deepForEach(
108111
[def], injectorDef => this.processInjectorType(injectorDef, new Set<InjectorType<any>>()));
109112

113+
additionalProviders &&
114+
deepForEach(additionalProviders, provider => this.processProvider(provider));
115+
116+
110117
// Make sure the INJECTOR token provides this injector.
111118
this.records.set(INJECTOR, makeRecord(undefined, this));
112119

@@ -284,20 +291,18 @@ export class R3Injector {
284291
throw new Error(`Mixed multi-provider for ${token}.`);
285292
}
286293
} else {
287-
token = provider;
288294
multiRecord = makeRecord(undefined, NOT_YET, true);
289295
multiRecord.factory = () => injectArgs(multiRecord !.multi !);
290296
this.records.set(token, multiRecord);
291297
}
292298
token = provider;
293299
multiRecord.multi !.push(provider);
300+
} else {
301+
const existing = this.records.get(token);
302+
if (existing && existing.multi !== undefined) {
303+
throw new Error(`Mixed multi-provider for ${stringify(token)}`);
304+
}
294305
}
295-
296-
const existing = this.records.get(token);
297-
if (existing && existing.multi !== undefined) {
298-
throw new Error(`Mixed multi-provider for ${token}`);
299-
}
300-
301306
this.records.set(token, record);
302307
}
303308

packages/core/src/render3/assert.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ export function assertGreaterThan<T>(actual: T, expected: T, msg: string) {
4646
}
4747
}
4848

49-
export function assertNull<T>(actual: T, msg: string) {
49+
export function assertNotDefined<T>(actual: T, msg: string) {
5050
if (actual != null) {
5151
throwError(msg);
5252
}
5353
}
5454

55-
export function assertNotNull<T>(actual: T, msg: string) {
55+
export function assertDefined<T>(actual: T, msg: string) {
5656
if (actual == null) {
5757
throwError(msg);
5858
}

packages/core/src/render3/component.ts

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {Injector} from '../di/injector';
1313
import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
1414
import {Sanitizer} from '../sanitization/security';
1515

16-
import {assertComponentType, assertNotNull} from './assert';
16+
import {assertComponentType, assertDefined} from './assert';
1717
import {queueInitHooks, queueLifecycleHooks} from './hooks';
1818
import {CLEAN_PROMISE, ROOT_DIRECTIVE_INDICES, _getComponentHostLElementNode, baseDirectiveCreate, createLView, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, setHostBindings} from './instructions';
1919
import {ComponentDef, ComponentType} from './interfaces/definition';
@@ -72,31 +72,6 @@ export interface CreateComponentOptions {
7272
}
7373

7474

75-
/**
76-
* Bootstraps a component, then creates and returns a `ComponentRef` for that component.
77-
*
78-
* @param componentType Component to bootstrap
79-
* @param options Optional parameters which control bootstrapping
80-
*/
81-
export function createComponentRef<T>(
82-
componentType: ComponentType<T>, opts: CreateComponentOptions): viewEngine_ComponentRef<T> {
83-
const component = renderComponent(componentType, opts);
84-
const hostView = _getComponentHostLElementNode(component).data as LView;
85-
const hostViewRef = new ViewRef(hostView, component);
86-
return {
87-
location: {nativeElement: getHostElement(component)},
88-
injector: opts.injector || NULL_INJECTOR,
89-
instance: component,
90-
hostView: hostViewRef,
91-
changeDetectorRef: hostViewRef,
92-
componentType: componentType,
93-
// TODO: implement destroy and onDestroy
94-
destroy: () => {},
95-
onDestroy: (cb: Function) => {}
96-
};
97-
}
98-
99-
10075
// TODO: A hack to not pull in the NullInjector from @angular/core.
10176
export const NULL_INJECTOR: Injector = {
10277
get: (token: any, notFoundValue?: any) => {
@@ -131,12 +106,8 @@ export function renderComponent<T>(
131106
// The first index of the first selector is the tag name.
132107
const componentTag = componentDef.selectors ![0] ![0] as string;
133108
const hostNode = locateHostElement(rendererFactory, opts.host || componentTag);
134-
const rootContext: RootContext = {
135-
// Incomplete initialization due to circular reference.
136-
component: null !,
137-
scheduler: opts.scheduler || requestAnimationFrame.bind(window),
138-
clean: CLEAN_PROMISE,
139-
};
109+
const rootContext = createRootContext(opts.scheduler || requestAnimationFrame.bind(window));
110+
140111
const rootView: LView = createLView(
141112
rendererFactory.createRenderer(hostNode, componentDef.rendererType),
142113
createTView(-1, null, null, null), rootContext,
@@ -152,8 +123,8 @@ export function renderComponent<T>(
152123
elementNode = hostElement(componentTag, hostNode, componentDef, sanitizer);
153124

154125
// Create directive instance with factory() and store at index 0 in directives array
155-
component = rootContext.component =
156-
baseDirectiveCreate(0, componentDef.factory(), componentDef) as T;
126+
rootContext.components.push(
127+
component = baseDirectiveCreate(0, componentDef.factory(), componentDef) as T);
157128
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
158129

159130
opts.hostFeatures && opts.hostFeatures.forEach((feature) => feature(component, componentDef));
@@ -169,6 +140,14 @@ export function renderComponent<T>(
169140
return component;
170141
}
171142

143+
export function createRootContext(scheduler: (workFn: () => void) => void): RootContext {
144+
return {
145+
components: [],
146+
scheduler: scheduler,
147+
clean: CLEAN_PROMISE,
148+
};
149+
}
150+
172151
/**
173152
* Used to enable lifecycle hooks on the root component.
174153
*
@@ -198,7 +177,7 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
198177
*/
199178
function getRootContext(component: any): RootContext {
200179
const rootContext = getRootView(component).context as RootContext;
201-
ngDevMode && assertNotNull(rootContext, 'rootContext');
180+
ngDevMode && assertDefined(rootContext, 'rootContext');
202181
return rootContext;
203182
}
204183

0 commit comments

Comments
 (0)