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;
} else if (paramEntry instanceof Inject) {
token = paramEntry.token;
} else if (paramEntry instanceof InjectionToken) {
token = paramEntry;
} else if (isValidType(paramEntry) && token == null) {
token = paramEntry;
}

View File

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

View File

@ -10,7 +10,8 @@ import {reflector} from '../reflection/reflection';
import {Type} from '../type';
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 {invalidProviderError, mixingMultiProvidersWithRegularProvidersError, noAnnotationError} from './reflective_errors';
import {ReflectiveKey} from './reflective_key';
@ -245,6 +246,8 @@ function _extractToken(
} else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) {
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
*/
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 {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider';
import {getOriginalError} from '@angular/core/src/errors';
@ -31,14 +31,12 @@ class TurboEngine extends Engine {}
@Injectable()
class Car {
engine: Engine;
constructor(engine: Engine) { this.engine = engine; }
constructor(public engine: Engine) {}
}
@Injectable()
class CarWithOptionalEngine {
engine: any /** TODO #9100 */;
constructor(@Optional() engine: Engine) { this.engine = engine; }
constructor(@Optional() public engine: Engine) {}
}
@Injectable()
@ -53,14 +51,11 @@ class CarWithDashboard {
@Injectable()
class SportsCar extends Car {
engine: Engine;
constructor(engine: Engine) { super(engine); }
}
@Injectable()
class CarWithInject {
engine: Engine;
constructor(@Inject(TurboEngine) engine: Engine) { this.engine = engine; }
constructor(@Inject(TurboEngine) public engine: Engine) {}
}
@Injectable()
@ -151,8 +146,20 @@ export function main() {
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', () => {
function sportsCarFactory(e: any /** TODO #9100 */) { return new SportsCar(e); }
function sportsCarFactory(e: any) { return new SportsCar(e); }
const injector =
createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]);

View File

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

View File

@ -137,7 +137,7 @@ export function main() {
provide: Hash,
useFactory: (location: string) => `Hash for: ${location}`,
// 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');