-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(di/decorators): create all Di param and class decorators
closes #34
- Loading branch information
Showing
6 changed files
with
527 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { | ||
InjectMetadata, | ||
OptionalMetadata, | ||
InjectableMetadata, | ||
SelfMetadata, | ||
HostMetadata, | ||
SkipSelfMetadata | ||
} from './metadata'; | ||
import {makeDecorator, makeParamDecorator} from '../util/decorators'; | ||
|
||
/** | ||
* Factory for creating {@link InjectMetadata}. | ||
*/ | ||
export interface InjectFactory { | ||
(token: any): any; | ||
new (token: any): InjectMetadata; | ||
} | ||
|
||
/** | ||
* Factory for creating {@link OptionalMetadata}. | ||
*/ | ||
export interface OptionalFactory { | ||
(): any; | ||
new (): OptionalMetadata; | ||
} | ||
|
||
/** | ||
* Factory for creating {@link InjectableMetadata}. | ||
*/ | ||
export interface InjectableFactory { | ||
(): any; | ||
new (): InjectableMetadata; | ||
} | ||
|
||
/** | ||
* Factory for creating {@link SelfMetadata}. | ||
*/ | ||
export interface SelfFactory { | ||
(): any; | ||
new (): SelfMetadata; | ||
} | ||
|
||
/** | ||
* Factory for creating {@link HostMetadata}. | ||
*/ | ||
export interface HostFactory { | ||
(): any; | ||
new (): HostMetadata; | ||
} | ||
|
||
/** | ||
* Factory for creating {@link SkipSelfMetadata}. | ||
*/ | ||
export interface SkipSelfFactory { | ||
(): any; | ||
new (): SkipSelfMetadata; | ||
} | ||
|
||
/** | ||
* Factory for creating {@link InjectMetadata}. | ||
*/ | ||
export const Inject: InjectFactory = makeParamDecorator(InjectMetadata,InjectMetadata.paramDecoratorForNonConstructor); | ||
|
||
/** | ||
* Factory for creating {@link OptionalMetadata}. | ||
*/ | ||
export const Optional: OptionalFactory = makeParamDecorator(OptionalMetadata); | ||
|
||
/** | ||
* Factory for creating {@link InjectableMetadata}. | ||
*/ | ||
export const Injectable: InjectableFactory = <InjectableFactory>makeDecorator(InjectableMetadata); | ||
|
||
/** | ||
* Factory for creating {@link SelfMetadata}. | ||
*/ | ||
export const Self: SelfFactory = makeParamDecorator(SelfMetadata); | ||
|
||
/** | ||
* Factory for creating {@link HostMetadata}. | ||
*/ | ||
export const Host: HostFactory = makeParamDecorator(HostMetadata); | ||
|
||
/** | ||
* Factory for creating {@link SkipSelfMetadata}. | ||
*/ | ||
export const SkipSelf: SkipSelfFactory = makeParamDecorator(SkipSelfMetadata); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
import {CONST, stringify, isBlank, isPresent, isString} from "../facade/lang"; | ||
import {InjectFactory} from "./decorators"; | ||
|
||
/** | ||
* A parameter metadata that specifies a dependency. | ||
* | ||
* ### Example ([live demo](http://plnkr.co/edit/6uHYJK?p=preview)) | ||
* | ||
* ```typescript | ||
* class Engine {} | ||
* | ||
* @Injectable() | ||
* class Car { | ||
* engine; | ||
* constructor(@Inject("MyEngine") engine:Engine) { | ||
* this.engine = engine; | ||
* } | ||
* } | ||
* | ||
* var injector = Injector.resolveAndCreate([ | ||
* provide("MyEngine", {useClass: Engine}), | ||
* Car | ||
* ]); | ||
* | ||
* expect(injector.get(Car).engine instanceof Engine).toBe(true); | ||
* ``` | ||
* | ||
* When `@Inject()` is not present, {@link Injector} will use the type annotation of the parameter. | ||
* | ||
* ### Example | ||
* | ||
* ```typescript | ||
* class Engine {} | ||
* | ||
* @Injectable() | ||
* class Car { | ||
* constructor(public engine: Engine) {} //same as constructor(@Inject(Engine) engine:Engine) | ||
* } | ||
* | ||
* var injector = Injector.resolveAndCreate([Engine, Car]); | ||
* expect(injector.get(Car).engine instanceof Engine).toBe(true); | ||
* ``` | ||
*/ | ||
@CONST() | ||
export class InjectMetadata { | ||
|
||
static paramDecoratorForNonConstructor( | ||
annotationInstance: InjectMetadata, | ||
target: any, | ||
propertyKey: string, | ||
paramIndex: number | ||
) { | ||
|
||
const annotateMethod = target[ propertyKey ]; | ||
|
||
annotateMethod.$inject = annotateMethod.$inject || []; | ||
annotateMethod.$inject[ paramIndex ] = annotationInstance.token; | ||
|
||
} | ||
|
||
constructor( public token: any ) {} | ||
|
||
toString(): string { return `@Inject(${stringify( this.token )})`; } | ||
} | ||
|
||
/** | ||
* A parameter metadata that marks a dependency as optional. {@link Injector} provides `null` if | ||
* the dependency is not found. | ||
* | ||
* ### Example ([live demo](http://plnkr.co/edit/AsryOm?p=preview)) | ||
* | ||
* ```typescript | ||
* class Engine {} | ||
* | ||
* @Injectable() | ||
* class Car { | ||
* engine; | ||
* constructor(@Optional() engine:Engine) { | ||
* this.engine = engine; | ||
* } | ||
* } | ||
* | ||
* var injector = Injector.resolveAndCreate([Car]); | ||
* expect(injector.get(Car).engine).toBeNull(); | ||
* ``` | ||
*/ | ||
@CONST() | ||
export class OptionalMetadata { | ||
toString(): string { return `@Optional()`; } | ||
} | ||
|
||
/** | ||
* A marker metadata that marks a class as available to {@link Injector} for creation. | ||
* | ||
* ### Example ([live demo](http://plnkr.co/edit/Wk4DMQ?p=preview)) | ||
* | ||
* ```typescript | ||
* @Injectable() | ||
* class UsefulService {} | ||
* | ||
* @Injectable() | ||
* class NeedsService { | ||
* constructor(public service:UsefulService) {} | ||
* } | ||
* | ||
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]); | ||
* expect(injector.get(NeedsService).service instanceof UsefulService).toBe(true); | ||
* ``` | ||
* {@link Injector} will throw {@link NoAnnotationError} when trying to instantiate a class that | ||
* does not have `@Injectable` marker, as shown in the example below. | ||
* | ||
* ```typescript | ||
* class UsefulService {} | ||
* | ||
* class NeedsService { | ||
* constructor(public service:UsefulService) {} | ||
* } | ||
* | ||
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]); | ||
* expect(() => injector.get(NeedsService)).toThrowError(); | ||
* ``` | ||
*/ | ||
@CONST() | ||
export class InjectableMetadata { | ||
} | ||
|
||
/** | ||
* Specifies that an {@link Injector} should retrieve a dependency only from itself. | ||
* | ||
* ### Example ([live demo](http://plnkr.co/edit/NeagAg?p=preview)) | ||
* | ||
* ```typescript | ||
* class Dependency { | ||
* } | ||
* | ||
* @Injectable() | ||
* class NeedsDependency { | ||
* dependency; | ||
* constructor(@Self() dependency:Dependency) { | ||
* this.dependency = dependency; | ||
* } | ||
* } | ||
* | ||
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]); | ||
* var nd = inj.get(NeedsDependency); | ||
* | ||
* expect(nd.dependency instanceof Dependency).toBe(true); | ||
* | ||
* var inj = Injector.resolveAndCreate([Dependency]); | ||
* var child = inj.resolveAndCreateChild([NeedsDependency]); | ||
* expect(() => child.get(NeedsDependency)).toThrowError(); | ||
* ``` | ||
*/ | ||
@CONST() | ||
export class SelfMetadata { | ||
toString(): string { return `@Self()`; } | ||
} | ||
|
||
/** | ||
* Specifies that the dependency resolution should start from the parent injector. | ||
* | ||
* ### Example ([live demo](http://plnkr.co/edit/Wchdzb?p=preview)) | ||
* | ||
* ```typescript | ||
* class Dependency { | ||
* } | ||
* | ||
* @Injectable() | ||
* class NeedsDependency { | ||
* dependency; | ||
* constructor(@SkipSelf() dependency:Dependency) { | ||
* this.dependency = dependency; | ||
* } | ||
* } | ||
* | ||
* var parent = Injector.resolveAndCreate([Dependency]); | ||
* var child = parent.resolveAndCreateChild([NeedsDependency]); | ||
* expect(child.get(NeedsDependency).dependency instanceof Depedency).toBe(true); | ||
* | ||
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]); | ||
* expect(() => inj.get(NeedsDependency)).toThrowError(); | ||
* ``` | ||
*/ | ||
@CONST() | ||
export class SkipSelfMetadata { | ||
toString(): string { return `@SkipSelf()`; } | ||
} | ||
|
||
/** | ||
* Specifies that an injector should retrieve a dependency from any injector until reaching the | ||
* closest host. | ||
* | ||
* In Angular, a component element is automatically declared as a host for all the injectors in | ||
* its view. | ||
* | ||
* ### Example ([live demo](http://plnkr.co/edit/GX79pV?p=preview)) | ||
* | ||
* In the following example `App` contains `ParentCmp`, which contains `ChildDirective`. | ||
* So `ParentCmp` is the host of `ChildDirective`. | ||
* | ||
* `ChildDirective` depends on two services: `HostService` and `OtherService`. | ||
* `HostService` is defined at `ParentCmp`, and `OtherService` is defined at `App`. | ||
* | ||
*```typescript | ||
* class OtherService {} | ||
* class HostService {} | ||
* | ||
* @Directive({ | ||
* selector: 'child-directive' | ||
* }) | ||
* class ChildDirective { | ||
* constructor(@Optional() @Host() os:OtherService, @Optional() @Host() hs:HostService){ | ||
* console.log("os is null", os); | ||
* console.log("hs is NOT null", hs); | ||
* } | ||
* } | ||
* | ||
* @Component({ | ||
* selector: 'parent-cmp', | ||
* providers: [HostService], | ||
* template: ` | ||
* Dir: <child-directive></child-directive> | ||
* `, | ||
* directives: [ChildDirective] | ||
* }) | ||
* class ParentCmp { | ||
* } | ||
* | ||
* @Component({ | ||
* selector: 'app', | ||
* providers: [OtherService], | ||
* template: ` | ||
* Parent: <parent-cmp></parent-cmp> | ||
* `, | ||
* directives: [ParentCmp] | ||
* }) | ||
* class App { | ||
* } | ||
* | ||
* bootstrap(App); | ||
*``` | ||
*/ | ||
@CONST() | ||
export class HostMetadata { | ||
toString(): string { return `@Host()`; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {CONST} from '../facade/lang'; | ||
|
||
/** | ||
* Creates a token that can be used in a DI Provider. | ||
* | ||
* ### Example ([live demo](http://plnkr.co/edit/Ys9ezXpj2Mnoy3Uc8KBp?p=preview)) | ||
* | ||
* ```typescript | ||
* var t = new OpaqueToken("value"); | ||
* | ||
* var injector = Injector.resolveAndCreate([ | ||
* provide(t, {useValue: "providedValue"}) | ||
* ]); | ||
* | ||
* expect(injector.get(t)).toEqual("providedValue"); | ||
* ``` | ||
* | ||
* Using an `OpaqueToken` is preferable to using strings as tokens because of possible collisions | ||
* caused by multiple providers using the same string as two different tokens. | ||
* | ||
* Using an `OpaqueToken` is preferable to using an `Object` as tokens because it provides better | ||
* error messages. | ||
*/ | ||
@CONST() | ||
export class OpaqueToken { | ||
constructor(private _desc: string) {} | ||
|
||
toString(): string { return `Token ${this._desc}`; } | ||
} |
Empty file.
Oops, something went wrong.