feat(core): make `new Inject()` optional for deps specified as `InjectionToken` (#14486)

fixes #10625
This commit is contained in:
Victor Berchet 2017-02-20 16:20:45 -08:00 committed by GitHub
parent 5f3c8441e4
commit d6a58f9f70
6 changed files with 41 additions and 29 deletions

View File

@ -825,6 +825,8 @@ export class CompileMetadataResolver {
token = paramEntry.attributeName; token = paramEntry.attributeName;
} else if (paramEntry instanceof Inject) { } else if (paramEntry instanceof Inject) {
token = paramEntry.token; token = paramEntry.token;
} else if (paramEntry instanceof InjectionToken) {
token = paramEntry;
} else if (isValidType(paramEntry) && token == null) { } else if (isValidType(paramEntry) && token == null) {
token = paramEntry; token = paramEntry;
} }

View File

@ -827,7 +827,6 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
function createProvider( function createProvider(
token: string, {multi = false, deps = []}: {multi?: boolean, deps?: string[]} = {}): token: string, {multi = false, deps = []}: {multi?: boolean, deps?: string[]} = {}):
CompileProviderMetadata { CompileProviderMetadata {
const name = `provider${nextProviderId++}`;
const compileToken = createToken(token); const compileToken = createToken(token);
return { return {
token: compileToken, token: compileToken,

View File

@ -10,7 +10,8 @@ import {reflector} from '../reflection/reflection';
import {Type} from '../type'; import {Type} from '../type';
import {resolveForwardRef} from './forward_ref'; import {resolveForwardRef} from './forward_ref';
import {Host, Inject, Optional, Self, SkipSelf} from './metadata'; import {InjectionToken} from './injection_token';
import {Inject, Optional, Self, SkipSelf} from './metadata';
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './provider'; import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './provider';
import {invalidProviderError, mixingMultiProvidersWithRegularProvidersError, noAnnotationError} from './reflective_errors'; import {invalidProviderError, mixingMultiProvidersWithRegularProvidersError, noAnnotationError} from './reflective_errors';
import {ReflectiveKey} from './reflective_key'; import {ReflectiveKey} from './reflective_key';
@ -245,6 +246,8 @@ function _extractToken(
} else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) { } else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) {
visibility = paramMetadata; visibility = paramMetadata;
} else if (paramMetadata instanceof InjectionToken) {
token = paramMetadata;
} }
} }

View File

@ -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 {Inject, Injectable, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self, forwardRef} from '@angular/core'; import {Inject, Injectable, InjectionToken, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self, forwardRef} from '@angular/core';
import {ReflectiveInjector_} from '@angular/core/src/di/reflective_injector'; import {ReflectiveInjector_} from '@angular/core/src/di/reflective_injector';
import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider'; import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider';
import {getOriginalError} from '@angular/core/src/errors'; import {getOriginalError} from '@angular/core/src/errors';
@ -31,14 +31,12 @@ class TurboEngine extends Engine {}
@Injectable() @Injectable()
class Car { class Car {
engine: Engine; constructor(public engine: Engine) {}
constructor(engine: Engine) { this.engine = engine; }
} }
@Injectable() @Injectable()
class CarWithOptionalEngine { class CarWithOptionalEngine {
engine: any /** TODO #9100 */; constructor(@Optional() public engine: Engine) {}
constructor(@Optional() engine: Engine) { this.engine = engine; }
} }
@Injectable() @Injectable()
@ -53,14 +51,11 @@ class CarWithDashboard {
@Injectable() @Injectable()
class SportsCar extends Car { class SportsCar extends Car {
engine: Engine;
constructor(engine: Engine) { super(engine); }
} }
@Injectable() @Injectable()
class CarWithInject { class CarWithInject {
engine: Engine; constructor(@Inject(TurboEngine) public engine: Engine) {}
constructor(@Inject(TurboEngine) engine: Engine) { this.engine = engine; }
} }
@Injectable() @Injectable()
@ -151,8 +146,20 @@ export function main() {
expect(engine).toEqual('fake engine'); expect(engine).toEqual('fake engine');
}); });
it('should inject dependencies instance of InjectionToken', () => {
const TOKEN = new InjectionToken<string>('token');
const injector = createInjector([
{provide: TOKEN, useValue: 'by token'},
{provide: Engine, useFactory: (v: string) => v, deps: [[TOKEN]]},
]);
const engine = injector.get(Engine);
expect(engine).toEqual('by token');
});
it('should provide to a factory', () => { it('should provide to a factory', () => {
function sportsCarFactory(e: any /** TODO #9100 */) { return new SportsCar(e); } function sportsCarFactory(e: any) { return new SportsCar(e); }
const injector = const injector =
createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]); createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]);

