feat: tree-shakeable providers API updates (#22655)
Rename @Injectable({scope -> providedIn}). Instead of {providedIn: APP_ROOT_SCOPE}, accept {providedIn: 'root'}. Also, {providedIn: null} implies the injectable should not be added to any scope. PR Close #22655
This commit is contained in:
parent
21e44c6ba9
commit
db56836425
|
@ -3,7 +3,7 @@ import {Injectable, NgModule} from '@angular/core';
|
||||||
@NgModule({})
|
@NgModule({})
|
||||||
export class Lib1Module {}
|
export class Lib1Module {}
|
||||||
|
|
||||||
@Injectable({scope: Lib1Module})
|
@Injectable({providedIn: Lib1Module})
|
||||||
export class Service {
|
export class Service {
|
||||||
static instance = 0;
|
static instance = 0;
|
||||||
readonly instance = Service.instance++;
|
readonly instance = Service.instance++;
|
||||||
|
|
|
@ -35,7 +35,7 @@ export class AppComponent {
|
||||||
export class DepAppModule {
|
export class DepAppModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({scope: DepAppModule})
|
@Injectable({providedIn: DepAppModule})
|
||||||
export class ShakeableService {
|
export class ShakeableService {
|
||||||
constructor(readonly normal: NormalService) {}
|
constructor(readonly normal: NormalService) {}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {APP_ROOT_SCOPE, Component, Injectable, NgModule, Optional, Self} from '@angular/core';
|
import {Component, Injectable, NgModule, Optional, Self} from '@angular/core';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {ServerModule} from '@angular/platform-server';
|
import {ServerModule} from '@angular/platform-server';
|
||||||
import {RouterModule} from '@angular/router';
|
import {RouterModule} from '@angular/router';
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {APP_ROOT_SCOPE, Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
scope: APP_ROOT_SCOPE,
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class Service {
|
export class Service {
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,6 @@ export class AppComponent {
|
||||||
export class SelfAppModule {
|
export class SelfAppModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({scope: SelfAppModule})
|
@Injectable({providedIn: SelfAppModule})
|
||||||
export class ShakeableService {
|
export class ShakeableService {
|
||||||
}
|
}
|
|
@ -32,7 +32,7 @@ export class AppComponent {
|
||||||
export class StringAppModule {
|
export class StringAppModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({scope: StringAppModule})
|
@Injectable({providedIn: StringAppModule})
|
||||||
export class Service {
|
export class Service {
|
||||||
constructor(@Inject('someStringToken') readonly data: string) {}
|
constructor(@Inject('someStringToken') readonly data: string) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class TokenModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TOKEN = new InjectionToken('test', {
|
export const TOKEN = new InjectionToken('test', {
|
||||||
scope: TokenModule,
|
providedIn: TokenModule,
|
||||||
factory: () => new Service(inject(Dep)),
|
factory: () => new Service(inject(Dep)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ ts_library(
|
||||||
deps = [
|
deps = [
|
||||||
"//packages/compiler-cli/integrationtest/bazel/injectable_def/app",
|
"//packages/compiler-cli/integrationtest/bazel/injectable_def/app",
|
||||||
"//packages/core",
|
"//packages/core",
|
||||||
|
"//packages/core/testing",
|
||||||
"//packages/platform-server",
|
"//packages/platform-server",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {enableProdMode} from '@angular/core';
|
import {Component, Injectable, NgModule} from '@angular/core';
|
||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {renderModuleFactory} from '@angular/platform-server';
|
import {renderModuleFactory} from '@angular/platform-server';
|
||||||
import {BasicAppModuleNgFactory} from 'app_built/src/basic.ngfactory';
|
import {BasicAppModuleNgFactory} from 'app_built/src/basic.ngfactory';
|
||||||
import {DepAppModuleNgFactory} from 'app_built/src/dep.ngfactory';
|
import {DepAppModuleNgFactory} from 'app_built/src/dep.ngfactory';
|
||||||
|
@ -16,8 +17,6 @@ import {SelfAppModuleNgFactory} from 'app_built/src/self.ngfactory';
|
||||||
import {StringAppModuleNgFactory} from 'app_built/src/string.ngfactory';
|
import {StringAppModuleNgFactory} from 'app_built/src/string.ngfactory';
|
||||||
import {TokenAppModuleNgFactory} from 'app_built/src/token.ngfactory';
|
import {TokenAppModuleNgFactory} from 'app_built/src/token.ngfactory';
|
||||||
|
|
||||||
enableProdMode();
|
|
||||||
|
|
||||||
describe('ngInjectableDef Bazel Integration', () => {
|
describe('ngInjectableDef Bazel Integration', () => {
|
||||||
it('works in AOT', done => {
|
it('works in AOT', done => {
|
||||||
renderModuleFactory(BasicAppModuleNgFactory, {
|
renderModuleFactory(BasicAppModuleNgFactory, {
|
||||||
|
@ -88,4 +87,41 @@ describe('ngInjectableDef Bazel Integration', () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('allows provider override in JIT for root-scoped @Injectables', () => {
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
useValue: new Service('default'),
|
||||||
|
})
|
||||||
|
class Service {
|
||||||
|
constructor(readonly value: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
TestBed.overrideProvider(Service, {useValue: new Service('overridden')});
|
||||||
|
|
||||||
|
expect(TestBed.get(Service).value).toEqual('overridden');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows provider override in JIT for module-scoped @Injectables', () => {
|
||||||
|
|
||||||
|
@NgModule()
|
||||||
|
class Module {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: Module,
|
||||||
|
useValue: new Service('default'),
|
||||||
|
})
|
||||||
|
class Service {
|
||||||
|
constructor(readonly value: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [Module],
|
||||||
|
});
|
||||||
|
TestBed.overrideProvider(Service, {useValue: new Service('overridden')});
|
||||||
|
|
||||||
|
expect(TestBed.get(Service).value).toEqual('overridden');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class Lib1Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
scope: Lib1Module,
|
providedIn: Lib1Module,
|
||||||
})
|
})
|
||||||
export class Service {
|
export class Service {
|
||||||
static instanceCount = 0;
|
static instanceCount = 0;
|
||||||
|
|
|
@ -2058,13 +2058,13 @@ describe('ngc transformer command-line', () => {
|
||||||
import {Module} from './module';
|
import {Module} from './module';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
scope: Module,
|
providedIn: Module,
|
||||||
})
|
})
|
||||||
export class Service {}
|
export class Service {}
|
||||||
`);
|
`);
|
||||||
expect(source).toMatch(/ngInjectableDef = .+\.defineInjectable\(/);
|
expect(source).toMatch(/ngInjectableDef = .+\.defineInjectable\(/);
|
||||||
expect(source).toMatch(/ngInjectableDef.*token: Service/);
|
expect(source).toMatch(/ngInjectableDef.*token: Service/);
|
||||||
expect(source).toMatch(/ngInjectableDef.*scope: .+\.Module/);
|
expect(source).toMatch(/ngInjectableDef.*providedIn: .+\.Module/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ngInjectableDef in es5 mode is annotated @nocollapse when closure options are enabled',
|
it('ngInjectableDef in es5 mode is annotated @nocollapse when closure options are enabled',
|
||||||
|
@ -2081,7 +2081,7 @@ describe('ngc transformer command-line', () => {
|
||||||
import {Module} from './module';
|
import {Module} from './module';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
scope: Module,
|
providedIn: Module,
|
||||||
})
|
})
|
||||||
export class Service {}
|
export class Service {}
|
||||||
`);
|
`);
|
||||||
|
@ -2096,7 +2096,7 @@ describe('ngc transformer command-line', () => {
|
||||||
export const CONST_SERVICE: Service = null;
|
export const CONST_SERVICE: Service = null;
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
scope: Module,
|
providedIn: Module,
|
||||||
useValue: CONST_SERVICE
|
useValue: CONST_SERVICE
|
||||||
})
|
})
|
||||||
export class Service {}
|
export class Service {}
|
||||||
|
@ -2113,7 +2113,7 @@ describe('ngc transformer command-line', () => {
|
||||||
export class Existing {}
|
export class Existing {}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
scope: Module,
|
providedIn: Module,
|
||||||
useExisting: Existing,
|
useExisting: Existing,
|
||||||
})
|
})
|
||||||
export class Service {}
|
export class Service {}
|
||||||
|
@ -2130,7 +2130,7 @@ describe('ngc transformer command-line', () => {
|
||||||
export class Existing {}
|
export class Existing {}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
scope: Module,
|
providedIn: Module,
|
||||||
useFactory: (existing: Existing|null) => new Service(existing),
|
useFactory: (existing: Existing|null) => new Service(existing),
|
||||||
deps: [[new Optional(), Existing]],
|
deps: [[new Optional(), Existing]],
|
||||||
})
|
})
|
||||||
|
@ -2150,7 +2150,7 @@ describe('ngc transformer command-line', () => {
|
||||||
export class Existing {}
|
export class Existing {}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
scope: Module,
|
providedIn: Module,
|
||||||
useFactory: (existing: Existing) => new Service(existing),
|
useFactory: (existing: Existing) => new Service(existing),
|
||||||
deps: [[new SkipSelf(), Existing]],
|
deps: [[new SkipSelf(), Existing]],
|
||||||
})
|
})
|
||||||
|
@ -2166,10 +2166,10 @@ describe('ngc transformer command-line', () => {
|
||||||
import {Inject, Injectable, InjectionToken} from '@angular/core';
|
import {Inject, Injectable, InjectionToken} from '@angular/core';
|
||||||
import {Module} from './module';
|
import {Module} from './module';
|
||||||
|
|
||||||
export const TOKEN = new InjectionToken('desc', {scope: Module, factory: () => true});
|
export const TOKEN = new InjectionToken('desc', {providedIn: Module, factory: () => true});
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
scope: Module,
|
providedIn: Module,
|
||||||
})
|
})
|
||||||
export class Service {
|
export class Service {
|
||||||
constructor(@Inject(TOKEN) value: boolean) {}
|
constructor(@Inject(TOKEN) value: boolean) {}
|
||||||
|
@ -2177,7 +2177,7 @@ describe('ngc transformer command-line', () => {
|
||||||
`);
|
`);
|
||||||
expect(source).toMatch(/ngInjectableDef = .+\.defineInjectable\(/);
|
expect(source).toMatch(/ngInjectableDef = .+\.defineInjectable\(/);
|
||||||
expect(source).toMatch(/ngInjectableDef.*token: Service/);
|
expect(source).toMatch(/ngInjectableDef.*token: Service/);
|
||||||
expect(source).toMatch(/ngInjectableDef.*scope: .+\.Module/);
|
expect(source).toMatch(/ngInjectableDef.*providedIn: .+\.Module/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates exports.* references when outputting commonjs', () => {
|
it('generates exports.* references when outputting commonjs', () => {
|
||||||
|
@ -2189,15 +2189,15 @@ describe('ngc transformer command-line', () => {
|
||||||
"files": ["service.ts"]
|
"files": ["service.ts"]
|
||||||
}`);
|
}`);
|
||||||
const source = compileService(`
|
const source = compileService(`
|
||||||
import {Inject, Injectable, InjectionToken, APP_ROOT_SCOPE} from '@angular/core';
|
import {Inject, Injectable, InjectionToken} from '@angular/core';
|
||||||
import {Module} from './module';
|
import {Module} from './module';
|
||||||
|
|
||||||
export const TOKEN = new InjectionToken<string>('test token', {
|
export const TOKEN = new InjectionToken<string>('test token', {
|
||||||
scope: APP_ROOT_SCOPE,
|
providedIn: 'root',
|
||||||
factory: () => 'this is a test',
|
factory: () => 'this is a test',
|
||||||
});
|
});
|
||||||
|
|
||||||
@Injectable({scope: APP_ROOT_SCOPE})
|
@Injectable({providedIn: 'root'})
|
||||||
export class Service {
|
export class Service {
|
||||||
constructor(@Inject(TOKEN) token: any) {}
|
constructor(@Inject(TOKEN) token: any) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,7 @@ export interface CompileInjectableMetadata {
|
||||||
symbol: StaticSymbol;
|
symbol: StaticSymbol;
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
|
|
||||||
module?: StaticSymbol;
|
providedIn?: StaticSymbol;
|
||||||
|
|
||||||
useValue?: any;
|
useValue?: any;
|
||||||
useClass?: StaticSymbol;
|
useClass?: StaticSymbol;
|
||||||
|
|
|
@ -127,7 +127,7 @@ export interface ModuleWithProviders {
|
||||||
providers?: Provider[];
|
providers?: Provider[];
|
||||||
}
|
}
|
||||||
export interface Injectable {
|
export interface Injectable {
|
||||||
scope?: Type|any;
|
providedIn?: Type|'root'|any;
|
||||||
useClass?: Type|any;
|
useClass?: Type|any;
|
||||||
useExisting?: Type|any;
|
useExisting?: Type|any;
|
||||||
useValue?: any;
|
useValue?: any;
|
||||||
|
|
|
@ -89,16 +89,24 @@ export class InjectableCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
injectableDef(injectable: CompileInjectableMetadata, ctx: OutputContext): o.Expression {
|
injectableDef(injectable: CompileInjectableMetadata, ctx: OutputContext): o.Expression {
|
||||||
|
let providedIn: o.Expression = o.NULL_EXPR;
|
||||||
|
if (injectable.providedIn) {
|
||||||
|
if (typeof injectable.providedIn === 'string') {
|
||||||
|
providedIn = o.literal(injectable.providedIn);
|
||||||
|
} else {
|
||||||
|
providedIn = ctx.importExpr(injectable.providedIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
const def: MapLiteral = [
|
const def: MapLiteral = [
|
||||||
mapEntry('factory', this.factoryFor(injectable, ctx)),
|
mapEntry('factory', this.factoryFor(injectable, ctx)),
|
||||||
mapEntry('token', ctx.importExpr(injectable.type.reference)),
|
mapEntry('token', ctx.importExpr(injectable.type.reference)),
|
||||||
mapEntry('scope', ctx.importExpr(injectable.module !)),
|
mapEntry('providedIn', providedIn),
|
||||||
];
|
];
|
||||||
return o.importExpr(Identifiers.defineInjectable).callFn([o.literalMap(def)]);
|
return o.importExpr(Identifiers.defineInjectable).callFn([o.literalMap(def)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
compile(injectable: CompileInjectableMetadata, ctx: OutputContext): void {
|
compile(injectable: CompileInjectableMetadata, ctx: OutputContext): void {
|
||||||
if (injectable.module) {
|
if (injectable.providedIn) {
|
||||||
const className = identifierName(injectable.type) !;
|
const className = identifierName(injectable.type) !;
|
||||||
const clazz = new o.ClassStmt(
|
const clazz = new o.ClassStmt(
|
||||||
className, null,
|
className, null,
|
||||||
|
|
|
@ -801,7 +801,7 @@ export class CompileMetadataResolver {
|
||||||
return {
|
return {
|
||||||
symbol: type,
|
symbol: type,
|
||||||
type: typeMetadata,
|
type: typeMetadata,
|
||||||
module: meta.scope || undefined,
|
providedIn: meta.providedIn,
|
||||||
useValue: meta.useValue,
|
useValue: meta.useValue,
|
||||||
useClass: meta.useClass,
|
useClass: meta.useClass,
|
||||||
useExisting: meta.useExisting,
|
useExisting: meta.useExisting,
|
||||||
|
|
|
@ -13,6 +13,7 @@ export {isListLikeIterable as ɵisListLikeIterable} from './change_detection/cha
|
||||||
export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants';
|
export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants';
|
||||||
export {Console as ɵConsole} from './console';
|
export {Console as ɵConsole} from './console';
|
||||||
export {setCurrentInjector as ɵsetCurrentInjector} from './di/injector';
|
export {setCurrentInjector as ɵsetCurrentInjector} from './di/injector';
|
||||||
|
export {APP_ROOT as ɵAPP_ROOT} from './di/scope';
|
||||||
export {ComponentFactory as ɵComponentFactory} from './linker/component_factory';
|
export {ComponentFactory as ɵComponentFactory} from './linker/component_factory';
|
||||||
export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver';
|
export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver';
|
||||||
export {ReflectionCapabilities as ɵReflectionCapabilities} from './reflection/reflection_capabilities';
|
export {ReflectionCapabilities as ɵReflectionCapabilities} from './reflection/reflection_capabilities';
|
||||||
|
|
|
@ -23,4 +23,3 @@ export {StaticProvider, ValueProvider, ExistingProvider, FactoryProvider, Provid
|
||||||
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './di/reflective_provider';
|
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './di/reflective_provider';
|
||||||
export {ReflectiveKey} from './di/reflective_key';
|
export {ReflectiveKey} from './di/reflective_key';
|
||||||
export {InjectionToken} from './di/injection_token';
|
export {InjectionToken} from './di/injection_token';
|
||||||
export {APP_ROOT_SCOPE} from './di/scope';
|
|
||||||
|
|
|
@ -55,9 +55,9 @@ export interface InjectableDecorator {
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
(): any;
|
(): any;
|
||||||
(options?: {scope: Type<any>}&InjectableProvider): any;
|
(options?: {providedIn: Type<any>| 'root' | null}&InjectableProvider): any;
|
||||||
new (): Injectable;
|
new (): Injectable;
|
||||||
new (options?: {scope: Type<any>}&InjectableProvider): Injectable;
|
new (options?: {providedIn: Type<any>| 'root' | null}&InjectableProvider): Injectable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +66,7 @@ export interface InjectableDecorator {
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export interface Injectable {
|
export interface Injectable {
|
||||||
scope?: Type<any>;
|
providedIn?: Type<any>|'root'|null;
|
||||||
factory: () => any;
|
factory: () => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,12 +109,19 @@ export function convertInjectableProviderToFactory(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define injectable
|
* Construct an `InjectableDef` which defines how a token will be constructed by the DI system, and
|
||||||
|
* in which injectors (if any) it will be available.
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export function defineInjectable(opts: Injectable): Injectable {
|
export function defineInjectable<T>(opts: {
|
||||||
return opts;
|
providedIn?: Type<any>| 'root' | null,
|
||||||
|
factory: () => T,
|
||||||
|
}): InjectableDef<T> {
|
||||||
|
return {
|
||||||
|
providedIn: opts.providedIn || null,
|
||||||
|
factory: opts.factory,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,19 +132,24 @@ export function defineInjectable(opts: Injectable): Injectable {
|
||||||
*/
|
*/
|
||||||
export const Injectable: InjectableDecorator = makeDecorator(
|
export const Injectable: InjectableDecorator = makeDecorator(
|
||||||
'Injectable', undefined, undefined, undefined,
|
'Injectable', undefined, undefined, undefined,
|
||||||
(injectableType: Type<any>, options: {scope: Type<any>} & InjectableProvider) => {
|
(injectableType: Type<any>,
|
||||||
if (options && options.scope) {
|
options: {providedIn?: Type<any>| 'root' | null} & InjectableProvider) => {
|
||||||
|
if (options && options.providedIn) {
|
||||||
(injectableType as InjectableType<any>).ngInjectableDef = defineInjectable({
|
(injectableType as InjectableType<any>).ngInjectableDef = defineInjectable({
|
||||||
scope: options.scope,
|
providedIn: options.providedIn,
|
||||||
factory: convertInjectableProviderToFactory(injectableType, options)
|
factory: convertInjectableProviderToFactory(injectableType, options)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export interface InjectableDef<T> {
|
||||||
|
providedIn: Type<any>|'root'|null;
|
||||||
|
factory: () => T;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type representing injectable service.
|
* Type representing injectable service.
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export interface InjectableType<T> extends Type<T> { ngInjectableDef?: Injectable; }
|
export interface InjectableType<T> extends Type<T> { ngInjectableDef: InjectableDef<T>; }
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
|
||||||
import {Injectable, defineInjectable} from './injectable';
|
import {InjectableDef, defineInjectable} from './injectable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a token that can be used in a DI Provider.
|
* Creates a token that can be used in a DI Provider.
|
||||||
|
@ -36,12 +36,15 @@ export class InjectionToken<T> {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
readonly ngMetadataName = 'InjectionToken';
|
readonly ngMetadataName = 'InjectionToken';
|
||||||
|
|
||||||
readonly ngInjectableDef: Injectable|undefined;
|
readonly ngInjectableDef: InjectableDef<T>|undefined;
|
||||||
|
|
||||||
constructor(protected _desc: string, options?: {scope: Type<any>, factory: () => T}) {
|
constructor(protected _desc: string, options?: {
|
||||||
|
providedIn?: Type<any>| 'root' | null,
|
||||||
|
factory: () => T
|
||||||
|
}) {
|
||||||
if (options !== undefined) {
|
if (options !== undefined) {
|
||||||
this.ngInjectableDef = defineInjectable({
|
this.ngInjectableDef = defineInjectable({
|
||||||
scope: options.scope,
|
providedIn: options.providedIn || 'root',
|
||||||
factory: options.factory,
|
factory: options.factory,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,18 +10,10 @@ import {Type} from '../type';
|
||||||
import {InjectionToken} from './injection_token';
|
import {InjectionToken} from './injection_token';
|
||||||
|
|
||||||
|
|
||||||
// APP_ROOT_SCOPE is cast as a Type to allow for its usage as the scope parameter of @Injectable().
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A scope which targets the root injector.
|
* An internal token whose presence in an injector indicates that the injector should treat itself
|
||||||
*
|
* as a root scoped injector when processing requests for unknown tokens which may indicate
|
||||||
* When specified as the `scope` parameter to `@Injectable` or `InjectionToken`, this special
|
* they are provided in the root scope.
|
||||||
* scope indicates the provider for the service or token being configured belongs in the root
|
|
||||||
* injector. This is loosely equivalent to the convention of having a `forRoot()` static
|
|
||||||
* function within a module that configures the provider, and expecting users to only import that
|
|
||||||
* module via its `forRoot()` function in the root injector.
|
|
||||||
*
|
|
||||||
* @experimental
|
|
||||||
*/
|
*/
|
||||||
export const APP_ROOT_SCOPE: Type<any> = new InjectionToken<boolean>(
|
export const APP_ROOT = new InjectionToken<boolean>(
|
||||||
'The presence of this token marks an injector as being the root injector.') as any;
|
'The presence of this token marks an injector as being the root injector.');
|
||||||
|
|
|
@ -7,12 +7,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {resolveForwardRef} from '../di/forward_ref';
|
import {resolveForwardRef} from '../di/forward_ref';
|
||||||
|
import {InjectableDef} from '../di/injectable';
|
||||||
import {InjectFlags, Injector, setCurrentInjector} from '../di/injector';
|
import {InjectFlags, Injector, setCurrentInjector} from '../di/injector';
|
||||||
import {APP_ROOT_SCOPE} from '../di/scope';
|
import {APP_ROOT} from '../di/scope';
|
||||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||||
import {stringify} from '../util';
|
import {stringify} from '../util';
|
||||||
|
|
||||||
import {DepDef, DepFlags, InjectableDef, NgModuleData, NgModuleDefinition, NgModuleProviderDef, NodeFlags} from './types';
|
import {DepDef, DepFlags, NgModuleData, NgModuleDefinition, NgModuleProviderDef, NodeFlags} from './types';
|
||||||
import {splitDepsDsl, tokenKey} from './util';
|
import {splitDepsDsl, tokenKey} from './util';
|
||||||
|
|
||||||
const UNDEFINED_VALUE = new Object();
|
const UNDEFINED_VALUE = new Object();
|
||||||
|
@ -20,12 +21,6 @@ const UNDEFINED_VALUE = new Object();
|
||||||
const InjectorRefTokenKey = tokenKey(Injector);
|
const InjectorRefTokenKey = tokenKey(Injector);
|
||||||
const NgModuleRefTokenKey = tokenKey(NgModuleRef);
|
const NgModuleRefTokenKey = tokenKey(NgModuleRef);
|
||||||
|
|
||||||
export function injectableDef(scope: any, factory: () => any): InjectableDef {
|
|
||||||
return {
|
|
||||||
scope, factory,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function moduleProvideDef(
|
export function moduleProvideDef(
|
||||||
flags: NodeFlags, token: any, value: any,
|
flags: NodeFlags, token: any, value: any,
|
||||||
deps: ([DepFlags, any] | any)[]): NgModuleProviderDef {
|
deps: ([DepFlags, any] | any)[]): NgModuleProviderDef {
|
||||||
|
@ -47,7 +42,7 @@ export function moduleDef(providers: NgModuleProviderDef[]): NgModuleDefinition
|
||||||
let isRoot: boolean = false;
|
let isRoot: boolean = false;
|
||||||
for (let i = 0; i < providers.length; i++) {
|
for (let i = 0; i < providers.length; i++) {
|
||||||
const provider = providers[i];
|
const provider = providers[i];
|
||||||
if (provider.token === APP_ROOT_SCOPE) {
|
if (provider.token === APP_ROOT) {
|
||||||
isRoot = true;
|
isRoot = true;
|
||||||
}
|
}
|
||||||
if (provider.flags & NodeFlags.TypeNgModule) {
|
if (provider.flags & NodeFlags.TypeNgModule) {
|
||||||
|
@ -103,7 +98,7 @@ export function resolveNgModuleDep(
|
||||||
}
|
}
|
||||||
return providerInstance === UNDEFINED_VALUE ? undefined : providerInstance;
|
return providerInstance === UNDEFINED_VALUE ? undefined : providerInstance;
|
||||||
} else if (depDef.token.ngInjectableDef && targetsModule(data, depDef.token.ngInjectableDef)) {
|
} else if (depDef.token.ngInjectableDef && targetsModule(data, depDef.token.ngInjectableDef)) {
|
||||||
const injectableDef = depDef.token.ngInjectableDef as InjectableDef;
|
const injectableDef = depDef.token.ngInjectableDef as InjectableDef<any>;
|
||||||
const key = tokenKey;
|
const key = tokenKey;
|
||||||
const index = data._providers.length;
|
const index = data._providers.length;
|
||||||
data._def.providersByKey[depDef.tokenKey] = {
|
data._def.providersByKey[depDef.tokenKey] = {
|
||||||
|
@ -129,9 +124,9 @@ function moduleTransitivelyPresent(ngModule: NgModuleData, scope: any): boolean
|
||||||
return ngModule._def.modules.indexOf(scope) > -1;
|
return ngModule._def.modules.indexOf(scope) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function targetsModule(ngModule: NgModuleData, def: InjectableDef): boolean {
|
function targetsModule(ngModule: NgModuleData, def: InjectableDef<any>): boolean {
|
||||||
return def.scope != null && (moduleTransitivelyPresent(ngModule, def.scope) ||
|
return def.providedIn != null && (moduleTransitivelyPresent(ngModule, def.providedIn) ||
|
||||||
def.scope === APP_ROOT_SCOPE && ngModule._def.isRoot);
|
def.providedIn === 'root' && ngModule._def.isRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createProviderInstance(ngModule: NgModuleData, providerDef: NgModuleProviderDef): any {
|
function _createProviderInstance(ngModule: NgModuleData, providerDef: NgModuleProviderDef): any {
|
||||||
|
|
|
@ -8,13 +8,14 @@
|
||||||
|
|
||||||
import {isDevMode} from '../application_ref';
|
import {isDevMode} from '../application_ref';
|
||||||
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
|
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
|
||||||
import {Injector} from '../di';
|
import {InjectableType, Injector} from '../di';
|
||||||
import {ErrorHandler} from '../error_handler';
|
import {ErrorHandler} from '../error_handler';
|
||||||
import {ComponentFactory} from '../linker/component_factory';
|
import {ComponentFactory} from '../linker/component_factory';
|
||||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||||
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
|
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
|
||||||
import {Sanitizer} from '../sanitization/security';
|
import {Sanitizer} from '../sanitization/security';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
import {tokenKey} from '../view/util';
|
||||||
|
|
||||||
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
||||||
import {resolveDep} from './provider';
|
import {resolveDep} from './provider';
|
||||||
|
@ -162,10 +163,15 @@ function debugCreateNgModuleRef(
|
||||||
}
|
}
|
||||||
|
|
||||||
const providerOverrides = new Map<any, ProviderOverride>();
|
const providerOverrides = new Map<any, ProviderOverride>();
|
||||||
|
const providerOverridesWithScope = new Map<InjectableType<any>, ProviderOverride>();
|
||||||
const viewDefOverrides = new Map<any, ViewDefinition>();
|
const viewDefOverrides = new Map<any, ViewDefinition>();
|
||||||
|
|
||||||
function debugOverrideProvider(override: ProviderOverride) {
|
function debugOverrideProvider(override: ProviderOverride) {
|
||||||
providerOverrides.set(override.token, override);
|
providerOverrides.set(override.token, override);
|
||||||
|
if (typeof override.token === 'function' && override.token.ngInjectableDef &&
|
||||||
|
typeof override.token.ngInjectableDef.providedIn === 'function') {
|
||||||
|
providerOverridesWithScope.set(override.token as InjectableType<any>, override);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function debugOverrideComponentView(comp: any, compFactory: ComponentFactory<any>) {
|
function debugOverrideComponentView(comp: any, compFactory: ComponentFactory<any>) {
|
||||||
|
@ -176,6 +182,7 @@ function debugOverrideComponentView(comp: any, compFactory: ComponentFactory<any
|
||||||
|
|
||||||
function debugClearOverrides() {
|
function debugClearOverrides() {
|
||||||
providerOverrides.clear();
|
providerOverrides.clear();
|
||||||
|
providerOverridesWithScope.clear();
|
||||||
viewDefOverrides.clear();
|
viewDefOverrides.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +273,14 @@ function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefi
|
||||||
hasDeprecatedOverrides = hasDeprecatedOverrides || override.deprecatedBehavior;
|
hasDeprecatedOverrides = hasDeprecatedOverrides || override.deprecatedBehavior;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
def.modules.forEach(module => {
|
||||||
|
providerOverridesWithScope.forEach((override, token) => {
|
||||||
|
if (token.ngInjectableDef.providedIn === module) {
|
||||||
|
hasOverrides = true;
|
||||||
|
hasDeprecatedOverrides = hasDeprecatedOverrides || override.deprecatedBehavior;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
return {hasOverrides, hasDeprecatedOverrides};
|
return {hasOverrides, hasDeprecatedOverrides};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +300,23 @@ function applyProviderOverridesToNgModule(def: NgModuleDefinition): NgModuleDefi
|
||||||
provider.value = override.value;
|
provider.value = override.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (providerOverridesWithScope.size > 0) {
|
||||||
|
let moduleSet = new Set<any>(def.modules);
|
||||||
|
providerOverridesWithScope.forEach((override, token) => {
|
||||||
|
if (moduleSet.has(token.ngInjectableDef.providedIn)) {
|
||||||
|
let provider = {
|
||||||
|
token: token,
|
||||||
|
flags:
|
||||||
|
override.flags | (hasDeprecatedOverrides ? NodeFlags.LazyProvider : NodeFlags.None),
|
||||||
|
deps: splitDepsDsl(override.deps),
|
||||||
|
value: override.value,
|
||||||
|
index: def.providers.length,
|
||||||
|
};
|
||||||
|
def.providers.push(provider);
|
||||||
|
def.providersByKey[tokenKey(token)] = provider;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -297,11 +297,6 @@ export const enum DepFlags {
|
||||||
Value = 1 << 3,
|
Value = 1 << 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InjectableDef {
|
|
||||||
scope: any;
|
|
||||||
factory: () => any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TextDef { prefix: string; }
|
export interface TextDef { prefix: string; }
|
||||||
|
|
||||||
export interface QueryDef {
|
export interface QueryDef {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {renderComponent, toHtml} from '../render_util';
|
||||||
xdescribe('NgModule', () => {
|
xdescribe('NgModule', () => {
|
||||||
|
|
||||||
interface Injectable {
|
interface Injectable {
|
||||||
scope?: /*InjectorDefType<any>*/ any;
|
providedIn?: /*InjectorDefType<any>*/ any;
|
||||||
factory: Function;
|
factory: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ xdescribe('NgModule', () => {
|
||||||
constructor(@Optional() toast: Toast|null, name: String) {}
|
constructor(@Optional() toast: Toast|null, name: String) {}
|
||||||
// NORMATIVE
|
// NORMATIVE
|
||||||
static ngInjectableDef = defineInjectable({
|
static ngInjectableDef = defineInjectable({
|
||||||
scope: MyModule,
|
providedIn: MyModule,
|
||||||
factory: () => new BurntToast(
|
factory: () => new BurntToast(
|
||||||
$r3$.ɵdirectiveInject(Toast, $r3$.ɵInjectFlags.Optional),
|
$r3$.ɵdirectiveInject(Toast, $r3$.ɵInjectFlags.Optional),
|
||||||
$r3$.ɵdirectiveInject(String)),
|
$r3$.ɵdirectiveInject(String)),
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {NgModuleRef} from '@angular/core';
|
import {NgModuleRef} from '@angular/core';
|
||||||
|
import {InjectableDef} from '@angular/core/src/di/injectable';
|
||||||
import {InjectFlags, Injector, inject} from '@angular/core/src/di/injector';
|
import {InjectFlags, Injector, inject} from '@angular/core/src/di/injector';
|
||||||
import {makePropDecorator} from '@angular/core/src/util/decorators';
|
import {makePropDecorator} from '@angular/core/src/util/decorators';
|
||||||
import {InjectableDef, NgModuleDefinition, NgModuleProviderDef, NodeFlags} from '@angular/core/src/view';
|
import {NgModuleDefinition, NgModuleProviderDef, NodeFlags} from '@angular/core/src/view';
|
||||||
import {moduleDef, moduleProvideDef, resolveNgModuleDep} from '@angular/core/src/view/ng_module';
|
import {moduleDef, moduleProvideDef, resolveNgModuleDep} from '@angular/core/src/view/ng_module';
|
||||||
import {createNgModuleRef} from '@angular/core/src/view/refs';
|
import {createNgModuleRef} from '@angular/core/src/view/refs';
|
||||||
import {tokenKey} from '@angular/core/src/view/util';
|
import {tokenKey} from '@angular/core/src/view/util';
|
||||||
|
@ -23,67 +24,67 @@ class MyChildModule {}
|
||||||
class NotMyModule {}
|
class NotMyModule {}
|
||||||
|
|
||||||
class Bar {
|
class Bar {
|
||||||
static ngInjectableDef: InjectableDef = {
|
static ngInjectableDef: InjectableDef<Bar> = {
|
||||||
factory: () => new Bar(),
|
factory: () => new Bar(),
|
||||||
scope: MyModule,
|
providedIn: MyModule,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class Baz {
|
class Baz {
|
||||||
static ngInjectableDef: InjectableDef = {
|
static ngInjectableDef: InjectableDef<Baz> = {
|
||||||
factory: () => new Baz(),
|
factory: () => new Baz(),
|
||||||
scope: NotMyModule,
|
providedIn: NotMyModule,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class HasNormalDep {
|
class HasNormalDep {
|
||||||
constructor(public foo: Foo) {}
|
constructor(public foo: Foo) {}
|
||||||
|
|
||||||
static ngInjectableDef: InjectableDef = {
|
static ngInjectableDef: InjectableDef<HasNormalDep> = {
|
||||||
factory: () => new HasNormalDep(inject(Foo)),
|
factory: () => new HasNormalDep(inject(Foo)),
|
||||||
scope: MyModule,
|
providedIn: MyModule,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class HasDefinedDep {
|
class HasDefinedDep {
|
||||||
constructor(public bar: Bar) {}
|
constructor(public bar: Bar) {}
|
||||||
|
|
||||||
static ngInjectableDef: InjectableDef = {
|
static ngInjectableDef: InjectableDef<HasDefinedDep> = {
|
||||||
factory: () => new HasDefinedDep(inject(Bar)),
|
factory: () => new HasDefinedDep(inject(Bar)),
|
||||||
scope: MyModule,
|
providedIn: MyModule,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class HasOptionalDep {
|
class HasOptionalDep {
|
||||||
constructor(public baz: Baz|null) {}
|
constructor(public baz: Baz|null) {}
|
||||||
|
|
||||||
static ngInjectableDef: InjectableDef = {
|
static ngInjectableDef: InjectableDef<HasOptionalDep> = {
|
||||||
factory: () => new HasOptionalDep(inject(Baz, null)),
|
factory: () => new HasOptionalDep(inject(Baz, null)),
|
||||||
scope: MyModule,
|
providedIn: MyModule,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChildDep {
|
class ChildDep {
|
||||||
static ngInjectableDef: InjectableDef = {
|
static ngInjectableDef: InjectableDef<ChildDep> = {
|
||||||
factory: () => new ChildDep(),
|
factory: () => new ChildDep(),
|
||||||
scope: MyChildModule,
|
providedIn: MyChildModule,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class FromChildWithOptionalDep {
|
class FromChildWithOptionalDep {
|
||||||
constructor(public baz: Baz|null) {}
|
constructor(public baz: Baz|null) {}
|
||||||
static ngInjectableDef: InjectableDef = {
|
static ngInjectableDef: InjectableDef<FromChildWithOptionalDep> = {
|
||||||
factory: () => new FromChildWithOptionalDep(inject(Baz, null, InjectFlags.Default)),
|
factory: () => new FromChildWithOptionalDep(inject(Baz, null, InjectFlags.Default)),
|
||||||
scope: MyChildModule,
|
providedIn: MyChildModule,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class FromChildWithSkipSelfDep {
|
class FromChildWithSkipSelfDep {
|
||||||
constructor(public depFromParent: ChildDep|null, public depFromChild: Bar|null) {}
|
constructor(public depFromParent: ChildDep|null, public depFromChild: Bar|null) {}
|
||||||
static ngInjectableDef: InjectableDef = {
|
static ngInjectableDef: InjectableDef<FromChildWithSkipSelfDep> = {
|
||||||
factory: () => new FromChildWithSkipSelfDep(
|
factory: () => new FromChildWithSkipSelfDep(
|
||||||
inject(ChildDep, null, InjectFlags.SkipSelf), inject(Bar, null, InjectFlags.Self)),
|
inject(ChildDep, null, InjectFlags.SkipSelf), inject(Bar, null, InjectFlags.Self)),
|
||||||
scope: MyChildModule,
|
providedIn: MyChildModule,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵDepFlags as DepFlags, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
|
import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵAPP_ROOT as APP_ROOT, ɵDepFlags as DepFlags, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
|
||||||
|
|
||||||
import {AsyncTestCompleter} from './async_test_completer';
|
import {AsyncTestCompleter} from './async_test_completer';
|
||||||
import {ComponentFixture} from './component_fixture';
|
import {ComponentFixture} from './component_fixture';
|
||||||
|
@ -224,6 +224,9 @@ export class TestBed implements Injector {
|
||||||
private _aotSummaries: Array<() => any[]> = [];
|
private _aotSummaries: Array<() => any[]> = [];
|
||||||
private _templateOverrides: Array<{component: Type<any>, templateOf: Type<any>}> = [];
|
private _templateOverrides: Array<{component: Type<any>, templateOf: Type<any>}> = [];
|
||||||
|
|
||||||
|
private _isRoot: boolean = true;
|
||||||
|
private _rootProviderOverrides: Provider[] = [];
|
||||||
|
|
||||||
platform: PlatformRef = null !;
|
platform: PlatformRef = null !;
|
||||||
|
|
||||||
ngModule: Type<any>|Type<any>[] = null !;
|
ngModule: Type<any>|Type<any>[] = null !;
|
||||||
|
@ -275,6 +278,9 @@ export class TestBed implements Injector {
|
||||||
this._directiveOverrides = [];
|
this._directiveOverrides = [];
|
||||||
this._pipeOverrides = [];
|
this._pipeOverrides = [];
|
||||||
|
|
||||||
|
this._isRoot = true;
|
||||||
|
this._rootProviderOverrides = [];
|
||||||
|
|
||||||
this._moduleRef = null !;
|
this._moduleRef = null !;
|
||||||
this._moduleFactory = null !;
|
this._moduleFactory = null !;
|
||||||
this._compilerOptions = [];
|
this._compilerOptions = [];
|
||||||
|
@ -375,7 +381,22 @@ export class TestBed implements Injector {
|
||||||
const providers = this._providers.concat([{provide: TestBed, useValue: this}]);
|
const providers = this._providers.concat([{provide: TestBed, useValue: this}]);
|
||||||
const declarations =
|
const declarations =
|
||||||
[...this._declarations, ...this._templateOverrides.map(entry => entry.templateOf)];
|
[...this._declarations, ...this._templateOverrides.map(entry => entry.templateOf)];
|
||||||
const imports = [this.ngModule, this._imports];
|
|
||||||
|
const rootScopeImports = [];
|
||||||
|
const rootProviderOverrides = this._rootProviderOverrides;
|
||||||
|
if (this._isRoot) {
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
...rootProviderOverrides,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
class RootScopeModule {
|
||||||
|
}
|
||||||
|
rootScopeImports.push(RootScopeModule);
|
||||||
|
}
|
||||||
|
providers.push({provide: APP_ROOT, useValue: this._isRoot});
|
||||||
|
|
||||||
|
const imports = [rootScopeImports, this.ngModule, this._imports];
|
||||||
const schemas = this._schemas;
|
const schemas = this._schemas;
|
||||||
|
|
||||||
@NgModule({providers, declarations, imports, schemas})
|
@NgModule({providers, declarations, imports, schemas})
|
||||||
|
@ -477,6 +498,15 @@ export class TestBed implements Injector {
|
||||||
deps?: any[],
|
deps?: any[],
|
||||||
},
|
},
|
||||||
deprecated = false): void {
|
deprecated = false): void {
|
||||||
|
if (typeof token !== 'string' && token.ngInjectableDef &&
|
||||||
|
token.ngInjectableDef.providedIn === 'root') {
|
||||||
|
if (provider.useFactory) {
|
||||||
|
this._rootProviderOverrides.push(
|
||||||
|
{provide: token, useFactory: provider.useFactory, deps: provider.deps || []});
|
||||||
|
} else {
|
||||||
|
this._rootProviderOverrides.push({provide: token, useValue: provider.useValue});
|
||||||
|
}
|
||||||
|
}
|
||||||
let flags: NodeFlags = 0;
|
let flags: NodeFlags = 0;
|
||||||
let value: any;
|
let value: any;
|
||||||
if (provider.useFactory) {
|
if (provider.useFactory) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {APP_ROOT_SCOPE, InjectFlags, InjectionToken, Injector, ReflectiveInjector, Type, inject, ɵsetCurrentInjector as setCurrentInjector} from '@angular/core';
|
import {InjectFlags, InjectionToken, Injector, ReflectiveInjector, Type, inject, ɵsetCurrentInjector as setCurrentInjector} from '@angular/core';
|
||||||
|
|
||||||
class MockRootScopeInjector implements Injector {
|
class MockRootScopeInjector implements Injector {
|
||||||
constructor(readonly parent: Injector) {}
|
constructor(readonly parent: Injector) {}
|
||||||
|
@ -14,7 +14,7 @@ class MockRootScopeInjector implements Injector {
|
||||||
get<T>(
|
get<T>(
|
||||||
token: Type<T>|InjectionToken<T>, defaultValue?: any,
|
token: Type<T>|InjectionToken<T>, defaultValue?: any,
|
||||||
flags: InjectFlags = InjectFlags.Default): T {
|
flags: InjectFlags = InjectFlags.Default): T {
|
||||||
if ((token as any).ngInjectableDef && (token as any).ngInjectableDef.scope === APP_ROOT_SCOPE) {
|
if ((token as any).ngInjectableDef && (token as any).ngInjectableDef.providedIn === 'root') {
|
||||||
const old = setCurrentInjector(this);
|
const old = setCurrentInjector(this);
|
||||||
try {
|
try {
|
||||||
return (token as any).ngInjectableDef.factory();
|
return (token as any).ngInjectableDef.factory();
|
||||||
|
@ -56,7 +56,7 @@ class MockRootScopeInjector implements Injector {
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
});
|
});
|
||||||
|
|
||||||
it('injects a tree-shaekable InjectionToken', () => {
|
it('injects a tree-shakeable InjectionToken', () => {
|
||||||
class MyDep {}
|
class MyDep {}
|
||||||
const injector = new MockRootScopeInjector(ReflectiveInjector.resolveAndCreate([MyDep]));
|
const injector = new MockRootScopeInjector(ReflectiveInjector.resolveAndCreate([MyDep]));
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class MockRootScopeInjector implements Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
const MY_SERVICE_TOKEN = new InjectionToken<MyService>('Manually constructed MyService', {
|
const MY_SERVICE_TOKEN = new InjectionToken<MyService>('Manually constructed MyService', {
|
||||||
scope: APP_ROOT_SCOPE,
|
providedIn: 'root',
|
||||||
factory: () => new MyService(inject(MyDep)),
|
factory: () => new MyService(inject(MyDep)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CommonModule, PlatformLocation, ɵPLATFORM_BROWSER_ID as PLATFORM_BROWSER_ID} from '@angular/common';
|
import {CommonModule, PlatformLocation, ɵPLATFORM_BROWSER_ID as PLATFORM_BROWSER_ID} from '@angular/common';
|
||||||
import {APP_ID, APP_ROOT_SCOPE, ApplicationModule, ErrorHandler, ModuleWithProviders, NgModule, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, RendererFactory2, RootRenderer, Sanitizer, SkipSelf, StaticProvider, Testability, createPlatformFactory, platformCore} from '@angular/core';
|
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 {BrowserDomAdapter} from './browser/browser_adapter';
|
import {BrowserDomAdapter} from './browser/browser_adapter';
|
||||||
import {BrowserPlatformLocation} from './browser/location/browser_platform_location';
|
import {BrowserPlatformLocation} from './browser/location/browser_platform_location';
|
||||||
|
@ -71,7 +71,7 @@ export function _document(): any {
|
||||||
@NgModule({
|
@NgModule({
|
||||||
providers: [
|
providers: [
|
||||||
BROWSER_SANITIZATION_PROVIDERS,
|
BROWSER_SANITIZATION_PROVIDERS,
|
||||||
{provide: APP_ROOT_SCOPE, useValue: true},
|
{provide: APP_ROOT, useValue: true},
|
||||||
{provide: ErrorHandler, useFactory: errorHandler, deps: []},
|
{provide: ErrorHandler, useFactory: errorHandler, deps: []},
|
||||||
{provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true},
|
{provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true},
|
||||||
{provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true},
|
{provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true},
|
||||||
|
|
|
@ -112,9 +112,6 @@ export declare const APP_ID: InjectionToken<string>;
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare const APP_INITIALIZER: InjectionToken<(() => void)[]>;
|
export declare const APP_INITIALIZER: InjectionToken<(() => void)[]>;
|
||||||
|
|
||||||
/** @experimental */
|
|
||||||
export declare const APP_ROOT_SCOPE: Type<any>;
|
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare class ApplicationInitStatus {
|
export declare class ApplicationInitStatus {
|
||||||
readonly done: boolean;
|
readonly done: boolean;
|
||||||
|
@ -347,7 +344,10 @@ export declare class DefaultIterableDiffer<V> implements IterableDiffer<V>, Iter
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare function defineInjectable(opts: Injectable): Injectable;
|
export declare function defineInjectable<T>(opts: {
|
||||||
|
providedIn?: Type<any> | 'root' | null;
|
||||||
|
factory: () => T;
|
||||||
|
}): InjectableDef<T>;
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare function destroyPlatform(): void;
|
export declare function destroyPlatform(): void;
|
||||||
|
@ -461,11 +461,11 @@ export declare const Injectable: InjectableDecorator;
|
||||||
export interface InjectableDecorator {
|
export interface InjectableDecorator {
|
||||||
/** @stable */ (): any;
|
/** @stable */ (): any;
|
||||||
(options?: {
|
(options?: {
|
||||||
scope: Type<any>;
|
providedIn: Type<any> | 'root' | null;
|
||||||
} & InjectableProvider): any;
|
} & InjectableProvider): any;
|
||||||
new (): Injectable;
|
new (): Injectable;
|
||||||
new (options?: {
|
new (options?: {
|
||||||
scope: Type<any>;
|
providedIn: Type<any> | 'root' | null;
|
||||||
} & InjectableProvider): Injectable;
|
} & InjectableProvider): Injectable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,7 +474,7 @@ export declare type InjectableProvider = ValueSansProvider | ExistingSansProvide
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export interface InjectableType<T> extends Type<T> {
|
export interface InjectableType<T> extends Type<T> {
|
||||||
ngInjectableDef?: Injectable;
|
ngInjectableDef: InjectableDef<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
|
@ -493,9 +493,9 @@ export declare const enum InjectFlags {
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class InjectionToken<T> {
|
export declare class InjectionToken<T> {
|
||||||
protected _desc: string;
|
protected _desc: string;
|
||||||
readonly ngInjectableDef: Injectable | undefined;
|
readonly ngInjectableDef: InjectableDef<T> | undefined;
|
||||||
constructor(_desc: string, options?: {
|
constructor(_desc: string, options?: {
|
||||||
scope: Type<any>;
|
providedIn?: Type<any> | 'root' | null;
|
||||||
factory: () => T;
|
factory: () => T;
|
||||||
});
|
});
|
||||||
toString(): string;
|
toString(): string;
|
||||||
|
|
Loading…
Reference in New Issue