feat(ivy): add support of ApplicationRef.bootstrapModuleFactory (#23811)
PR Close #23811
This commit is contained in:
parent
86b13ccf80
commit
22b58a717a
|
@ -10,11 +10,15 @@ import {APP_INITIALIZER, ApplicationInitStatus} from './application_init';
|
|||
import {ApplicationRef} from './application_ref';
|
||||
import {APP_ID_RANDOM_PROVIDER} from './application_tokens';
|
||||
import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection';
|
||||
import {forwardRef} from './di/forward_ref';
|
||||
import {Console} from './console';
|
||||
import {InjectionToken, Injector, StaticProvider} from './di';
|
||||
import {Inject, Optional, SkipSelf} from './di/metadata';
|
||||
import {ErrorHandler} from './error_handler';
|
||||
import {LOCALE_ID} from './i18n/tokens';
|
||||
import {ComponentFactoryResolver} from './linker';
|
||||
import {Compiler} from './linker/compiler';
|
||||
import {NgModule} from './metadata';
|
||||
import {NgZone} from './zone';
|
||||
|
||||
export function _iterableDiffersFactory() {
|
||||
return defaultIterableDiffers;
|
||||
|
@ -28,27 +32,36 @@ export function _localeFactory(locale?: string): string {
|
|||
return locale || 'en-US';
|
||||
}
|
||||
|
||||
export const APPLICATION_MODULE_PROVIDERS: StaticProvider[] = [
|
||||
{
|
||||
provide: ApplicationRef,
|
||||
useClass: ApplicationRef,
|
||||
deps:
|
||||
[NgZone, Console, Injector, ErrorHandler, ComponentFactoryResolver, ApplicationInitStatus]
|
||||
},
|
||||
{
|
||||
provide: ApplicationInitStatus,
|
||||
useClass: ApplicationInitStatus,
|
||||
deps: [[new Optional(), APP_INITIALIZER]]
|
||||
},
|
||||
{provide: Compiler, useClass: Compiler, deps: []},
|
||||
APP_ID_RANDOM_PROVIDER,
|
||||
{provide: IterableDiffers, useFactory: _iterableDiffersFactory, deps: []},
|
||||
{provide: KeyValueDiffers, useFactory: _keyValueDiffersFactory, deps: []},
|
||||
{
|
||||
provide: LOCALE_ID,
|
||||
useFactory: _localeFactory,
|
||||
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* This module includes the providers of @angular/core that are needed
|
||||
* to bootstrap components via `ApplicationRef`.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@NgModule({
|
||||
providers: [
|
||||
ApplicationRef,
|
||||
ApplicationInitStatus,
|
||||
Compiler,
|
||||
APP_ID_RANDOM_PROVIDER,
|
||||
{provide: IterableDiffers, useFactory: _iterableDiffersFactory},
|
||||
{provide: KeyValueDiffers, useFactory: _keyValueDiffersFactory},
|
||||
{
|
||||
provide: LOCALE_ID,
|
||||
useFactory: _localeFactory,
|
||||
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
|
||||
},
|
||||
],
|
||||
})
|
||||
@NgModule({providers: APPLICATION_MODULE_PROVIDERS})
|
||||
export class ApplicationModule {
|
||||
// Inject ApplicationRef to make it eager...
|
||||
constructor(appRef: ApplicationRef) {}
|
||||
|
|
|
@ -23,6 +23,8 @@ export {
|
|||
injectAttribute as ɵinjectAttribute,
|
||||
PublicFeature as ɵPublicFeature,
|
||||
NgOnChangesFeature as ɵNgOnChangesFeature,
|
||||
NgModuleDef as ɵNgModuleDef,
|
||||
NgModuleType as ɵNgModuleType,
|
||||
CssSelectorList as ɵCssSelectorList,
|
||||
markDirty as ɵmarkDirty,
|
||||
NC as ɵNC,
|
||||
|
|
|
@ -18,7 +18,7 @@ export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
|
|||
export {Injectable, InjectableDecorator, InjectableProvider} from './di/injectable';
|
||||
export {inject, InjectFlags, INJECTOR, Injector} from './di/injector';
|
||||
export {ReflectiveInjector} from './di/reflective_injector';
|
||||
export {StaticProvider, ValueProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
|
||||
export {StaticProvider, ValueProvider, ConstructorSansProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
|
||||
export {createInjector} from './di/r3_injector';
|
||||
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './di/reflective_provider';
|
||||
export {ReflectiveKey} from './di/reflective_key';
|
||||
|
|
|
@ -32,6 +32,9 @@ export const INJECTOR = new InjectionToken<Injector>('INJECTOR');
|
|||
export class NullInjector implements Injector {
|
||||
get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
|
||||
if (notFoundValue === _THROW_IF_NOT_FOUND) {
|
||||
// Intentionally left behind: With dev tools open the debugger will stop here. There is no
|
||||
// reason why correctly written application should cause this exception.
|
||||
debugger;
|
||||
throw new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
|
||||
}
|
||||
return notFoundValue;
|
||||
|
@ -487,11 +490,11 @@ export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): an
|
|||
|
||||
for (let j = 0; j < arg.length; j++) {
|
||||
const meta = arg[j];
|
||||
if (meta instanceof Optional || meta.__proto__.ngMetadataName === 'Optional') {
|
||||
if (meta instanceof Optional || meta.ngMetadataName === 'Optional') {
|
||||
flags |= InjectFlags.Optional;
|
||||
} else if (meta instanceof SkipSelf || meta.__proto__.ngMetadataName === 'SkipSelf') {
|
||||
} else if (meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf') {
|
||||
flags |= InjectFlags.SkipSelf;
|
||||
} else if (meta instanceof Self || meta.__proto__.ngMetadataName === 'Self') {
|
||||
} else if (meta instanceof Self || meta.ngMetadataName === 'Self') {
|
||||
flags |= InjectFlags.Self;
|
||||
} else if (meta instanceof Inject) {
|
||||
type = meta.token;
|
||||
|
|
|
@ -151,10 +151,6 @@ export interface StaticClassProvider extends StaticClassSansProvider {
|
|||
*
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='ConstructorSansProvider'}
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export interface ConstructorSansProvider {
|
||||
|
@ -453,5 +449,5 @@ export interface ClassProvider extends ClassSansProvider {
|
|||
*
|
||||
*
|
||||
*/
|
||||
export type Provider =
|
||||
TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any[];
|
||||
export type Provider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider |
|
||||
ExistingProvider | FactoryProvider | any[];
|
||||
|
|
|
@ -14,7 +14,7 @@ import {InjectableDef, InjectableType, InjectorDef, InjectorType, InjectorTypeWi
|
|||
import {resolveForwardRef} from './forward_ref';
|
||||
import {InjectableDefToken, InjectionToken} from './injection_token';
|
||||
import {INJECTOR, InjectFlags, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, inject, injectArgs, setCurrentInjector} from './injector';
|
||||
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, TypeProvider, ValueProvider} from './provider';
|
||||
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './provider';
|
||||
import {APP_ROOT} from './scope';
|
||||
|
||||
|
||||
|
@ -64,14 +64,15 @@ interface Record<T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new `Injector` which is configured using `InjectorType`s.
|
||||
* Create a new `Injector` which is configured using a `defType` of `InjectorType<any>`s.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export function createInjector(
|
||||
defType: /* InjectorType<any> */ any, parent: Injector | null = null): Injector {
|
||||
defType: /* InjectorType<any> */ any, parent: Injector | null = null,
|
||||
additionalProviders: StaticProvider[] | null = null): Injector {
|
||||
parent = parent || getNullInjector();
|
||||
return new R3Injector(defType, parent);
|
||||
return new R3Injector(defType, additionalProviders, parent);
|
||||
}
|
||||
|
||||
export class R3Injector {
|
||||
|
@ -101,12 +102,18 @@ export class R3Injector {
|
|||
*/
|
||||
private destroyed = false;
|
||||
|
||||
constructor(def: InjectorType<any>, readonly parent: Injector) {
|
||||
constructor(
|
||||
def: InjectorType<any>, additionalProviders: StaticProvider[]|null,
|
||||
readonly parent: Injector) {
|
||||
// Start off by creating Records for every provider declared in every InjectorType
|
||||
// included transitively in `def`.
|
||||
deepForEach(
|
||||
[def], injectorDef => this.processInjectorType(injectorDef, new Set<InjectorType<any>>()));
|
||||
|
||||
additionalProviders &&
|
||||
deepForEach(additionalProviders, provider => this.processProvider(provider));
|
||||
|
||||
|
||||
// Make sure the INJECTOR token provides this injector.
|
||||
this.records.set(INJECTOR, makeRecord(undefined, this));
|
||||
|
||||
|
@ -284,20 +291,18 @@ export class R3Injector {
|
|||
throw new Error(`Mixed multi-provider for ${token}.`);
|
||||
}
|
||||
} else {
|
||||
token = provider;
|
||||
multiRecord = makeRecord(undefined, NOT_YET, true);
|
||||
multiRecord.factory = () => injectArgs(multiRecord !.multi !);
|
||||
this.records.set(token, multiRecord);
|
||||
}
|
||||
token = provider;
|
||||
multiRecord.multi !.push(provider);
|
||||
} else {
|
||||
const existing = this.records.get(token);
|
||||
if (existing && existing.multi !== undefined) {
|
||||
throw new Error(`Mixed multi-provider for ${stringify(token)}`);
|
||||
}
|
||||
}
|
||||
|
||||
const existing = this.records.get(token);
|
||||
if (existing && existing.multi !== undefined) {
|
||||
throw new Error(`Mixed multi-provider for ${token}`);
|
||||
}
|
||||
|
||||
this.records.set(token, record);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,13 +46,13 @@ export function assertGreaterThan<T>(actual: T, expected: T, msg: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export function assertNull<T>(actual: T, msg: string) {
|
||||
export function assertNotDefined<T>(actual: T, msg: string) {
|
||||
if (actual != null) {
|
||||
throwError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNotNull<T>(actual: T, msg: string) {
|
||||
export function assertDefined<T>(actual: T, msg: string) {
|
||||
if (actual == null) {
|
||||
throwError(msg);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import {Injector} from '../di/injector';
|
|||
import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
|
||||
import {Sanitizer} from '../sanitization/security';
|
||||
|
||||
import {assertComponentType, assertNotNull} from './assert';
|
||||
import {assertComponentType, assertDefined} from './assert';
|
||||
import {queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {CLEAN_PROMISE, ROOT_DIRECTIVE_INDICES, _getComponentHostLElementNode, baseDirectiveCreate, createLView, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement, setHostBindings} from './instructions';
|
||||
import {ComponentDef, ComponentType} from './interfaces/definition';
|
||||
|
@ -72,31 +72,6 @@ export interface CreateComponentOptions {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bootstraps a component, then creates and returns a `ComponentRef` for that component.
|
||||
*
|
||||
* @param componentType Component to bootstrap
|
||||
* @param options Optional parameters which control bootstrapping
|
||||
*/
|
||||
export function createComponentRef<T>(
|
||||
componentType: ComponentType<T>, opts: CreateComponentOptions): viewEngine_ComponentRef<T> {
|
||||
const component = renderComponent(componentType, opts);
|
||||
const hostView = _getComponentHostLElementNode(component).data as LView;
|
||||
const hostViewRef = new ViewRef(hostView, component);
|
||||
return {
|
||||
location: {nativeElement: getHostElement(component)},
|
||||
injector: opts.injector || NULL_INJECTOR,
|
||||
instance: component,
|
||||
hostView: hostViewRef,
|
||||
changeDetectorRef: hostViewRef,
|
||||
componentType: componentType,
|
||||
// TODO: implement destroy and onDestroy
|
||||
destroy: () => {},
|
||||
onDestroy: (cb: Function) => {}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// TODO: A hack to not pull in the NullInjector from @angular/core.
|
||||
export const NULL_INJECTOR: Injector = {
|
||||
get: (token: any, notFoundValue?: any) => {
|
||||
|
@ -131,12 +106,8 @@ export function renderComponent<T>(
|
|||
// The first index of the first selector is the tag name.
|
||||
const componentTag = componentDef.selectors ![0] ![0] as string;
|
||||
const hostNode = locateHostElement(rendererFactory, opts.host || componentTag);
|
||||
const rootContext: RootContext = {
|
||||
// Incomplete initialization due to circular reference.
|
||||
component: null !,
|
||||
scheduler: opts.scheduler || requestAnimationFrame.bind(window),
|
||||
clean: CLEAN_PROMISE,
|
||||
};
|
||||
const rootContext = createRootContext(opts.scheduler || requestAnimationFrame.bind(window));
|
||||
|
||||
const rootView: LView = createLView(
|
||||
rendererFactory.createRenderer(hostNode, componentDef.rendererType),
|
||||
createTView(-1, null, null, null), rootContext,
|
||||
|
@ -152,8 +123,8 @@ export function renderComponent<T>(
|
|||
elementNode = hostElement(componentTag, hostNode, componentDef, sanitizer);
|
||||
|
||||
// Create directive instance with factory() and store at index 0 in directives array
|
||||
component = rootContext.component =
|
||||
baseDirectiveCreate(0, componentDef.factory(), componentDef) as T;
|
||||
rootContext.components.push(
|
||||
component = baseDirectiveCreate(0, componentDef.factory(), componentDef) as T);
|
||||
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
|
||||
|
||||
opts.hostFeatures && opts.hostFeatures.forEach((feature) => feature(component, componentDef));
|
||||
|
@ -169,6 +140,14 @@ export function renderComponent<T>(
|
|||
return component;
|
||||
}
|
||||
|
||||
export function createRootContext(scheduler: (workFn: () => void) => void): RootContext {
|
||||
return {
|
||||
components: [],
|
||||
scheduler: scheduler,
|
||||
clean: CLEAN_PROMISE,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to enable lifecycle hooks on the root component.
|
||||
*
|
||||
|
@ -198,7 +177,7 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
|
|||
*/
|
||||
function getRootContext(component: any): RootContext {
|
||||
const rootContext = getRootView(component).context as RootContext;
|
||||
ngDevMode && assertNotNull(rootContext, 'rootContext');
|
||||
ngDevMode && assertDefined(rootContext, 'rootContext');
|
||||
return rootContext;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
||||
import {InjectionToken} from '../di/injection_token';
|
||||
import {Injector, inject} from '../di/injector';
|
||||
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
|
||||
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
||||
import {ElementRef} from '../linker/element_ref';
|
||||
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {RendererFactory2} from '../render/api';
|
||||
import {Type} from '../type';
|
||||
|
||||
import {assertComponentType, assertDefined} from './assert';
|
||||
import {createRootContext} from './component';
|
||||
import {baseDirectiveCreate, createLView, createTView, enterView, hostElement, initChangeDetectorIfExisting, leaveView, locateHostElement} from './instructions';
|
||||
import {ComponentDef, ComponentType} from './interfaces/definition';
|
||||
import {LElementNode} from './interfaces/node';
|
||||
import {RElement} from './interfaces/renderer';
|
||||
import {LView, LViewFlags, RootContext} from './interfaces/view';
|
||||
import {ViewRef} from './view_ref';
|
||||
|
||||
export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver {
|
||||
resolveComponentFactory<T>(component: Type<T>): viewEngine_ComponentFactory<T> {
|
||||
ngDevMode && assertComponentType(component);
|
||||
const componentDef = (component as ComponentType<T>).ngComponentDef;
|
||||
return new ComponentFactory(componentDef);
|
||||
}
|
||||
}
|
||||
|
||||
function toRefArray(map: {[key: string]: string}): {propName: string; templateName: string;}[] {
|
||||
const array: {propName: string; templateName: string;}[] = [];
|
||||
for (let nonMinified in map) {
|
||||
if (map.hasOwnProperty(nonMinified)) {
|
||||
const minified = map[nonMinified];
|
||||
array.push({propName: minified, templateName: nonMinified});
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default {@link RootContext} for all components rendered with {@link renderComponent}.
|
||||
*/
|
||||
export const ROOT_CONTEXT = new InjectionToken<RootContext>(
|
||||
'ROOT_CONTEXT_TOKEN',
|
||||
{providedIn: 'root', factory: () => createRootContext(inject(SCHEDULER))});
|
||||
|
||||
/**
|
||||
* A change detection scheduler token for {@link RootContext}. This token is the default value used
|
||||
* for the default `RootContext` found in the {@link ROOT_CONTEXT} token.
|
||||
*/
|
||||
export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>(
|
||||
'SCHEDULER_TOKEN', {providedIn: 'root', factory: () => requestAnimationFrame.bind(window)});
|
||||
|
||||
/**
|
||||
* Render3 implementation of {@link viewEngine_ComponentFactory}.
|
||||
*/
|
||||
export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||
selector: string;
|
||||
componentType: Type<any>;
|
||||
ngContentSelectors: string[];
|
||||
get inputs(): {propName: string; templateName: string;}[] {
|
||||
return toRefArray(this.componentDef.inputs);
|
||||
}
|
||||
get outputs(): {propName: string; templateName: string;}[] {
|
||||
return toRefArray(this.componentDef.outputs);
|
||||
}
|
||||
|
||||
constructor(private componentDef: ComponentDef<any>) {
|
||||
super();
|
||||
this.componentType = componentDef.type;
|
||||
this.selector = componentDef.selectors[0][0] as string;
|
||||
this.ngContentSelectors = [];
|
||||
}
|
||||
|
||||
create(
|
||||
parentComponentInjector: Injector, projectableNodes?: any[][]|undefined,
|
||||
rootSelectorOrNode?: any,
|
||||
ngModule?: viewEngine_NgModuleRef<any>|undefined): viewEngine_ComponentRef<T> {
|
||||
ngDevMode && assertDefined(ngModule, 'ngModule should always be defined');
|
||||
|
||||
const rendererFactory = ngModule ? ngModule.injector.get(RendererFactory2) : document;
|
||||
const hostNode = locateHostElement(rendererFactory, rootSelectorOrNode);
|
||||
|
||||
// The first index of the first selector is the tag name.
|
||||
const componentTag = this.componentDef.selectors ![0] ![0] as string;
|
||||
|
||||
const rootContext: RootContext = ngModule !.injector.get(ROOT_CONTEXT);
|
||||
|
||||
// Create the root view. Uses empty TView and ContentTemplate.
|
||||
const rootView: LView = createLView(
|
||||
rendererFactory.createRenderer(hostNode, this.componentDef.rendererType),
|
||||
createTView(-1, null, null, null), null,
|
||||
this.componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
|
||||
rootView.injector = ngModule && ngModule.injector || null;
|
||||
|
||||
// rootView is the parent when bootstrapping
|
||||
const oldView = enterView(rootView, null !);
|
||||
|
||||
let component: T;
|
||||
let elementNode: LElementNode;
|
||||
try {
|
||||
if (rendererFactory.begin) rendererFactory.begin();
|
||||
|
||||
// Create element node at index 0 in data array
|
||||
elementNode = hostElement(componentTag, hostNode, this.componentDef);
|
||||
|
||||
// Create directive instance with factory() and store at index 0 in directives array
|
||||
rootContext.components.push(
|
||||
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef) as T);
|
||||
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
|
||||
|
||||
} finally {
|
||||
enterView(oldView, null);
|
||||
if (rendererFactory.end) rendererFactory.end();
|
||||
}
|
||||
|
||||
// TODO(misko): this is the wrong injector here.
|
||||
return new ComponentRef(
|
||||
this.componentType, component, rootView, ngModule !.injector, hostNode !);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instance of a Component created via a {@link ComponentFactory}.
|
||||
*
|
||||
* `ComponentRef` provides access to the Component Instance as well other objects related to this
|
||||
* Component Instance and allows you to destroy the Component Instance via the {@link #destroy}
|
||||
* method.
|
||||
*
|
||||
*/
|
||||
export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
|
||||
destroyCbs: (() => void)[]|null = [];
|
||||
location: ElementRef<any>;
|
||||
injector: Injector;
|
||||
instance: T;
|
||||
hostView: ViewRef<T>;
|
||||
changeDetectorRef: ChangeDetectorRef;
|
||||
componentType: Type<T>;
|
||||
|
||||
constructor(
|
||||
componentType: Type<T>, instance: T, rootView: LView, injector: Injector,
|
||||
hostNode: RElement) {
|
||||
super();
|
||||
this.instance = instance;
|
||||
/* TODO(jasonaden): This is incomplete, to be adjusted in follow-up PR. Notes from Kara:When
|
||||
* ViewRef.detectChanges is called from ApplicationRef.tick, it will call detectChanges at the
|
||||
* component instance level. I suspect this means that lifecycle hooks and host bindings on the
|
||||
* given component won't work (as these are always called at the level above a component).
|
||||
*
|
||||
* In render2, ViewRef.detectChanges uses the root view instance for view checks, not the
|
||||
* component instance. So passing in the root view (1 level above the component) is sufficient.
|
||||
* We might want to think about creating a fake component for the top level? Or overwrite
|
||||
* detectChanges with a function that calls tickRootContext? */
|
||||
this.hostView = this.changeDetectorRef = new ViewRef(rootView, instance);
|
||||
this.injector = injector;
|
||||
this.location = new ElementRef(hostNode);
|
||||
this.componentType = componentType;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
||||
this.destroyCbs !.forEach(fn => fn());
|
||||
this.destroyCbs = null;
|
||||
}
|
||||
onDestroy(callback: () => void): void {
|
||||
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
||||
this.destroyCbs !.push(callback);
|
||||
}
|
||||
}
|
|
@ -18,9 +18,9 @@ import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_co
|
|||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref';
|
||||
import {Type} from '../type';
|
||||
|
||||
import {assertGreaterThan, assertLessThan, assertNotNull} from './assert';
|
||||
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
|
||||
import {addToViewTree, assertPreviousIsParent, createLContainer, createLNodeObject, createTNode, createTView, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
|
||||
import {ComponentTemplate, DirectiveDef} from './interfaces/definition';
|
||||
import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList} from './interfaces/definition';
|
||||
import {LInjector} from './interfaces/injector';
|
||||
import {AttributeMarker, LContainerNode, LElementNode, LNode, LViewNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {LQueries, QueryReadType} from './interfaces/query';
|
||||
|
@ -256,7 +256,7 @@ export function injectAttribute(attrNameToInject: string): string|undefined {
|
|||
const lElement = getPreviousOrParentNode() as LElementNode;
|
||||
ngDevMode && assertNodeType(lElement, TNodeType.Element);
|
||||
const tElement = lElement.tNode;
|
||||
ngDevMode && assertNotNull(tElement, 'expecting tNode');
|
||||
ngDevMode && assertDefined(tElement, 'expecting tNode');
|
||||
const attrs = tElement.attrs;
|
||||
if (attrs) {
|
||||
for (let i = 0; i < attrs.length; i = i + 2) {
|
||||
|
@ -707,7 +707,7 @@ export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef
|
|||
hostTNode.tViews = createTView(
|
||||
-1, hostNode.data.template !, hostTView.directiveRegistry, hostTView.pipeRegistry);
|
||||
}
|
||||
ngDevMode && assertNotNull(hostTNode.tViews, 'TView must be allocated');
|
||||
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
|
||||
di.templateRef = new TemplateRef<any>(
|
||||
getOrCreateElementRef(di), hostTNode.tViews as TView, getRenderer(), hostNode.data.queries);
|
||||
}
|
||||
|
|
|
@ -6,16 +6,16 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {LifecycleHooksFeature, createComponentRef, getHostElement, getRenderedText, renderComponent, whenRendered} from './component';
|
||||
import {LifecycleHooksFeature, getHostElement, getRenderedText, renderComponent, whenRendered} from './component';
|
||||
import {NgOnChangesFeature, PublicFeature, defineComponent, defineDirective, definePipe} from './definition';
|
||||
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveType, PipeDef} from './interfaces/definition';
|
||||
|
||||
export {ComponentFactory, ComponentFactoryResolver, ComponentRef} from './component_ref';
|
||||
export {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, directiveInject, injectAttribute, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di';
|
||||
export {RenderFlags} from './interfaces/definition';
|
||||
export {CssSelectorList} from './interfaces/projection';
|
||||
|
||||
|
||||
|
||||
// Naming scheme:
|
||||
// - Capital letters are for creating things: T(Text), E(Element), D(Directive), V(View),
|
||||
// C(Container), L(Listener)
|
||||
|
@ -73,6 +73,8 @@ export {
|
|||
tick,
|
||||
} from './instructions';
|
||||
|
||||
export {NgModuleDef, NgModuleFactory, NgModuleRef, NgModuleType} from './ng_module_ref';
|
||||
|
||||
export {
|
||||
AttributeMarker
|
||||
} from './interfaces/node';
|
||||
|
@ -122,7 +124,6 @@ export {
|
|||
defineComponent,
|
||||
defineDirective,
|
||||
definePipe,
|
||||
createComponentRef,
|
||||
getHostElement,
|
||||
getRenderedText,
|
||||
renderComponent,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import './ng_dev_mode';
|
||||
|
||||
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull, assertNull, assertSame} from './assert';
|
||||
import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotEqual, assertSame} from './assert';
|
||||
import {LContainer} from './interfaces/container';
|
||||
import {LInjector} from './interfaces/injector';
|
||||
import {CssSelectorList, LProjection, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||
|
@ -406,7 +406,8 @@ export function createLNode(
|
|||
if ((type & TNodeType.ViewOrElement) === TNodeType.ViewOrElement && isState) {
|
||||
// Bit of a hack to bust through the readonly because there is a circular dep between
|
||||
// LView and LNode.
|
||||
ngDevMode && assertNull((state as LView).node, 'LView.node should not have been initialized');
|
||||
ngDevMode &&
|
||||
assertNotDefined((state as LView).node, 'LView.node should not have been initialized');
|
||||
(state as{node: LNode}).node = node;
|
||||
if (firstTemplatePass) (state as LView).tView.node = node.tNode;
|
||||
}
|
||||
|
@ -455,7 +456,7 @@ export function renderTemplate<T>(
|
|||
sanitizer));
|
||||
}
|
||||
const hostView = host.data !;
|
||||
ngDevMode && assertNotNull(hostView, 'Host node should have an LView defined in host.data.');
|
||||
ngDevMode && assertDefined(hostView, 'Host node should have an LView defined in host.data.');
|
||||
renderComponentOrTemplate(host, hostView, context, template);
|
||||
return host;
|
||||
}
|
||||
|
@ -1300,8 +1301,8 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
|
|||
if (value !== NO_CHANGE) {
|
||||
ngDevMode && assertDataInRange(index);
|
||||
const existingNode = data[index] as LTextNode;
|
||||
ngDevMode && assertNotNull(existingNode, 'LNode should exist');
|
||||
ngDevMode && assertNotNull(existingNode.native, 'native element should exist');
|
||||
ngDevMode && assertDefined(existingNode, 'LNode should exist');
|
||||
ngDevMode && assertDefined(existingNode.native, 'native element should exist');
|
||||
ngDevMode && ngDevMode.rendererSetText++;
|
||||
isProceduralRenderer(renderer) ? renderer.setValue(existingNode.native, stringify(value)) :
|
||||
existingNode.native.textContent = stringify(value);
|
||||
|
@ -1325,7 +1326,7 @@ export function directiveCreate<T>(
|
|||
index: number, directive: T, directiveDef: DirectiveDef<T>| ComponentDef<T>): T {
|
||||
const instance = baseDirectiveCreate(index, directive, directiveDef);
|
||||
|
||||
ngDevMode && assertNotNull(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
|
||||
ngDevMode && assertDefined(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
|
||||
const tNode = previousOrParentNode.tNode;
|
||||
|
||||
const isComponent = (directiveDef as ComponentDef<T>).template;
|
||||
|
@ -1494,7 +1495,7 @@ function generateInitialInputs(
|
|||
export function createLContainer(
|
||||
parentLNode: LNode, currentView: LView, template?: ComponentTemplate<any>,
|
||||
isForViewContainerRef?: boolean): LContainer {
|
||||
ngDevMode && assertNotNull(parentLNode, 'containers should have a parent');
|
||||
ngDevMode && assertDefined(parentLNode, 'containers should have a parent');
|
||||
return <LContainer>{
|
||||
views: [],
|
||||
nextIndex: isForViewContainerRef ? null : 0,
|
||||
|
@ -1611,7 +1612,7 @@ function refreshDynamicChildren() {
|
|||
const lViewNode = container.views[i];
|
||||
// The directives and pipes are not needed here as an existing view is only being refreshed.
|
||||
const dynamicView = lViewNode.data;
|
||||
ngDevMode && assertNotNull(dynamicView.tView, 'TView must be allocated');
|
||||
ngDevMode && assertDefined(dynamicView.tView, 'TView must be allocated');
|
||||
renderEmbeddedTemplate(lViewNode, dynamicView.tView, dynamicView.context !, renderer);
|
||||
}
|
||||
}
|
||||
|
@ -1699,7 +1700,7 @@ export function embeddedViewStart(viewBlockId: number): RenderFlags {
|
|||
function getOrCreateEmbeddedTView(viewIndex: number, parent: LContainerNode): TView {
|
||||
ngDevMode && assertNodeType(parent, TNodeType.Container);
|
||||
const containerTViews = (parent !.tNode as TContainerNode).tViews as TView[];
|
||||
ngDevMode && assertNotNull(containerTViews, 'TView expected');
|
||||
ngDevMode && assertDefined(containerTViews, 'TView expected');
|
||||
ngDevMode && assertEqual(Array.isArray(containerTViews), true, 'TViews should be in an array');
|
||||
if (viewIndex >= containerTViews.length || containerTViews[viewIndex] == null) {
|
||||
const tView = currentView.tView;
|
||||
|
@ -1773,7 +1774,7 @@ export function componentRefresh<T>(directiveIndex: number, elementIndex: number
|
|||
ngDevMode && assertDataInRange(elementIndex);
|
||||
const element = data ![elementIndex] as LElementNode;
|
||||
ngDevMode && assertNodeType(element, TNodeType.Element);
|
||||
ngDevMode && assertNotNull(element.data, `Component's host node should have an LView attached.`);
|
||||
ngDevMode && assertDefined(element.data, `Component's host node should have an LView attached.`);
|
||||
const hostView = element.data !;
|
||||
|
||||
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
|
||||
|
@ -1928,13 +1929,13 @@ export function projection(
|
|||
function findComponentHost(lView: LView): LElementNode {
|
||||
let viewRootLNode = lView.node;
|
||||
while (viewRootLNode.tNode.type === TNodeType.View) {
|
||||
ngDevMode && assertNotNull(lView.parent, 'lView.parent');
|
||||
ngDevMode && assertDefined(lView.parent, 'lView.parent');
|
||||
lView = lView.parent !;
|
||||
viewRootLNode = lView.node;
|
||||
}
|
||||
|
||||
ngDevMode && assertNodeType(viewRootLNode, TNodeType.Element);
|
||||
ngDevMode && assertNotNull(viewRootLNode.data, 'node.data');
|
||||
ngDevMode && assertDefined(viewRootLNode.data, 'node.data');
|
||||
|
||||
return viewRootLNode as LElementNode;
|
||||
}
|
||||
|
@ -2012,7 +2013,7 @@ export function markViewDirty(view: LView): void {
|
|||
}
|
||||
currentView.flags |= LViewFlags.Dirty;
|
||||
|
||||
ngDevMode && assertNotNull(currentView !.context, 'rootContext');
|
||||
ngDevMode && assertDefined(currentView !.context, 'rootContext');
|
||||
scheduleTick(currentView !.context as RootContext);
|
||||
}
|
||||
|
||||
|
@ -2033,7 +2034,7 @@ export function scheduleTick<T>(rootContext: RootContext) {
|
|||
let res: null|((val: null) => void);
|
||||
rootContext.clean = new Promise<null>((r) => res = r);
|
||||
rootContext.scheduler(() => {
|
||||
tick(rootContext.component);
|
||||
tickRootContext(rootContext);
|
||||
res !(null);
|
||||
rootContext.clean = _CLEAN_PROMISE;
|
||||
});
|
||||
|
@ -2054,11 +2055,18 @@ export function scheduleTick<T>(rootContext: RootContext) {
|
|||
*/
|
||||
export function tick<T>(component: T): void {
|
||||
const rootView = getRootView(component);
|
||||
const rootComponent = (rootView.context as RootContext).component;
|
||||
const hostNode = _getComponentHostLElementNode(rootComponent);
|
||||
const rootContext = rootView.context as RootContext;
|
||||
tickRootContext(rootContext);
|
||||
}
|
||||
|
||||
ngDevMode && assertNotNull(hostNode.data, 'Component host node should be attached to an LView');
|
||||
renderComponentOrTemplate(hostNode, rootView, rootComponent);
|
||||
function tickRootContext(rootContext: RootContext) {
|
||||
for (let i = 0; i < rootContext.components.length; i++) {
|
||||
const rootComponent = rootContext.components[i];
|
||||
const hostNode = _getComponentHostLElementNode(rootComponent);
|
||||
|
||||
ngDevMode && assertDefined(hostNode.data, 'Component host node should be attached to an LView');
|
||||
renderComponentOrTemplate(hostNode, getRootView(rootComponent), rootComponent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2069,7 +2077,7 @@ export function tick<T>(component: T): void {
|
|||
*/
|
||||
|
||||
export function getRootView(component: any): LView {
|
||||
ngDevMode && assertNotNull(component, 'component');
|
||||
ngDevMode && assertDefined(component, 'component');
|
||||
const lElementNode = _getComponentHostLElementNode(component);
|
||||
let lView = lElementNode.view;
|
||||
while (lView.parent) {
|
||||
|
@ -2093,7 +2101,7 @@ export function getRootView(component: any): LView {
|
|||
*/
|
||||
export function detectChanges<T>(component: T): void {
|
||||
const hostNode = _getComponentHostLElementNode(component);
|
||||
ngDevMode && assertNotNull(hostNode.data, 'Component host node should be attached to an LView');
|
||||
ngDevMode && assertDefined(hostNode.data, 'Component host node should be attached to an LView');
|
||||
detectChangesInternal(hostNode.data as LView, hostNode, component);
|
||||
}
|
||||
|
||||
|
@ -2142,7 +2150,7 @@ export function detectChangesInternal<T>(hostView: LView, hostNode: LElementNode
|
|||
* @param component Component to mark as dirty.
|
||||
*/
|
||||
export function markDirty<T>(component: T) {
|
||||
ngDevMode && assertNotNull(component, 'component');
|
||||
ngDevMode && assertDefined(component, 'component');
|
||||
const lElementNode = _getComponentHostLElementNode(component);
|
||||
markViewDirty(lElementNode.view);
|
||||
}
|
||||
|
@ -2388,7 +2396,7 @@ export function load<T>(index: number): T {
|
|||
|
||||
/** Retrieves a value from the `directives` array. */
|
||||
export function loadDirective<T>(index: number): T {
|
||||
ngDevMode && assertNotNull(directives, 'Directives array should be defined if reading a dir.');
|
||||
ngDevMode && assertDefined(directives, 'Directives array should be defined if reading a dir.');
|
||||
ngDevMode && assertDataInRange(index, directives !);
|
||||
return directives ![index];
|
||||
}
|
||||
|
@ -2453,7 +2461,7 @@ export function assertPreviousIsParent() {
|
|||
}
|
||||
|
||||
function assertHasParent() {
|
||||
assertNotNull(getParentLNode(previousOrParentNode), 'previousOrParentNode should have a parent');
|
||||
assertDefined(getParentLNode(previousOrParentNode), 'previousOrParentNode should have a parent');
|
||||
}
|
||||
|
||||
function assertDataInRange(index: number, arr?: any[]) {
|
||||
|
@ -2484,9 +2492,9 @@ export function assertReservedSlotInitialized(slotOffset: number, numSlots: numb
|
|||
}
|
||||
|
||||
export function _getComponentHostLElementNode<T>(component: T): LElementNode {
|
||||
ngDevMode && assertNotNull(component, 'expecting component got null');
|
||||
ngDevMode && assertDefined(component, 'expecting component got null');
|
||||
const lElementNode = (component as any)[NG_HOST_SYMBOL] as LElementNode;
|
||||
ngDevMode && assertNotNull(component, 'object is not a component');
|
||||
ngDevMode && assertDefined(component, 'object is not a component');
|
||||
return lElementNode;
|
||||
}
|
||||
|
||||
|
|
|
@ -420,10 +420,10 @@ export interface RootContext {
|
|||
clean: Promise<null>;
|
||||
|
||||
/**
|
||||
* RootComponent - The component which was instantiated by the call to
|
||||
* RootComponents - The components that were instantiated by the call to
|
||||
* {@link renderComponent}.
|
||||
*/
|
||||
component: {};
|
||||
components: {}[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injector} from '../di/injector';
|
||||
import {StaticProvider} from '../di/provider';
|
||||
import {createInjector} from '../di/r3_injector';
|
||||
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
||||
import {InternalNgModuleRef, NgModuleFactory as viewEngine_NgModuleFactory, NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {Type} from '../type';
|
||||
import {stringify} from '../util';
|
||||
import {assertDefined} from './assert';
|
||||
import {ComponentFactoryResolver} from './component_ref';
|
||||
|
||||
export interface NgModuleType { ngModuleDef: NgModuleDef; }
|
||||
|
||||
export interface NgModuleDef { bootstrap: Type<any>[]; }
|
||||
|
||||
export const COMPONENT_FACTORY_RESOLVER: StaticProvider = {
|
||||
provide: viewEngine_ComponentFactoryResolver,
|
||||
useFactory: () => new ComponentFactoryResolver(),
|
||||
deps: [],
|
||||
};
|
||||
|
||||
export class NgModuleRef<T> extends viewEngine_NgModuleRef<T> implements InternalNgModuleRef<T> {
|
||||
// tslint:disable-next-line:require-internal-with-underscore
|
||||
_bootstrapComponents: Type<any>[] = [];
|
||||
injector: Injector;
|
||||
componentFactoryResolver: viewEngine_ComponentFactoryResolver;
|
||||
instance: T;
|
||||
destroyCbs: (() => void)[]|null = [];
|
||||
|
||||
constructor(ngModuleType: Type<T>, parentInjector: Injector|null) {
|
||||
super();
|
||||
const ngModuleDef = (ngModuleType as any as NgModuleType).ngModuleDef;
|
||||
ngDevMode && assertDefined(
|
||||
ngModuleDef,
|
||||
`NgModule '${stringify(ngModuleType)}' is not a subtype of 'NgModuleType'.`);
|
||||
|
||||
this._bootstrapComponents = ngModuleDef.bootstrap;
|
||||
const additionalProviders: StaticProvider[] = [
|
||||
COMPONENT_FACTORY_RESOLVER, {
|
||||
provide: viewEngine_NgModuleRef,
|
||||
useValue: this,
|
||||
}
|
||||
];
|
||||
this.injector = createInjector(ngModuleType, parentInjector, additionalProviders);
|
||||
this.instance = this.injector.get(ngModuleType);
|
||||
this.componentFactoryResolver = new ComponentFactoryResolver();
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
||||
this.destroyCbs !.forEach(fn => fn());
|
||||
this.destroyCbs = null;
|
||||
}
|
||||
onDestroy(callback: () => void): void {
|
||||
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
||||
this.destroyCbs !.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
export class NgModuleFactory<T> extends viewEngine_NgModuleFactory<T> {
|
||||
constructor(public moduleType: Type<T>) { super(); }
|
||||
|
||||
create(parentInjector: Injector|null): viewEngine_NgModuleRef<T> {
|
||||
return new NgModuleRef(this.moduleType, parentInjector);
|
||||
}
|
||||
}
|
|
@ -6,16 +6,16 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {assertEqual, assertNotNull} from './assert';
|
||||
import {assertDefined, assertEqual} from './assert';
|
||||
import {LNode, TNodeType} from './interfaces/node';
|
||||
|
||||
export function assertNodeType(node: LNode, type: TNodeType) {
|
||||
assertNotNull(node, 'should be called with a node');
|
||||
assertDefined(node, 'should be called with a node');
|
||||
assertEqual(node.tNode.type, type, `should be a ${typeName(type)}`);
|
||||
}
|
||||
|
||||
export function assertNodeOfPossibleTypes(node: LNode, ...types: TNodeType[]) {
|
||||
assertNotNull(node, 'should be called with a node');
|
||||
assertDefined(node, 'should be called with a node');
|
||||
const found = types.some(type => node.tNode.type === type);
|
||||
assertEqual(found, true, `Should be one of ${types.map(typeName).join(', ')}`);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {assertNotNull} from './assert';
|
||||
import {assertDefined} from './assert';
|
||||
import {callHooks} from './hooks';
|
||||
import {LContainer, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
||||
import {LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import './ng_dev_mode';
|
||||
|
||||
import {assertNotNull} from './assert';
|
||||
import {assertDefined} from './assert';
|
||||
import {AttributeMarker, TAttributes, TNode, unusedValueExportToPlacateAjd as unused1} from './interfaces/node';
|
||||
import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags, unusedValueExportToPlacateAjd as unused2} from './interfaces/projection';
|
||||
|
||||
|
@ -36,7 +36,7 @@ function isCssClassMatching(nodeClassAttrVal: string, cssClassToMatch: string):
|
|||
* @returns true if node matches the selector.
|
||||
*/
|
||||
export function isNodeMatchingSelector(tNode: TNode, selector: CssSelector): boolean {
|
||||
ngDevMode && assertNotNull(selector[0], 'Selector should have a tag name');
|
||||
ngDevMode && assertDefined(selector[0], 'Selector should have a tag name');
|
||||
|
||||
let mode: SelectorFlags = SelectorFlags.ELEMENT;
|
||||
const nodeAttrs = tNode.attrs !;
|
||||
|
|
|
@ -15,7 +15,7 @@ import {QueryList as viewEngine_QueryList} from '../linker/query_list';
|
|||
import {Type} from '../type';
|
||||
import {getSymbolIterator} from '../util';
|
||||
|
||||
import {assertEqual, assertNotNull} from './assert';
|
||||
import {assertDefined, assertEqual} from './assert';
|
||||
import {ReadFromInjectorFn, getOrCreateNodeInjectorForNode} from './di';
|
||||
import {assertPreviousIsParent, getCurrentQueries, store, storeCleanupWithContext} from './instructions';
|
||||
import {DirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
||||
|
@ -163,7 +163,7 @@ export class LQueries_ implements LQueries {
|
|||
let query = this.deep;
|
||||
while (query) {
|
||||
ngDevMode &&
|
||||
assertNotNull(
|
||||
assertDefined(
|
||||
query.containerValues, 'View queries need to have a pointer to container values.');
|
||||
query.containerValues !.splice(index, 0, query.values);
|
||||
query = query.next;
|
||||
|
@ -179,7 +179,7 @@ export class LQueries_ implements LQueries {
|
|||
let query = this.deep;
|
||||
while (query) {
|
||||
ngDevMode &&
|
||||
assertNotNull(
|
||||
assertDefined(
|
||||
query.containerValues, 'View queries need to have a pointer to container values.');
|
||||
const removed = query.containerValues !.splice(index, 1);
|
||||
|
||||
|
@ -273,7 +273,7 @@ function add(query: LQuery<any>| null, node: LNode) {
|
|||
if (directiveIdx !== null) {
|
||||
// a node is matching a predicate - determine what to read
|
||||
// note that queries using name selector must specify read strategy
|
||||
ngDevMode && assertNotNull(predicate.read, 'the node should have a predicate');
|
||||
ngDevMode && assertDefined(predicate.read, 'the node should have a predicate');
|
||||
const result = readFromNodeInjector(nodeInjector, node, predicate.read !, directiveIdx);
|
||||
if (result !== null) {
|
||||
addMatch(query, result);
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ApplicationRef} from '../application_ref';
|
||||
import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
||||
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef} from '../linker/view_ref';
|
||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
||||
|
||||
import {checkNoChanges, detectChanges, markViewDirty, storeCleanupFn} from './instructions';
|
||||
import {ComponentTemplate} from './interfaces/definition';
|
||||
|
@ -15,7 +17,10 @@ import {LViewNode} from './interfaces/node';
|
|||
import {LView, LViewFlags} from './interfaces/view';
|
||||
import {destroyLView} from './node_manipulation';
|
||||
|
||||
export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T> {
|
||||
export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_InternalViewRef,
|
||||
viewEngine_ChangeDetectorRef {
|
||||
private _appRef: ApplicationRef|null;
|
||||
|
||||
context: T;
|
||||
rootNodes: any[];
|
||||
|
||||
|
@ -210,6 +215,10 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T> {
|
|||
* introduce other changes.
|
||||
*/
|
||||
checkNoChanges(): void { checkNoChanges(this.context); }
|
||||
|
||||
detachFromAppRef() { this._appRef = null; }
|
||||
|
||||
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ApplicationModule, ApplicationRef, DoCheck, InjectFlags, InjectorType, Input, OnInit, PlatformRef, TestabilityRegistry, Type, defineInjector, inject, ɵE as elementStart, ɵNgModuleDef as NgModuleDef, ɵRenderFlags as RenderFlags, ɵT as text, ɵdefineComponent as defineComponent, ɵe as elementEnd, ɵi1 as interpolation1, ɵt as textBinding} from '@angular/core';
|
||||
import {getTestBed, withBody} from '@angular/core/testing';
|
||||
import {BrowserModule, EVENT_MANAGER_PLUGINS, platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
import {BROWSER_MODULE_PROVIDERS} from '../../platform-browser/src/browser';
|
||||
import {APPLICATION_MODULE_PROVIDERS} from '../src/application_module';
|
||||
import {NgModuleFactory} from '../src/render3/ng_module_ref';
|
||||
|
||||
describe('ApplicationRef bootstrap', () => {
|
||||
class HelloWorldComponent implements OnInit, DoCheck {
|
||||
log: string[] = [];
|
||||
name = 'World';
|
||||
static ngComponentDef = defineComponent({
|
||||
type: HelloWorldComponent,
|
||||
selectors: [['hello-world']],
|
||||
factory: () => new HelloWorldComponent(),
|
||||
template: function(rf: RenderFlags, ctx: HelloWorldComponent): void {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'div');
|
||||
text(1);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(1, interpolation1('Hello ', ctx.name, ''));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ngOnInit(): void { this.log.push('OnInit'); }
|
||||
|
||||
ngDoCheck(): void { this.log.push('DoCheck'); }
|
||||
}
|
||||
|
||||
class MyAppModule {
|
||||
static ngInjectorDef =
|
||||
defineInjector({factory: () => new MyAppModule(), imports: [BrowserModule]});
|
||||
static ngModuleDef = defineNgModule({bootstrap: [HelloWorldComponent]});
|
||||
}
|
||||
|
||||
it('should bootstrap hello world', withBody('<hello-world></hello-world>', async() => {
|
||||
const MyAppModuleFactory = new NgModuleFactory(MyAppModule);
|
||||
const moduleRef =
|
||||
await getTestBed().platform.bootstrapModuleFactory(MyAppModuleFactory, {ngZone: 'noop'});
|
||||
const appRef = moduleRef.injector.get(ApplicationRef);
|
||||
const helloWorldComponent = appRef.components[0].instance as HelloWorldComponent;
|
||||
expect(document.body.innerHTML).toEqual('<hello-world><div>Hello World</div></hello-world>');
|
||||
// TODO(jasonaden): Get with Kara on lifecycle hooks
|
||||
// expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck']);
|
||||
helloWorldComponent.name = 'Mundo';
|
||||
appRef.tick();
|
||||
expect(document.body.innerHTML).toEqual('<hello-world><div>Hello Mundo</div></hello-world>');
|
||||
// TODO(jasonaden): Get with Kara on lifecycle hooks
|
||||
// expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']);
|
||||
|
||||
// Cleanup TestabilityRegistry
|
||||
const registry: TestabilityRegistry = getTestBed().get(TestabilityRegistry);
|
||||
registry.unregisterAllApplications();
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
// These go away when Compiler is ready
|
||||
|
||||
(BrowserModule as any as InjectorType<BrowserModule>).ngInjectorDef = defineInjector({
|
||||
factory: function BrowserModule_Factory() {
|
||||
return new BrowserModule(inject(BrowserModule, InjectFlags.Optional | InjectFlags.SkipSelf));
|
||||
},
|
||||
imports: [ApplicationModule],
|
||||
providers: BROWSER_MODULE_PROVIDERS
|
||||
});
|
||||
|
||||
(ApplicationModule as any as InjectorType<ApplicationModule>).ngInjectorDef = defineInjector({
|
||||
factory: function ApplicationModule_Factory() {
|
||||
return new ApplicationModule(inject(ApplicationRef));
|
||||
},
|
||||
providers: APPLICATION_MODULE_PROVIDERS
|
||||
});
|
||||
|
||||
export function defineNgModule({bootstrap}: {bootstrap?: Type<any>[]}): NgModuleDef {
|
||||
return {
|
||||
bootstrap: bootstrap || [],
|
||||
};
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
|
@ -56,6 +56,9 @@
|
|||
{
|
||||
"name": "createLView"
|
||||
},
|
||||
{
|
||||
"name": "createRootContext"
|
||||
},
|
||||
{
|
||||
"name": "createTNode"
|
||||
},
|
||||
|
|
|
@ -2418,7 +2418,7 @@
|
|||
"name": "assertInterpolationSymbols"
|
||||
},
|
||||
{
|
||||
"name": "assertNotNull$1"
|
||||
"name": "assertNotNull"
|
||||
},
|
||||
{
|
||||
"name": "assertPlatform"
|
||||
|
|
|
@ -266,6 +266,9 @@
|
|||
{
|
||||
"name": "createOutput"
|
||||
},
|
||||
{
|
||||
"name": "createRootContext"
|
||||
},
|
||||
{
|
||||
"name": "createTNode"
|
||||
},
|
||||
|
@ -657,7 +660,7 @@
|
|||
"name": "throwMultipleComponentError"
|
||||
},
|
||||
{
|
||||
"name": "tick"
|
||||
"name": "tickRootContext"
|
||||
},
|
||||
{
|
||||
"name": "trackByIdentity"
|
||||
|
|
|
@ -41,6 +41,8 @@ describe('InjectorDef-based createInjector()', () => {
|
|||
|
||||
const STATIC_TOKEN = new InjectionToken<StaticService>('STATIC_TOKEN');
|
||||
|
||||
const LOCALE = new InjectionToken<string[]>('LOCALE');
|
||||
|
||||
class ServiceWithDep {
|
||||
constructor(readonly service: Service) {}
|
||||
|
||||
|
@ -50,6 +52,15 @@ describe('InjectorDef-based createInjector()', () => {
|
|||
});
|
||||
}
|
||||
|
||||
class ServiceWithMultiDep {
|
||||
constructor(readonly locale: string[]) {}
|
||||
|
||||
static ngInjectableDef = defineInjectable({
|
||||
providedIn: null,
|
||||
factory: () => new ServiceWithMultiDep(inject(LOCALE)),
|
||||
});
|
||||
}
|
||||
|
||||
class ServiceTwo {
|
||||
static ngInjectableDef = defineInjectable({
|
||||
providedIn: null,
|
||||
|
@ -112,6 +123,9 @@ describe('InjectorDef-based createInjector()', () => {
|
|||
imports: [IntermediateModule],
|
||||
providers: [
|
||||
ServiceWithDep,
|
||||
ServiceWithMultiDep,
|
||||
{provide: LOCALE, multi: true, useValue: 'en'},
|
||||
{provide: LOCALE, multi: true, useValue: 'es'},
|
||||
Service,
|
||||
{provide: SERVICE_TOKEN, useExisting: Service},
|
||||
CircularA,
|
||||
|
@ -168,6 +182,12 @@ describe('InjectorDef-based createInjector()', () => {
|
|||
expect(instance.service).toBe(injector.get(Service));
|
||||
});
|
||||
|
||||
it('injects a service with dependencies on multi-providers', () => {
|
||||
const instance = injector.get(ServiceWithMultiDep);
|
||||
expect(instance instanceof ServiceWithMultiDep);
|
||||
expect(instance.locale).toEqual(['en', 'es']);
|
||||
});
|
||||
|
||||
it('injects a token with useExisting', () => {
|
||||
const instance = injector.get(SERVICE_TOKEN);
|
||||
expect(instance).toBe(injector.get(Service));
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
*/
|
||||
|
||||
|
||||
import {ComponentFactory, DoCheck, ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
|
||||
import {DoCheck, ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
|
||||
import {getRenderedText} from '../../src/render3/component';
|
||||
import {LifecycleHooksFeature, defineComponent, directiveInject, markDirty} from '../../src/render3/index';
|
||||
import {ComponentFactory, LifecycleHooksFeature, defineComponent, directiveInject, markDirty} from '../../src/render3/index';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, text, textBinding, tick} from '../../src/render3/instructions';
|
||||
import {ComponentDef, DirectiveDef, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {createRendererType2} from '../../src/view/index';
|
||||
|
@ -364,4 +364,27 @@ describe('recursive components', () => {
|
|||
tick(comp);
|
||||
expect(events).toEqual(['check6', 'check2', 'check0', 'check1', 'check5', 'check3', 'check4']);
|
||||
});
|
||||
|
||||
it('should map inputs minified & unminified names', async() => {
|
||||
class TestInputsComponent {
|
||||
minifiedName: string;
|
||||
static ngComponentDef = defineComponent({
|
||||
type: TestInputsComponent,
|
||||
selectors: [['test-inputs']],
|
||||
inputs: {minifiedName: 'unminifiedName'},
|
||||
factory: () => new TestInputsComponent(),
|
||||
template: function(rf: RenderFlags, ctx: TestInputsComponent): void {
|
||||
// Template not needed for this test
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const testInputsComponentFactory = new ComponentFactory(TestInputsComponent.ngComponentDef);
|
||||
|
||||
expect([
|
||||
{propName: 'minifiedName', templateName: 'unminifiedName'}
|
||||
]).toEqual(testInputsComponentFactory.inputs);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -135,7 +135,7 @@ export class ComponentFixture<T> extends BaseFixture {
|
|||
// Fixtures above are preferred way of testing Components and Templates
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
export const document = ((global || window) as any).document;
|
||||
export const document = ((typeof global == 'object' && global || window) as any).document;
|
||||
export let containerEl: HTMLElement = null !;
|
||||
let host: LElementNode|null;
|
||||
const isRenderer2 =
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {CommonModule, PlatformLocation, ɵPLATFORM_BROWSER_ID as PLATFORM_BROWSER_ID} from '@angular/common';
|
||||
import {APP_ID, ApplicationModule, ErrorHandler, ModuleWithProviders, NgModule, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, RendererFactory2, RootRenderer, Sanitizer, SkipSelf, StaticProvider, Testability, createPlatformFactory, platformCore, ɵAPP_ROOT as APP_ROOT} from '@angular/core';
|
||||
import {APP_ID, ApplicationModule, ClassProvider, ConstructorSansProvider, ErrorHandler, ExistingProvider, FactoryProvider, Inject, InjectionToken, ModuleWithProviders, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, RendererFactory2, RootRenderer, Sanitizer, SkipSelf, StaticProvider, Testability, TypeProvider, ValueProvider, createPlatformFactory, platformCore, ɵAPP_ROOT as APP_ROOT} from '@angular/core';
|
||||
|
||||
import {BrowserDomAdapter} from './browser/browser_adapter';
|
||||
import {BrowserPlatformLocation} from './browser/location/browser_platform_location';
|
||||
|
@ -20,7 +20,7 @@ import {getDOM} from './dom/dom_adapter';
|
|||
import {DomRendererFactory2} from './dom/dom_renderer';
|
||||
import {DOCUMENT} from './dom/dom_tokens';
|
||||
import {DomEventsPlugin} from './dom/events/dom_events';
|
||||
import {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager';
|
||||
import {EVENT_MANAGER_PLUGINS, EventManager, EventManagerPlugin} from './dom/events/event_manager';
|
||||
import {HAMMER_GESTURE_CONFIG, HammerGestureConfig, HammerGesturesPlugin} from './dom/events/hammer_gestures';
|
||||
import {KeyEventsPlugin} from './dom/events/key_events';
|
||||
import {DomSharedStylesHost, SharedStylesHost} from './dom/shared_styles_host';
|
||||
|
@ -60,34 +60,47 @@ export function _document(): any {
|
|||
return document;
|
||||
}
|
||||
|
||||
export const BROWSER_MODULE_PROVIDERS: StaticProvider[] = [
|
||||
BROWSER_SANITIZATION_PROVIDERS,
|
||||
{provide: APP_ROOT, useValue: true},
|
||||
{provide: ErrorHandler, useFactory: errorHandler, deps: []},
|
||||
{
|
||||
provide: EVENT_MANAGER_PLUGINS,
|
||||
useClass: DomEventsPlugin,
|
||||
multi: true,
|
||||
deps: [DOCUMENT, NgZone, PLATFORM_ID]
|
||||
},
|
||||
{provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true, deps: [DOCUMENT]},
|
||||
{
|
||||
provide: EVENT_MANAGER_PLUGINS,
|
||||
useClass: HammerGesturesPlugin,
|
||||
multi: true,
|
||||
deps: [DOCUMENT, HAMMER_GESTURE_CONFIG]
|
||||
},
|
||||
{provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig, deps: []},
|
||||
{
|
||||
provide: DomRendererFactory2,
|
||||
useClass: DomRendererFactory2,
|
||||
deps: [EventManager, DomSharedStylesHost]
|
||||
},
|
||||
{provide: RendererFactory2, useExisting: DomRendererFactory2},
|
||||
{provide: SharedStylesHost, useExisting: DomSharedStylesHost},
|
||||
{provide: DomSharedStylesHost, useClass: DomSharedStylesHost, deps: [DOCUMENT]},
|
||||
{provide: Testability, useClass: Testability, deps: [NgZone]},
|
||||
{provide: EventManager, useClass: EventManager, deps: [EVENT_MANAGER_PLUGINS, NgZone]},
|
||||
ELEMENT_PROBE_PROVIDERS,
|
||||
{provide: Meta, useClass: Meta, deps: [DOCUMENT]},
|
||||
{provide: Title, useClass: Title, deps: [DOCUMENT]},
|
||||
];
|
||||
|
||||
/**
|
||||
* The ng module for the browser.
|
||||
*
|
||||
*
|
||||
*/
|
||||
@NgModule({
|
||||
providers: [
|
||||
BROWSER_SANITIZATION_PROVIDERS,
|
||||
{provide: APP_ROOT, useValue: true},
|
||||
{provide: ErrorHandler, useFactory: errorHandler, deps: []},
|
||||
{provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true},
|
||||
{provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true},
|
||||
{provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true},
|
||||
{provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig},
|
||||
DomRendererFactory2,
|
||||
{provide: RendererFactory2, useExisting: DomRendererFactory2},
|
||||
{provide: SharedStylesHost, useExisting: DomSharedStylesHost},
|
||||
DomSharedStylesHost,
|
||||
Testability,
|
||||
EventManager,
|
||||
ELEMENT_PROBE_PROVIDERS,
|
||||
Meta,
|
||||
Title,
|
||||
],
|
||||
exports: [CommonModule, ApplicationModule]
|
||||
})
|
||||
@NgModule({providers: BROWSER_MODULE_PROVIDERS, exports: [CommonModule, ApplicationModule]})
|
||||
export class BrowserModule {
|
||||
constructor(@Optional() @SkipSelf() parentModule: BrowserModule) {
|
||||
constructor(@Optional() @SkipSelf() @Inject(BrowserModule) parentModule: BrowserModule|null) {
|
||||
if (parentModule) {
|
||||
throw new Error(
|
||||
`BrowserModule has already been loaded. If you need access to common directives such as NgIf and NgFor from a lazy loaded module, import CommonModule instead.`);
|
||||
|
|
|
@ -143,6 +143,11 @@ export declare abstract class ComponentRef<C> {
|
|||
abstract onDestroy(callback: Function): void;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export interface ConstructorSansProvider {
|
||||
deps?: any[];
|
||||
}
|
||||
|
||||
export declare const ContentChild: ContentChildDecorator;
|
||||
|
||||
export interface ContentChildDecorator {
|
||||
|
@ -168,7 +173,7 @@ export interface ContentChildrenDecorator {
|
|||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare function createInjector(defType: any, parent?: Injector | null): Injector;
|
||||
export declare function createInjector(defType: any, parent?: Injector | null, additionalProviders?: StaticProvider[] | null): Injector;
|
||||
|
||||
/** @experimental */
|
||||
export declare function createPlatform(injector: Injector): PlatformRef;
|
||||
|
@ -609,7 +614,7 @@ export interface Predicate<T> {
|
|||
(value: T): boolean;
|
||||
}
|
||||
|
||||
export declare type Provider = TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any[];
|
||||
export declare type Provider = TypeProvider | ValueProvider | ClassProvider | ConstructorProvider | ExistingProvider | FactoryProvider | any[];
|
||||
|
||||
export declare abstract class Query {
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export declare class BrowserModule {
|
||||
constructor(parentModule: BrowserModule);
|
||||
constructor(parentModule: BrowserModule | null);
|
||||
/** @experimental */ static withServerTransition(params: {
|
||||
appId: string;
|
||||
}): ModuleWithProviders;
|
||||
|
|
Loading…
Reference in New Issue