View File

@ -7,12 +7,11 @@
*/ */
import {USE_VIEW_ENGINE} from '@angular/compiler/src/config'; import {USE_VIEW_ENGINE} from '@angular/compiler/src/config';
import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, ElementRef, Host, Inject, Input, Optional, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewContainerRef} from '@angular/core'; import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, ElementRef, Host, Inject, InjectionToken, Input, Optional, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewContainerRef} from '@angular/core';
import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing'; import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {expect} from '@angular/platform-browser/testing/matchers'; import {expect} from '@angular/platform-browser/testing/matchers';
@Directive({selector: '[simpleDirective]'}) @Directive({selector: '[simpleDirective]'})
class SimpleDirective { class SimpleDirective {
@Input('simpleDirective') value: any = null; @Input('simpleDirective') value: any = null;
@ -107,32 +106,27 @@ class NeedsAttribute {
@Directive({selector: '[needsAttributeNoType]'}) @Directive({selector: '[needsAttributeNoType]'})
class NeedsAttributeNoType { class NeedsAttributeNoType {
fooAttribute: any; constructor(@Attribute('foo') public fooAttribute: any) {}
constructor(@Attribute('foo') fooAttribute: any) { this.fooAttribute = fooAttribute; }
} }
@Directive({selector: '[needsElementRef]'}) @Directive({selector: '[needsElementRef]'})
class NeedsElementRef { class NeedsElementRef {
elementRef: any; constructor(public elementRef: ElementRef) {}
constructor(ref: ElementRef) { this.elementRef = ref; }
} }
@Directive({selector: '[needsViewContainerRef]'}) @Directive({selector: '[needsViewContainerRef]'})
class NeedsViewContainerRef { class NeedsViewContainerRef {
viewContainer: any; constructor(public viewContainer: ViewContainerRef) {}
constructor(vc: ViewContainerRef) { this.viewContainer = vc; }
} }
@Directive({selector: '[needsTemplateRef]'}) @Directive({selector: '[needsTemplateRef]'})
class NeedsTemplateRef { class NeedsTemplateRef {
templateRef: any; constructor(public templateRef: TemplateRef<Object>) {}
constructor(ref: TemplateRef<Object>) { this.templateRef = ref; }
} }
@Directive({selector: '[optionallyNeedsTemplateRef]'}) @Directive({selector: '[optionallyNeedsTemplateRef]'})
class OptionallyNeedsTemplateRef { class OptionallyNeedsTemplateRef {
templateRef: any; constructor(@Optional() public templateRef: TemplateRef<Object>) {}
constructor(@Optional() ref: TemplateRef<Object>) { this.templateRef = ref; }
} }
@Directive({selector: '[directiveNeedsChangeDetectorRef]'}) @Directive({selector: '[directiveNeedsChangeDetectorRef]'})
@ -226,10 +220,17 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
// On CJS fakeAsync is not supported... // On CJS fakeAsync is not supported...
if (!getDOM().supportsDOMEvents()) return; if (!getDOM().supportsDOMEvents()) return;
beforeEach(() => TestBed.configureTestingModule({ const TOKEN = new InjectionToken<string>('token');
declarations: [TestComp],
providers: [{provide: 'appService', useValue: 'appService'}] beforeEach(() => {
})); TestBed.configureTestingModule({
declarations: [TestComp],
providers: [
{provide: TOKEN, useValue: 'appService'},
{provide: 'appService', useFactory: (v: string) => v, deps: [TOKEN]},
],
});
});
describe('injection', () => { describe('injection', () => {
it('should instantiate directives that have no dependencies', () => { it('should instantiate directives that have no dependencies', () => {

View File

@ -137,7 +137,7 @@ export function main() {
provide: Hash, provide: Hash,
useFactory: (location: string) => `Hash for: ${location}`, useFactory: (location: string) => `Hash for: ${location}`,
// use a nested array to define metadata for dependencies. // use a nested array to define metadata for dependencies.
deps: [[new Optional(), new Inject(Location)]] deps: [[new Optional(), Location]]
}]); }]);
expect(injector.get(Hash)).toEqual('Hash for: null'); expect(injector.get(Hash)).toEqual('Hash for: null');