feat(core): make `new Inject()` optional for deps specified as `InjectionToken` (#14486)
fixes #10625
This commit is contained in:
parent
5f3c8441e4
commit
d6a58f9f70
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]}]);
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
|
@ -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');
|
||||||
|
|
Loading…
Reference in New Issue