diff --git a/modules/angular2/di.ts b/modules/angular2/di.ts index 4c8072cafb..bfcb3b5bfd 100644 --- a/modules/angular2/di.ts +++ b/modules/angular2/di.ts @@ -9,7 +9,7 @@ export * from './src/di/annotations'; export * from './src/di/decorators'; export {Injector} from './src/di/injector'; export {Binding, ResolvedBinding, Dependency, bind} from './src/di/binding'; -export {Key, KeyRegistry} from './src/di/key'; +export {Key, KeyRegistry, TypeLiteral} from './src/di/key'; export { NoBindingError, AbstractBindingError, diff --git a/modules/angular2/src/di/key.ts b/modules/angular2/src/di/key.ts index 7b44f676f9..8e48244e89 100644 --- a/modules/angular2/src/di/key.ts +++ b/modules/angular2/src/di/key.ts @@ -1,5 +1,8 @@ import {MapWrapper} from 'angular2/src/facade/collection'; -import {stringify} from 'angular2/src/facade/lang'; +import {stringify, CONST, Type} from 'angular2/src/facade/lang'; +import {TypeLiteral} from './type_literal'; + +export {TypeLiteral} from './type_literal'; // TODO: uncoment `int` once https://github.com/angular/angular/issues/1414 is fixed @@ -18,6 +21,10 @@ import {stringify} from 'angular2/src/facade/lang'; export class Key { token: Object; id: number; + + /** + * @private + */ constructor(token: Object, id: number) { this.token = token; this.id = id; @@ -46,6 +53,13 @@ export class KeyRegistry { get(token: Object): Key { if (token instanceof Key) return token; + // TODO: workaround for https://github.com/Microsoft/TypeScript/issues/3123 + var theToken = token; + if (token instanceof TypeLiteral) { + theToken = token.type; + } + token = theToken; + if (MapWrapper.contains(this._allKeys, token)) { return MapWrapper.get(this._allKeys, token); } diff --git a/modules/angular2/src/di/type_literal.dart b/modules/angular2/src/di/type_literal.dart new file mode 100644 index 0000000000..b6159c1097 --- /dev/null +++ b/modules/angular2/src/di/type_literal.dart @@ -0,0 +1,26 @@ +library angular2.di.type_literal; + +/** + * Use type literals as DI keys corresponding to generic types. + * + * Example: + * + * ``` + * Injector.resolveAndCreate([ + * bind(new TypeLiteral>()).toValue([1, 2, 3]) + * ]); + * + * class Foo { + * // Delend on `List` normally. + * Foo(List list) { ... } + * } + * ``` + * + * This capability might be added to the language one day. See: + * + * https://code.google.com/p/dart/issues/detail?id=11923 + */ +class TypeLiteral { + const TypeLiteral(); + Type get type => T; +} diff --git a/modules/angular2/src/di/type_literal.ts b/modules/angular2/src/di/type_literal.ts new file mode 100644 index 0000000000..3f755c7a4f --- /dev/null +++ b/modules/angular2/src/di/type_literal.ts @@ -0,0 +1,7 @@ +/** + * Type literals is a Dart-only feature. This is here only so we can x-compile + * to multiple languages. + */ +export class TypeLiteral { + get type(): any { throw new Error("Type literals are only supported in Dart"); } +} diff --git a/modules/angular2/test/core/compiler/integration_dart_spec.dart b/modules/angular2/test/core/compiler/integration_dart_spec.dart new file mode 100644 index 0000000000..da60c5020e --- /dev/null +++ b/modules/angular2/test/core/compiler/integration_dart_spec.dart @@ -0,0 +1,45 @@ +/// This file contains tests that make sense only in Dart +library angular2.test.di.integration_dart_spec; + +import 'package:angular2/angular2.dart'; +import 'package:angular2/di.dart'; +import 'package:angular2/src/test_lib/test_bed.dart'; +import 'package:angular2/test_lib.dart'; + +main() { + describe('TypeLiteral', () { + it('should publish via injectables', + inject([TestBed, AsyncTestCompleter], (tb, async) { + tb.overrideView(Dummy, new View( + template: '', + directives: [TypeLiteralComponent] + )); + + tb.createView(Dummy).then((view) { + view.detectChanges(); + expect(view.rootNodes).toHaveText('[Hello, World]'); + async.done(); + }); + })); + }); +} + +@Component(selector: 'dummy') +class Dummy {} + +@Component( + selector: 'type-literal-component', + injectables: const [ + const Binding( + const TypeLiteral>(), + toValue: const ['Hello', 'World']) + ] +) +@View( + template: '{{list}}' +) +class TypeLiteralComponent { + final List list; + + TypeLiteralComponent(this.list); +} diff --git a/modules/angular2/test/di/injector_dart_spec.dart b/modules/angular2/test/di/injector_dart_spec.dart new file mode 100644 index 0000000000..ff6a756106 --- /dev/null +++ b/modules/angular2/test/di/injector_dart_spec.dart @@ -0,0 +1,22 @@ +/// This file contains tests that make sense only in Dart +library angular2.test.di.injector_dart_spec; + +import 'package:angular2/test_lib.dart'; +import 'package:angular2/di.dart'; + +main() { + describe('Injector', () { + it('should support TypeLiteral', () { + var i = Injector.resolveAndCreate([ + bind(new TypeLiteral>()).toValue([1, 2, 3]), + Foo, + ]); + expect(i.get(Foo).value).toEqual([1, 2, 3]); + }); + }); +} + +class Foo { + final List value; + Foo(this.value); +} diff --git a/modules/angular2/test/di/key_dart_spec.dart b/modules/angular2/test/di/key_dart_spec.dart new file mode 100644 index 0000000000..47ae22cd72 --- /dev/null +++ b/modules/angular2/test/di/key_dart_spec.dart @@ -0,0 +1,39 @@ +/// This file contains tests that make sense only in Dart +library angular2.test.di.key_dart_spec; + +import 'package:angular2/test_lib.dart'; +import 'package:angular2/di.dart'; + +main() { + describe('TypeLiteral', () { + it('contains type', () { + var t = new TypeLiteral>(); + expect('${t.type}').toEqual('List'); + }); + + it('can be a constant', () { + var a = const TypeLiteral>(); + var b = const TypeLiteral>(); + expect(identical(a, b)).toBe(true); + }); + + it('can be unique', () { + var a = const TypeLiteral>(); + var b = const TypeLiteral>(); + expect(identical(a, b)).toBe(false); + }); + }); + + describe('Key', () { + KeyRegistry registry; + + beforeEach(() { + registry = new KeyRegistry(); + }); + + it('understands TypeLiteral', () { + var k = registry.get(const TypeLiteral>()); + expect('${k.token}').toEqual('List'); + }); + }); +}