diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/dep.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/dep.ts
new file mode 100644
index 0000000000..a02cbe6e2e
--- /dev/null
+++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/dep.ts
@@ -0,0 +1,41 @@
+/**
+ * @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 {Component, Injectable, NgModule} from '@angular/core';
+import {BrowserModule} from '@angular/platform-browser';
+import {ServerModule} from '@angular/platform-server';
+
+@Injectable()
+export class NormalService {
+}
+
+@Component({
+ selector: 'dep-app',
+ template: '{{found}}',
+})
+export class AppComponent {
+ found: boolean;
+ constructor(service: ShakeableService) { this.found = !!service.normal; }
+}
+
+@NgModule({
+ imports: [
+ BrowserModule.withServerTransition({appId: 'id-app'}),
+ ServerModule,
+ ],
+ declarations: [AppComponent],
+ bootstrap: [AppComponent],
+ providers: [NormalService],
+})
+export class DepAppModule {
+}
+
+@Injectable({scope: DepAppModule})
+export class ShakeableService {
+ constructor(readonly normal: NormalService) {}
+}
\ No newline at end of file
diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/token.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/token.ts
index e557195ded..b458c405c6 100644
--- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/token.ts
+++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/src/token.ts
@@ -6,11 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {Component, Inject, InjectionToken, NgModule, forwardRef} from '@angular/core';
+import {Component, Inject, Injectable, InjectionToken, NgModule, forwardRef, inject} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {ServerModule} from '@angular/platform-server';
-export interface IService { readonly data: string; }
+export interface IService { readonly dep: {readonly data: string;}; }
@NgModule({})
export class TokenModule {
@@ -18,7 +18,7 @@ export class TokenModule {
export const TOKEN = new InjectionToken('test', {
scope: TokenModule,
- factory: () => new Service(),
+ factory: () => new Service(inject(Dep)),
});
@@ -28,7 +28,7 @@ export const TOKEN = new InjectionToken('test', {
})
export class AppComponent {
data: string;
- constructor(@Inject(TOKEN) service: IService) { this.data = service.data; }
+ constructor(@Inject(TOKEN) service: IService) { this.data = service.dep.data; }
}
@NgModule({
@@ -37,10 +37,18 @@ export class AppComponent {
ServerModule,
TokenModule,
],
+ providers: [forwardRef(() => Dep)],
declarations: [AppComponent],
bootstrap: [AppComponent],
})
export class TokenAppModule {
}
-export class Service { readonly data = 'fromToken'; }
\ No newline at end of file
+@Injectable()
+export class Dep {
+ readonly data = 'fromToken';
+}
+
+export class Service {
+ constructor(readonly dep: Dep) {}
+}
diff --git a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts
index 907941877c..74da00c714 100644
--- a/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts
+++ b/packages/compiler-cli/integrationtest/bazel/injectable_def/app/test/app_spec.ts
@@ -9,6 +9,7 @@
import {enableProdMode} from '@angular/core';
import {renderModuleFactory} from '@angular/platform-server';
import {BasicAppModuleNgFactory} from 'app_built/src/basic.ngfactory';
+import {DepAppModuleNgFactory} from 'app_built/src/dep.ngfactory';
import {HierarchyAppModuleNgFactory} from 'app_built/src/hierarchy.ngfactory';
import {RootAppModuleNgFactory} from 'app_built/src/root.ngfactory';
import {SelfAppModuleNgFactory} from 'app_built/src/self.ngfactory';
@@ -66,4 +67,14 @@ describe('ngInjectableDef Bazel Integration', () => {
done();
});
});
+
+ it('can inject dependencies', done => {
+ renderModuleFactory(DepAppModuleNgFactory, {
+ document: '',
+ url: '/',
+ }).then(html => {
+ expect(html).toMatch(/>true<\//);
+ done();
+ });
+ });
});
diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts
index bf0a7dbe54..26e714858f 100644
--- a/packages/core/src/core_private_export.ts
+++ b/packages/core/src/core_private_export.ts
@@ -12,6 +12,7 @@ export {devModeEqual as ɵdevModeEqual} from './change_detection/change_detectio
export {isListLikeIterable as ɵisListLikeIterable} from './change_detection/change_detection_util';
export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants';
export {Console as ɵConsole} from './console';
+export {setCurrentInjector as ɵsetCurrentInjector} from './di/injector';
export {ComponentFactory as ɵComponentFactory} from './linker/component_factory';
export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver';
export {ReflectionCapabilities as ɵReflectionCapabilities} from './reflection/reflection_capabilities';
diff --git a/packages/core/src/di.ts b/packages/core/src/di.ts
index 79e4993603..756904237a 100644
--- a/packages/core/src/di.ts
+++ b/packages/core/src/di.ts
@@ -17,7 +17,7 @@ export {defineInjectable, Injectable, InjectableDecorator, InjectableProvider, I
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
-export {InjectFlags, Injector} from './di/injector';
+export {inject, InjectFlags, Injector} from './di/injector';
export {ReflectiveInjector} from './di/reflective_injector';
export {StaticProvider, ValueProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './di/reflective_provider';
diff --git a/packages/core/src/di/injector.ts b/packages/core/src/di/injector.ts
index 0758464957..e428aa9a96 100644
--- a/packages/core/src/di/injector.ts
+++ b/packages/core/src/di/injector.ts
@@ -410,6 +410,20 @@ export function setCurrentInjector(injector: Injector | null): Injector|null {
return former;
}
+/**
+ * Injects a token from the currently active injector.
+ *
+ * This function must be used in the context of a factory function such as one defined for an
+ * `InjectionToken`, and will throw an error if not called from such a context. For example:
+ *
+ * {@example core/di/ts/injector_spec.ts region='ShakeableInjectionToken'}
+ *
+ * Within such a factory function `inject` is utilized to request injection of a dependency, instead
+ * of providing an additional array of dependencies as was common to do with `useFactory` providers.
+ * `inject` is faster and more type-safe.
+ *
+ * @experimental
+ */
export function inject(
token: Type| InjectionToken, notFoundValue?: undefined, flags?: InjectFlags): T;
export function inject(
diff --git a/packages/examples/core/di/ts/injector_spec.ts b/packages/examples/core/di/ts/injector_spec.ts
index ab57f44d0e..729f5123e0 100644
--- a/packages/examples/core/di/ts/injector_spec.ts
+++ b/packages/examples/core/di/ts/injector_spec.ts
@@ -6,7 +6,25 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {InjectionToken, Injector, ReflectiveInjector} from '@angular/core';
+import {APP_ROOT_SCOPE, InjectFlags, InjectionToken, Injector, ReflectiveInjector, Type, inject, ɵsetCurrentInjector as setCurrentInjector} from '@angular/core';
+
+class MockRootScopeInjector implements Injector {
+ constructor(readonly parent: Injector) {}
+
+ get(
+ token: Type|InjectionToken, defaultValue?: any,
+ flags: InjectFlags = InjectFlags.Default): T {
+ if ((token as any).ngInjectableDef && (token as any).ngInjectableDef.scope === APP_ROOT_SCOPE) {
+ const old = setCurrentInjector(this);
+ try {
+ return (token as any).ngInjectableDef.factory();
+ } finally {
+ setCurrentInjector(old);
+ }
+ }
+ return this.parent.get(token, defaultValue, flags);
+ }
+}
{
describe('injector metadata examples', () => {
@@ -37,5 +55,25 @@ import {InjectionToken, Injector, ReflectiveInjector} from '@angular/core';
expect(url).toBe('http://localhost');
// #enddocregion
});
+
+ it('injects a tree-shaekable InjectionToken', () => {
+ class MyDep {}
+ const injector = new MockRootScopeInjector(ReflectiveInjector.resolveAndCreate([MyDep]));
+
+ // #docregion ShakeableInjectionToken
+ class MyService {
+ constructor(readonly myDep: MyDep) {}
+ }
+
+ const MY_SERVICE_TOKEN = new InjectionToken('Manually constructed MyService', {
+ scope: APP_ROOT_SCOPE,
+ factory: () => new MyService(inject(MyDep)),
+ });
+
+ const instance = injector.get(MY_SERVICE_TOKEN);
+ expect(instance instanceof MyService).toBeTruthy();
+ expect(instance.myDep instanceof MyDep).toBeTruthy();
+ // #enddocregion
+ });
});
}
diff --git a/tools/public_api_guard/core/core.d.ts b/tools/public_api_guard/core/core.d.ts
index 8f82abbeea..835512649c 100644
--- a/tools/public_api_guard/core/core.d.ts
+++ b/tools/public_api_guard/core/core.d.ts
@@ -447,6 +447,9 @@ export interface HostDecorator {
/** @stable */
export declare const HostListener: HostListenerDecorator;
+/** @experimental */
+export declare function inject(token: Type | InjectionToken, notFoundValue?: undefined, flags?: InjectFlags): T;
+
/** @stable */
export declare const Inject: InjectDecorator;