From defc79f8004fa04b39d6c3c1a28bf69ffa7338b3 Mon Sep 17 00:00:00 2001 From: Andrew Archibald Date: Sat, 27 Jun 2015 11:47:09 -0700 Subject: [PATCH 01/14] Add bardjs definitions --- bardjs/bardjs.d.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 bardjs/bardjs.d.ts diff --git a/bardjs/bardjs.d.ts b/bardjs/bardjs.d.ts new file mode 100644 index 000000000..77fc799b1 --- /dev/null +++ b/bardjs/bardjs.d.ts @@ -0,0 +1,30 @@ +// Type definitions for bardjs 0.1.4 +// Project: https://github.com/wardbell/bardjs +// Definitions by: Andrew Archibald +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// +/// +/// + +declare module bard { + function $httpBackend($provide: angular.auto.IProvideService); + function $q($provide: angular.auto.IProvideService); + function addGlobals(...args: any[]): void; + function appModule(...args: any[]); + function assertFail(message: string): Chai.AssertionError; + function asyncModule(...args: any[]); + function debugging(x); + function fakeLogger($provide: angular.auto.IProvideService): void; + function fakeRouteHelperProvider($provide: angular.auto.IProvideService): void; + function fakeRouteProvider($provide: angular.auto.IProvideService): void; + function fakeStateProvider($provide: angular.auto.IProvideService): void; + function fakeToastr($provide: angular.auto.IProvideService): void; + function inject(...args: any[]): void; + function log(msg); + function mochaRunnerListener(runner: Mocha.IRunner): void; + function mockService(service, config); + function replaceAccentChars(s: string): string; + function verifyNoOutstandingHttpRequests(): void; + function wrapWithDone(callback: Function, done: Function); +} From 79daa85ac468dc65ba208c775e5ef53780279b4f Mon Sep 17 00:00:00 2001 From: Andrew Archibald Date: Sat, 27 Jun 2015 21:13:51 -0700 Subject: [PATCH 02/14] Add more specific types and comments to bardjs --- bardjs/bardjs.d.ts | 166 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 155 insertions(+), 11 deletions(-) diff --git a/bardjs/bardjs.d.ts b/bardjs/bardjs.d.ts index 77fc799b1..328d5b067 100644 --- a/bardjs/bardjs.d.ts +++ b/bardjs/bardjs.d.ts @@ -7,24 +7,168 @@ /// /// +/** + * Module for bardjs functions + */ declare module bard { - function $httpBackend($provide: angular.auto.IProvideService); - function $q($provide: angular.auto.IProvideService); - function addGlobals(...args: any[]): void; - function appModule(...args: any[]); + /** + * Replaces the ngMock'ed $httpBackend with the real one from ng thus + * restoring the ability to issue AJAX calls to the backend with $http. + * + * Note that $q remains ngMocked so you must flush $http calls ($rootScope.$digest). + * Use $rootScope.$apply() for this purpose. + * + * Could restore $q with $qReal in which case don't need to flush. + */ + function $httpBackend($provide: angular.auto.IProvideService): any; + + /** + * Replaces the ngMock'ed $q with the real one from ng thus + * obviating the need to flush $http and $q queues + * at the expense of ability to control $q timing. + */ + function $q($provide: angular.auto.IProvideService): any; + + /** + * Add names of globals to list of OK globals for this mocha spec + * NB: Call this method ONLY if you're using mocha! + * NB: Turn off browser-sync else mocha detects the browser-sync globals + * like ` ___browserSync___` + */ + function addGlobals(...globals: any[]): void; + + /** + * Prepare ngMocked application feature module + * along with faked toastr, routehelper, + * and faked router services. + * Especially useful for controller testing + * Use it as you would the ngMocks#module method + * + * DO NOT USE IF YOU NEED THE REAL ROUTER SERVICES! + * Fall back to `angular.mock.module(...)` or just `module(...)` + */ + function appModule(...args: any[]): any; + + /** + * Assert a failure in mocha, without condition + */ function assertFail(message: string): Chai.AssertionError; - function asyncModule(...args: any[]); - function debugging(x); + + /** + * Prepare ngMocked module definition that makes real $http and $q calls + * Also adds fakeLogger to the end of the definition + * Use it as you would the ngMocks#module method + */ + function asyncModule(...args: any[]): any; + + /** + * Get or set bard debugging flag + */ + function debugging(newFlag?: any): boolean; + + /** + * Creates a fake logger to be used in testing + */ function fakeLogger($provide: angular.auto.IProvideService): void; + + /** + * Creates a fake route helper provider to be used in testing + */ function fakeRouteHelperProvider($provide: angular.auto.IProvideService): void; + + /** + * Stub out the $routeProvider so we avoid + * all routing calls, including the default route + * which runs on every test otherwise. + * Make sure this goes before the inject in the spec. + */ function fakeRouteProvider($provide: angular.auto.IProvideService): void; + + /** + * Stub out the $stateProvider so we avoid + * all routing calls, including the default state + * which runs on every test otherwise. + * Make sure this goes before the inject in the spec. + */ function fakeStateProvider($provide: angular.auto.IProvideService): void; + + /** + * Creates a fake toastr to be used in testing + */ function fakeToastr($provide: angular.auto.IProvideService): void; - function inject(...args: any[]): void; - function log(msg); + + /** + * Inject selected services into the windows object during test + * then remove them when test ends with an `afterEach`. + * + * spares us the repetition of creating common service vars and injecting them + * + * Option: the first argument may be the mocha spec context object (`this`) + * It MUST be `this` if you what to check for mocha global leaks. + * Do NOT supply `this` as the first arg if you're not running mocha specs. + * + * remaining inject arguments may take one of 3 forms : + * + * function - This fn will be passed to ngMocks.inject. + * Annotations extracted after inject does its thing. + * [strings] - same string array you'd use to set fn.$inject + * (...string) - string arguments turned into a string array + */ + function inject(context?: any, ...args: string[]): void; + + /** + * Write to console if bard debugging flag is on + */ + function log(message: any): void; + + /** + * Listen to mocha test runner events + * Usage in browser: + * var runner = mocha.run(); + * bard.mochaRunnerListener(runner); + */ function mochaRunnerListener(runner: Mocha.IRunner): void; - function mockService(service, config); - function replaceAccentChars(s: string): string; + + /** + * Mocks out a service with sinon stubbed functions + * that return the values specified in the config + * + * If the config value is `undefined`, + * stub the service method with a dummy that doesn't return a value + * + * If the config value is a function, set service property with it + * + * If a service member is a property, not a function, + * set it with the config value + * If a service member name is not a key in config, + * follow the same logic as above to set its members + * using the config._default value (which is `undefined` if omitted) + * + * If there is a config entry that is NOT a member of the service + * add mocked function to the service using the config value + */ + function mockService(service: any, config: any): any; + + /** + * Replaces the accented characters of many European languages w/ unaccented chars + * Use it in JavaScript string sorts where such characters may be encountered + * Matches the default string comparers of most databases. + * Ex: replaceAccentChars(a.Name) < replaceAccentChars(b.Name) + * instead of: a.Name < b.Name + */ + function replaceAccentChars(text: string): string; + + /** + * Assert that there are no outstanding HTTP requests after test is complete + * For use with ngMocks; doesn't work for async server integration tests + */ function verifyNoOutstandingHttpRequests(): void; - function wrapWithDone(callback: Function, done: Function); + + /** + * Returns a function that execute a callback function + * (typically a fn making asserts) within a try/catch + * The try/catch then calls the ambient "done" function + * in the appropriate way for both success and failure + */ + function wrapWithDone(callback: Function, done: Function): Function; } From 9d20855e549fd43fcb71221f05e75be0e3e57418 Mon Sep 17 00:00:00 2001 From: Andrew Archibald Date: Sat, 27 Jun 2015 21:30:18 -0700 Subject: [PATCH 03/14] Made more specific comments in bardjs --- bardjs/bardjs.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bardjs/bardjs.d.ts b/bardjs/bardjs.d.ts index 328d5b067..e0288825f 100644 --- a/bardjs/bardjs.d.ts +++ b/bardjs/bardjs.d.ts @@ -67,12 +67,12 @@ declare module bard { function debugging(newFlag?: any): boolean; /** - * Creates a fake logger to be used in testing + * Registers a fake logger service that you can spy on */ function fakeLogger($provide: angular.auto.IProvideService): void; /** - * Creates a fake route helper provider to be used in testing + * Registers a fake route helper provider service that you can spy on */ function fakeRouteHelperProvider($provide: angular.auto.IProvideService): void; @@ -93,7 +93,7 @@ declare module bard { function fakeStateProvider($provide: angular.auto.IProvideService): void; /** - * Creates a fake toastr to be used in testing + * Registers a fake toastr service that you can spy on */ function fakeToastr($provide: angular.auto.IProvideService): void; From f91aabedc10bb94dd657d550f858af68077bf527 Mon Sep 17 00:00:00 2001 From: Andrew Archibald Date: Wed, 15 Jul 2015 17:08:47 -0700 Subject: [PATCH 04/14] Started bardjs tests; Made some bardjs types more clear. --- bardjs/bardjs-tests.ts | 158 +++++++++++++++++++++++++++++++++++++++++ bardjs/bardjs.d.ts | 6 +- 2 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 bardjs/bardjs-tests.ts diff --git a/bardjs/bardjs-tests.ts b/bardjs/bardjs-tests.ts new file mode 100644 index 000000000..9243c1008 --- /dev/null +++ b/bardjs/bardjs-tests.ts @@ -0,0 +1,158 @@ +/// +/// +/// +/// +/// + +var expect = chai.expect, assert = chai.assert; + +function someFunction() { } +var someObject = { }; +var myService; + +/* + * bard.$httpBackend + */ +var myService; + +beforeEach(module(bard.$httpBackend, 'app')); + +beforeEach(inject(function(_myService_) { + myService = _myService_; +})); + +it('should return valid data', function(done) { + myService.remoteCall() + .then(function(data) { + expect(data).to.exist; + }) + .then(done, done); + + $rootScope.$apply; // because not using bard.$q, must flush the $http and $q queues +}); + +/* + * bard.$q + */ +beforeEach(module(bard.$q, bard.$httpBackend, 'app')); + +beforeEach(inject(function(_myService_) { + myService = _myService_; +})); + +it('should return valid data', (done) => { + myService.remoteCall() + .then((data) => { + expect(data).to.exist; + }) + .then(done, done); + + // no need to flush +}); + +/* + * bard.addGlobals + */ + +/* + * bard.appModule + */ +beforeEach(bard.appModule('myModule')); + +beforeEach(bard.appModule('myModule', someFunction, someObject)); + +/* + * bard.assertFail + */ + +/* + * bard.asyncModule + */ +beforeEach(bard.asyncModule('app')); + +beforeEach(bard.asyncModule('myModule', someFunction, someObject)); + +/* + * bard.debugging + */ + +/* + * bard.fakeLogger + */ +beforeEach(module('myModule', bard.fakeLogger)); +beforeEach(bard.appModule('myModule', bard.fakeLogger)); +beforeEach(bard.asyncModule('myModule', bard.fakeLogger)); + +/* + * bard.fakeRouteHelperProvider + */ +beforeEach(module('myModule', bard.fakeRouteHelperProvider)); +beforeEach(bard.appModule('myModule', bard.fakeRouteHelperProvider)); +beforeEach(bard.asyncModule('myModule', bard.fakeRouteHelperProvider)); + +/* + * bard.fakeRouteProvider + */ +beforeEach(module('myModule', bard.fakeRouteProvider)); +beforeEach(bard.appModule('myModule', bard.fakeRouteProvider)); +beforeEach(bard.asyncModule('myModule', bard.fakeRouteProvider)); + +/* + * bard.fakeStateProvider + */ +beforeEach(module('myModule', bard.fakeStateProvider)); +beforeEach(bard.appModule('myModule', bard.fakeStateProvider)); +beforeEach(bard.asyncModule('myModule', bard.fakeStateProvider)); + +/* + * bard.fakeToastr + */ +beforeEach(module('myModule', bard.fakeToastr)); +beforeEach(bard.appModule('myModule', bard.fakeToastr)); +beforeEach(bard.asyncModule('myModule', bard.fakeToastr)); + +/* + * bard.inject + */ +beforeEach(() => bard.inject(this, '$controller', '$log', '$q', '$rootScope', 'dataservice')); + +/* + * bard.log + */ +bard.log('We got the goods'); + +/* + * bard.mochaRunnerListener + */ + +/* + * bard.mockService + */ +var controller; +var avengers = [{name: 'Captain America' /* ... */}]; +var $controller, $q, $rootScope, dataservice; + +beforeEach(function() { + bard.appModule('app.avengers'); + bard.inject(this, '$controller', '$q', '$rootScope', 'dataservice'); + + bard.mockService(dataservice, { + getAvengers: $q.when(avengers), + _default: $q.when([]) + }); + + controller = $controller('Avengers'); + $rootScope.$apply(); +}); + +/* + * bard.replaceAccentChars + */ + +/* + * bard.verifyNoOutstandingHttpRequests + */ + +/* + * bard.wrapWithDone + */ diff --git a/bardjs/bardjs.d.ts b/bardjs/bardjs.d.ts index e0288825f..3d3ad5f22 100644 --- a/bardjs/bardjs.d.ts +++ b/bardjs/bardjs.d.ts @@ -47,7 +47,7 @@ declare module bard { * DO NOT USE IF YOU NEED THE REAL ROUTER SERVICES! * Fall back to `angular.mock.module(...)` or just `module(...)` */ - function appModule(...args: any[]): any; + function appModule(...fns: (string | Function | Object)[]): () => void; /** * Assert a failure in mocha, without condition @@ -59,7 +59,7 @@ declare module bard { * Also adds fakeLogger to the end of the definition * Use it as you would the ngMocks#module method */ - function asyncModule(...args: any[]): any; + function asyncModule(...fns: (string | Function | Object)[]): () => void; /** * Get or set bard debugging flag @@ -114,7 +114,7 @@ declare module bard { * [strings] - same string array you'd use to set fn.$inject * (...string) - string arguments turned into a string array */ - function inject(context?: any, ...args: string[]): void; + function inject(context?: Function, ...args: string[]): void; /** * Write to console if bard debugging flag is on From e28aef1c025ec1f6377cc1a4cbba5ac256e8840a Mon Sep 17 00:00:00 2001 From: Andrew Archibald Date: Wed, 15 Jul 2015 21:35:55 -0700 Subject: [PATCH 05/14] Fixed some issues with bard tests --- bardjs/bardjs-tests.ts | 372 +++++++++++++++++++++++++---------------- 1 file changed, 226 insertions(+), 146 deletions(-) diff --git a/bardjs/bardjs-tests.ts b/bardjs/bardjs-tests.ts index 9243c1008..d15cc3d18 100644 --- a/bardjs/bardjs-tests.ts +++ b/bardjs/bardjs-tests.ts @@ -4,155 +4,235 @@ /// /// -var expect = chai.expect, assert = chai.assert; +var expect = chai.expect, + assert = chai.assert; -function someFunction() { } -var someObject = { }; -var myService; - -/* - * bard.$httpBackend - */ -var myService; - -beforeEach(module(bard.$httpBackend, 'app')); - -beforeEach(inject(function(_myService_) { - myService = _myService_; -})); - -it('should return valid data', function(done) { - myService.remoteCall() - .then(function(data) { - expect(data).to.exist; - }) - .then(done, done); - - $rootScope.$apply; // because not using bard.$q, must flush the $http and $q queues -}); - -/* - * bard.$q - */ -beforeEach(module(bard.$q, bard.$httpBackend, 'app')); - -beforeEach(inject(function(_myService_) { - myService = _myService_; -})); - -it('should return valid data', (done) => { - myService.remoteCall() - .then((data) => { - expect(data).to.exist; - }) - .then(done, done); - - // no need to flush -}); - -/* - * bard.addGlobals - */ - -/* - * bard.appModule - */ -beforeEach(bard.appModule('myModule')); - -beforeEach(bard.appModule('myModule', someFunction, someObject)); - -/* - * bard.assertFail - */ - -/* - * bard.asyncModule - */ -beforeEach(bard.asyncModule('app')); - -beforeEach(bard.asyncModule('myModule', someFunction, someObject)); - -/* - * bard.debugging - */ - -/* - * bard.fakeLogger - */ -beforeEach(module('myModule', bard.fakeLogger)); -beforeEach(bard.appModule('myModule', bard.fakeLogger)); -beforeEach(bard.asyncModule('myModule', bard.fakeLogger)); - -/* - * bard.fakeRouteHelperProvider - */ -beforeEach(module('myModule', bard.fakeRouteHelperProvider)); -beforeEach(bard.appModule('myModule', bard.fakeRouteHelperProvider)); -beforeEach(bard.asyncModule('myModule', bard.fakeRouteHelperProvider)); - -/* - * bard.fakeRouteProvider - */ -beforeEach(module('myModule', bard.fakeRouteProvider)); -beforeEach(bard.appModule('myModule', bard.fakeRouteProvider)); -beforeEach(bard.asyncModule('myModule', bard.fakeRouteProvider)); - -/* - * bard.fakeStateProvider - */ -beforeEach(module('myModule', bard.fakeStateProvider)); -beforeEach(bard.appModule('myModule', bard.fakeStateProvider)); -beforeEach(bard.asyncModule('myModule', bard.fakeStateProvider)); - -/* - * bard.fakeToastr - */ -beforeEach(module('myModule', bard.fakeToastr)); -beforeEach(bard.appModule('myModule', bard.fakeToastr)); -beforeEach(bard.asyncModule('myModule', bard.fakeToastr)); - -/* - * bard.inject - */ -beforeEach(() => bard.inject(this, '$controller', '$log', '$q', '$rootScope', 'dataservice')); - -/* - * bard.log - */ -bard.log('We got the goods'); - -/* - * bard.mochaRunnerListener - */ - -/* - * bard.mockService - */ -var controller; -var avengers = [{name: 'Captain America' /* ... */}]; -var $controller, $q, $rootScope, dataservice; - -beforeEach(function() { - bard.appModule('app.avengers'); - bard.inject(this, '$controller', '$q', '$rootScope', 'dataservice'); - - bard.mockService(dataservice, { - getAvengers: $q.when(avengers), - _default: $q.when([]) +class MyService { + static $inject = ['$q']; + + constructor(private $q: angular.IQService) {} + + remoteCall(): angular.IPromise { + return new this.$q((resolve, reject) => { + resolve('Here is some data'); }); + } +} - controller = $controller('Avengers'); - $rootScope.$apply(); -}); +function myService($q: angular.IQService) { + return new MyService($q); +} -/* - * bard.replaceAccentChars - */ +angular + .module('tests') + .service('myService', myService); -/* - * bard.verifyNoOutstandingHttpRequests - */ -/* - * bard.wrapWithDone - */ + +module bardTests { + /* + * bard.$httpBackend + */ + function test_$httpBackend() { + var myService: MyService; + var $rootScope: any; + + beforeEach(module(bard.$httpBackend, 'app')); + + beforeEach(inject(function(_myService_: MyService, _$rootScope_: any) { + myService = _myService_; + $rootScope = _$rootScope_; + })); + + it('should return valid data', function(done) { + myService.remoteCall() + .then(function(data) { + expect(data).to.exist; + }) + .then(done, done); + + $rootScope.$apply; // because not using bard.$q, must flush the $http and $q queues + }); + } + + /* + * bard.$q + */ + function test_$q() { + var myService: MyService; + + beforeEach(module(bard.$q, bard.$httpBackend, 'app')); + + beforeEach(inject(function(_myService_) { + myService = _myService_; + })); + + it('should return valid data', (done) => { + myService.remoteCall() + .then((data) => { + expect(data).to.exist; + }) + .then(done, done); + + // no need to flush + }); + } + + /* + * bard.addGlobals + */ + function test_addGlobals() { + + } + + /* + * bard.appModule + */ + function test_appModule() { + beforeEach(bard.appModule('myModule')); + //// + beforeEach(bard.appModule('myModule', function() {}, {})); + } + + /* + * bard.assertFail + */ + function test_assertFail() { + + } + + /* + * bard.asyncModule + */ + function test_asyncModule() { + beforeEach(bard.asyncModule('app')); + //// + beforeEach(bard.asyncModule('myModule', function() {}, {})); + } + + /* + * bard.debugging + */ + function test_debugging() { + + } + + /* + * bard.fakeLogger + */ + function test_fakeLogger() { + beforeEach(module('myModule', bard.fakeLogger)); + //// + beforeEach(bard.appModule('myModule', bard.fakeLogger)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeLogger)); + } + + /* + * bard.fakeRouteHelperProvider + */ + function test_fakeRouteHelperProvider() { + beforeEach(module('myModule', bard.fakeRouteHelperProvider)); + //// + beforeEach(bard.appModule('myModule', bard.fakeRouteHelperProvider)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeRouteHelperProvider)); + } + + /* + * bard.fakeRouteProvider + */ + function test_fakeRouteProvider() { + beforeEach(module('myModule', bard.fakeRouteProvider)); + //// + beforeEach(bard.appModule('myModule', bard.fakeRouteProvider)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeRouteProvider)); + } + + /* + * bard.fakeStateProvider + */ + function test_fakeStateProvider() { + beforeEach(module('myModule', bard.fakeStateProvider)); + //// + beforeEach(bard.appModule('myModule', bard.fakeStateProvider)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeStateProvider)); + } + + /* + * bard.fakeToastr + */ + function test_fakeToastr() { + beforeEach(module('myModule', bard.fakeToastr)); + //// + beforeEach(bard.appModule('myModule', bard.fakeToastr)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeToastr)); + } + + /* + * bard.inject + */ + function test_inject() { + beforeEach(() => bard.inject(this, '$controller', '$log', '$q', '$rootScope', 'dataservice')); + } + + /* + * bard.log + */ + function test_log() { + bard.log('We got the goods'); + } + + /* + * bard.mochaRunnerListener + */ + function test_mochaRunnerListener() { + + } + + /* + * bard.mockService + */ + function test_mockService() { + var controller; + var avengers = [{name: 'Captain America' /* ... */}]; + var $controller, $q, $rootScope, dataservice; + + beforeEach(function() { + bard.appModule('app.avengers'); + bard.inject(this, '$controller', '$q', '$rootScope', 'dataservice'); + + bard.mockService(dataservice, { + getAvengers: $q.when(avengers), + _default: $q.when([]) + }); + + controller = $controller('Avengers'); + $rootScope.$apply(); + }); + } + + /* + * bard.replaceAccentChars + */ + function test_replaceAccentChars() { + + } + + /* + * bard.verifyNoOutstandingHttpRequests + */ + function test_verifyNoOutstandingHttpRequests() { + + } + + /* + * bard.wrapWithDone + */ + function test_wrapWithDone() { + + } +} \ No newline at end of file From e80350d66819f909e50bbccef3e74f6205fb95a6 Mon Sep 17 00:00:00 2001 From: Andrew Archibald Date: Wed, 15 Jul 2015 21:51:59 -0700 Subject: [PATCH 06/14] Fixed some bardjs implicit 'any' types --- bardjs/bardjs-tests.ts | 67 +++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/bardjs/bardjs-tests.ts b/bardjs/bardjs-tests.ts index d15cc3d18..8083fd3f2 100644 --- a/bardjs/bardjs-tests.ts +++ b/bardjs/bardjs-tests.ts @@ -4,42 +4,40 @@ /// /// -var expect = chai.expect, - assert = chai.assert; - -class MyService { - static $inject = ['$q']; - - constructor(private $q: angular.IQService) {} - - remoteCall(): angular.IPromise { - return new this.$q((resolve, reject) => { - resolve('Here is some data'); - }); - } -} - -function myService($q: angular.IQService) { - return new MyService($q); -} - -angular - .module('tests') - .service('myService', myService); - - - module bardTests { + var expect = chai.expect, + assert = chai.assert; + + class MyService { + static $inject = ['$q']; + + constructor(private $q: angular.IQService) {} + + remoteCall(): angular.IPromise { + return new this.$q((resolve, reject) => { + resolve(['Hello', 'World']); + }); + } + } + + function myService($q: angular.IQService) { + return new MyService($q); + } + + angular + .module('bardTests') + .service('myService', myService); + /* * bard.$httpBackend */ function test_$httpBackend() { var myService: MyService; - var $rootScope: any; + var $rootScope: angular.IRootScopeService; beforeEach(module(bard.$httpBackend, 'app')); - beforeEach(inject(function(_myService_: MyService, _$rootScope_: any) { + beforeEach(inject(function(_myService_: MyService, _$rootScope_: angular.IRootScopeService) { myService = _myService_; $rootScope = _$rootScope_; })); @@ -51,7 +49,7 @@ module bardTests { }) .then(done, done); - $rootScope.$apply; // because not using bard.$q, must flush the $http and $q queues + $rootScope.$apply; // Because not using bard.$q, must flush the $http and $q queues }); } @@ -63,7 +61,7 @@ module bardTests { beforeEach(module(bard.$q, bard.$httpBackend, 'app')); - beforeEach(inject(function(_myService_) { + beforeEach(inject(function(_myService_: MyService) { myService = _myService_; })); @@ -74,7 +72,7 @@ module bardTests { }) .then(done, done); - // no need to flush + // No need to flush }); } @@ -176,7 +174,7 @@ module bardTests { * bard.inject */ function test_inject() { - beforeEach(() => bard.inject(this, '$controller', '$log', '$q', '$rootScope', 'dataservice')); + beforeEach(() => bard.inject(this, '$controller', '$log', '$q', '$rootScope', 'myService')); } /* @@ -199,11 +197,14 @@ module bardTests { function test_mockService() { var controller; var avengers = [{name: 'Captain America' /* ... */}]; - var $controller, $q, $rootScope, dataservice; + var $controller: angular.IControllerService, + $q: angular.IQService, + $rootScope: angular.IRootScopeService, + myService: MyService; beforeEach(function() { bard.appModule('app.avengers'); - bard.inject(this, '$controller', '$q', '$rootScope', 'dataservice'); + bard.inject(this, '$controller', '$q', '$rootScope', 'myService'); bard.mockService(dataservice, { getAvengers: $q.when(avengers), From 6de3f1f79c3470d02f41f9319839879c74b1328b Mon Sep 17 00:00:00 2001 From: Andrew Archibald Date: Wed, 15 Jul 2015 22:19:10 -0700 Subject: [PATCH 07/14] Added more tests to bardjs-tests.ts --- bardjs/bardjs-tests.ts | 462 ++++++++++++++++++++++------------------- 1 file changed, 253 insertions(+), 209 deletions(-) diff --git a/bardjs/bardjs-tests.ts b/bardjs/bardjs-tests.ts index 8083fd3f2..3eedd970b 100644 --- a/bardjs/bardjs-tests.ts +++ b/bardjs/bardjs-tests.ts @@ -5,235 +5,279 @@ /// module bardTests { - var expect = chai.expect, - assert = chai.assert; + var expect = chai.expect, + assert = chai.assert; - class MyService { - static $inject = ['$q']; + class MyService { + static $inject = ['$q']; - constructor(private $q: angular.IQService) {} + constructor(private $q: angular.IQService) {} - remoteCall(): angular.IPromise { - return new this.$q((resolve, reject) => { - resolve(['Hello', 'World']); - }); + remoteCall(): angular.IPromise { + return new this.$q((resolve, reject) => { + resolve(['Hello', 'World']); + }); + } } - } - function myService($q: angular.IQService) { - return new MyService($q); - } + function myService($q: angular.IQService) { + return new MyService($q); + } - angular - .module('bardTests') - .service('myService', myService); + class MyController { + myProperty: string; + } - /* - * bard.$httpBackend - */ - function test_$httpBackend() { - var myService: MyService; - var $rootScope: angular.IRootScopeService; + angular + .module('myModule') + .service('myService', myService) + .controller('MyController', MyController); - beforeEach(module(bard.$httpBackend, 'app')); + /* + * bard.$httpBackend + */ + function test_$httpBackend() { + var myService: MyService; + var $rootScope: angular.IRootScopeService; - beforeEach(inject(function(_myService_: MyService, _$rootScope_: angular.IRootScopeService) { - myService = _myService_; - $rootScope = _$rootScope_; - })); + beforeEach(module(bard.$httpBackend, 'myModule')); - it('should return valid data', function(done) { - myService.remoteCall() - .then(function(data) { - expect(data).to.exist; + beforeEach(inject(function(_myService_: MyService, _$rootScope_: angular.IRootScopeService) { + myService = _myService_; + $rootScope = _$rootScope_; + })); + + it('should return valid data', function(done) { + myService.remoteCall() + .then(function(data) { + expect(data).to.exist; + }) + .then(done, done); + + $rootScope.$apply; // Because not using bard.$q, must flush the $http and $q queues + }); + } + + /* + * bard.$q + */ + function test_$q() { + var myService: MyService; + + beforeEach(module(bard.$q, bard.$httpBackend, 'myModule')); + + beforeEach(inject(function(_myService_: MyService) { + myService = _myService_; + })); + + it('should return valid data', (done) => { + myService.remoteCall() + .then((data) => { + expect(data).to.exist; + }) + .then(done, done); + + // No need to flush + }); + } + + /* + * bard.addGlobals + */ + function test_addGlobals() { + describe('someting', function() { + var ctx = this; + + it('should work', function() { + var bar = 'bar'; + bard.addGlobals(this, 'foo'); // where `this` is the spec context + bard.addGlobals(this, 'foo', bar); + bard.addGlobals.bind(this)('foo', 'bar'); + bard.addGlobals(ctx, ['foo', 'bar']) // where ctx is the spec context + }); }) - .then(done, done); + } - $rootScope.$apply; // Because not using bard.$q, must flush the $http and $q queues - }); - } - - /* - * bard.$q - */ - function test_$q() { - var myService: MyService; + /* + * bard.appModule + */ + function test_appModule() { + beforeEach(bard.appModule('myModule')); + //// + beforeEach(bard.appModule('myModule', function() {}, {})); + } - beforeEach(module(bard.$q, bard.$httpBackend, 'app')); + /* + * bard.assertFail + */ + function test_assertFail() { + bard.assertFail('FAIL!'); + } - beforeEach(inject(function(_myService_: MyService) { - myService = _myService_; - })); + /* + * bard.asyncModule + */ + function test_asyncModule() { + beforeEach(bard.asyncModule('myModule')); + //// + beforeEach(bard.asyncModule('myModule', function() {}, {})); + } - it('should return valid data', (done) => { - myService.remoteCall() - .then((data) => { - expect(data).to.exist; - }) - .then(done, done); + /* + * bard.debugging + */ + function test_debugging() { + console.log( + bard.debugging(true), // should be true + bard.debugging(false), // should be false + bard.debugging(42), // should be true + bard.debugging(''), // should be false + bard.debugging() // should be false + ); + } - // No need to flush - }); - } - - /* - * bard.addGlobals - */ - function test_addGlobals() { - - } + /* + * bard.fakeLogger + */ + function test_fakeLogger() { + beforeEach(module('myModule', bard.fakeLogger)); + //// + beforeEach(bard.appModule('myModule', bard.fakeLogger)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeLogger)); + } - /* - * bard.appModule - */ - function test_appModule() { - beforeEach(bard.appModule('myModule')); - //// - beforeEach(bard.appModule('myModule', function() {}, {})); - } + /* + * bard.fakeRouteHelperProvider + */ + function test_fakeRouteHelperProvider() { + beforeEach(module('myModule', bard.fakeRouteHelperProvider)); + //// + beforeEach(bard.appModule('myModule', bard.fakeRouteHelperProvider)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeRouteHelperProvider)); + } - /* - * bard.assertFail - */ - function test_assertFail() { - - } + /* + * bard.fakeRouteProvider + */ + function test_fakeRouteProvider() { + beforeEach(module('myModule', bard.fakeRouteProvider)); + //// + beforeEach(bard.appModule('myModule', bard.fakeRouteProvider)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeRouteProvider)); + } - /* - * bard.asyncModule - */ - function test_asyncModule() { - beforeEach(bard.asyncModule('app')); - //// - beforeEach(bard.asyncModule('myModule', function() {}, {})); - } - - /* - * bard.debugging - */ - function test_debugging() { - - } - - /* - * bard.fakeLogger - */ - function test_fakeLogger() { - beforeEach(module('myModule', bard.fakeLogger)); - //// - beforeEach(bard.appModule('myModule', bard.fakeLogger)); - //// - beforeEach(bard.asyncModule('myModule', bard.fakeLogger)); - } - - /* - * bard.fakeRouteHelperProvider - */ - function test_fakeRouteHelperProvider() { - beforeEach(module('myModule', bard.fakeRouteHelperProvider)); - //// - beforeEach(bard.appModule('myModule', bard.fakeRouteHelperProvider)); - //// - beforeEach(bard.asyncModule('myModule', bard.fakeRouteHelperProvider)); - } - - /* - * bard.fakeRouteProvider - */ - function test_fakeRouteProvider() { - beforeEach(module('myModule', bard.fakeRouteProvider)); - //// - beforeEach(bard.appModule('myModule', bard.fakeRouteProvider)); - //// - beforeEach(bard.asyncModule('myModule', bard.fakeRouteProvider)); - } - - /* - * bard.fakeStateProvider - */ - function test_fakeStateProvider() { - beforeEach(module('myModule', bard.fakeStateProvider)); - //// - beforeEach(bard.appModule('myModule', bard.fakeStateProvider)); - //// - beforeEach(bard.asyncModule('myModule', bard.fakeStateProvider)); - } - - /* - * bard.fakeToastr - */ - function test_fakeToastr() { - beforeEach(module('myModule', bard.fakeToastr)); - //// - beforeEach(bard.appModule('myModule', bard.fakeToastr)); - //// - beforeEach(bard.asyncModule('myModule', bard.fakeToastr)); - } - - /* - * bard.inject - */ - function test_inject() { - beforeEach(() => bard.inject(this, '$controller', '$log', '$q', '$rootScope', 'myService')); - } - - /* - * bard.log - */ - function test_log() { - bard.log('We got the goods'); - } - - /* - * bard.mochaRunnerListener - */ - function test_mochaRunnerListener() { - - } - - /* - * bard.mockService - */ - function test_mockService() { - var controller; - var avengers = [{name: 'Captain America' /* ... */}]; - var $controller: angular.IControllerService, - $q: angular.IQService, - $rootScope: angular.IRootScopeService, - myService: MyService; + /* + * bard.fakeStateProvider + */ + function test_fakeStateProvider() { + beforeEach(module('myModule', bard.fakeStateProvider)); + //// + beforeEach(bard.appModule('myModule', bard.fakeStateProvider)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeStateProvider)); + } - beforeEach(function() { - bard.appModule('app.avengers'); - bard.inject(this, '$controller', '$q', '$rootScope', 'myService'); + /* + * bard.fakeToastr + */ + function test_fakeToastr() { + beforeEach(module('myModule', bard.fakeToastr)); + //// + beforeEach(bard.appModule('myModule', bard.fakeToastr)); + //// + beforeEach(bard.asyncModule('myModule', bard.fakeToastr)); + } - bard.mockService(dataservice, { - getAvengers: $q.when(avengers), - _default: $q.when([]) - }); + /* + * bard.inject + */ + function test_inject() { + beforeEach(() => bard.inject(this, '$controller', '$log', '$q', '$rootScope', 'myService')); + } - controller = $controller('Avengers'); - $rootScope.$apply(); - }); - } - - /* - * bard.replaceAccentChars - */ - function test_replaceAccentChars() { - - } - - /* - * bard.verifyNoOutstandingHttpRequests - */ - function test_verifyNoOutstandingHttpRequests() { - - } - - /* - * bard.wrapWithDone - */ - function test_wrapWithDone() { - - } + /* + * bard.log + */ + function test_log() { + bard.log('We got the goods'); + } + + /* + * bard.mochaRunnerListener + */ + function test_mochaRunnerListener() { + var runner = mocha.run(); + bard.mochaRunnerListener(runner); + } + + /* + * bard.mockService + */ + function test_mockService() { + var controller: MyController, + myArray = ['This', 'is', 'some', 'mocked', 'data']; + var $controller: angular.IControllerService, + $q: angular.IQService, + $rootScope: angular.IRootScopeService, + myService: MyService; + + beforeEach(function() { + bard.appModule('myModule'); + bard.inject(this, '$controller', '$q', '$rootScope', 'myService'); + + bard.mockService(myService, { + remoteCall: $q.when(myArray), + _default: $q.when([]) + }); + + controller = $controller('MyController'); + $rootScope.$apply(); + }); + } + + /* + * bard.replaceAccentChars + */ + function test_replaceAccentChars() { + console.log(bard.replaceAccentChars('àáâãäåèéêëìíîïòóôõöùúûüýÿ') === 'aaaaaaeeeeeeeeooooouuuuyy'); + } + + /* + * bard.verifyNoOutstandingHttpRequests + */ + function test_verifyNoOutstandingHttpRequests() { + var controller: MyController, + myArray = ['This', 'is', 'some', 'mocked', 'data']; + var $controller: angular.IControllerService, + $q: angular.IQService, + $rootScope: angular.IRootScopeService, + myService: MyService; + + beforeEach(function() { + bard.appModule('myModule'); + bard.inject(this, '$controller', '$q', '$rootScope', 'myService'); + + bard.mockService(myService, { + remoteCall: $q.when(myArray), + _default: $q.when([]) + }); + + controller = $controller('MyController'); + $rootScope.$apply(); + }); + + bard.verifyNoOutstandingHttpRequests(); + } + + /* + * bard.wrapWithDone + */ + function test_wrapWithDone() { + function callback() { console.log('Doing something...'); } + function done() { console.log('...Done'); } + bard.wrapWithDone(callback, done); + } } \ No newline at end of file From e6b1d5f73b03df88acabef15a393e4a52e19e681 Mon Sep 17 00:00:00 2001 From: Andrew Archibald Date: Thu, 16 Jul 2015 09:49:34 -0700 Subject: [PATCH 08/14] Formatted bardjs tests. --- bardjs/bardjs-tests.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bardjs/bardjs-tests.ts b/bardjs/bardjs-tests.ts index 3eedd970b..3aba6cade 100644 --- a/bardjs/bardjs-tests.ts +++ b/bardjs/bardjs-tests.ts @@ -128,11 +128,11 @@ module bardTests { */ function test_debugging() { console.log( - bard.debugging(true), // should be true - bard.debugging(false), // should be false - bard.debugging(42), // should be true - bard.debugging(''), // should be false - bard.debugging() // should be false + bard.debugging(true), // should return true + bard.debugging(false), // should return false + bard.debugging(42), // should return true + bard.debugging(''), // should return false + bard.debugging() // should return false ); } @@ -280,4 +280,4 @@ module bardTests { function done() { console.log('...Done'); } bard.wrapWithDone(callback, done); } -} \ No newline at end of file +} From 7fcdbd1a9b5ebdf39b5ac8e51ce72adfde685d74 Mon Sep 17 00:00:00 2001 From: Andrew Archibald Date: Thu, 16 Jul 2015 10:14:38 -0700 Subject: [PATCH 09/14] Made myService in bard tests minification safe --- bardjs/bardjs-tests.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bardjs/bardjs-tests.ts b/bardjs/bardjs-tests.ts index 3aba6cade..b033ffe3c 100644 --- a/bardjs/bardjs-tests.ts +++ b/bardjs/bardjs-tests.ts @@ -9,8 +9,6 @@ module bardTests { assert = chai.assert; class MyService { - static $inject = ['$q']; - constructor(private $q: angular.IQService) {} remoteCall(): angular.IPromise { @@ -20,6 +18,7 @@ module bardTests { } } + myService.$inject = ['$q']; function myService($q: angular.IQService) { return new MyService($q); } From 212793c4be051977f73675fa9bb125d891df037a Mon Sep 17 00:00:00 2001 From: Jeff Cross Date: Thu, 16 Jul 2015 15:59:19 -0700 Subject: [PATCH 10/14] angular2: add typings for angular2 and angular2 router --- angular2/angular2-2.0.0-alpha.31.d.ts | 6137 ++++++++++++++++++++++++ angular2/angular2.d.ts | 6218 +++++++++++-------------- angular2/router-2.0.0-alpha.31.d.ts | 459 ++ angular2/router.d.ts | 325 +- 4 files changed, 9552 insertions(+), 3587 deletions(-) create mode 100644 angular2/angular2-2.0.0-alpha.31.d.ts create mode 100644 angular2/router-2.0.0-alpha.31.d.ts diff --git a/angular2/angular2-2.0.0-alpha.31.d.ts b/angular2/angular2-2.0.0-alpha.31.d.ts new file mode 100644 index 000000000..dfad7d349 --- /dev/null +++ b/angular2/angular2-2.0.0-alpha.31.d.ts @@ -0,0 +1,6137 @@ +// Type definitions for Angular v2.0.0-alpha.31 +// Project: http://angular.io/ +// Definitions by: angular team +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +// *********************************************************** +// This file is generated by the Angular build process. +// Please do not create manual edits or send pull requests +// modifying this file. +// *********************************************************** + +// Angular depends transitively on these libraries. +// If you don't have them installed you can run +// $ tsd query es6-promise rx rx-lite --action install --save +/// +/// + +interface List extends Array {} +interface Map {} +interface StringMap extends Map {} + +declare module ng { + type SetterFn = typeof Function; + type int = number; + interface Type extends Function { + new (...args:any[]):any; + } + + // See https://github.com/Microsoft/TypeScript/issues/1168 + class BaseException /* extends Error */ { + message: string; + stack: string; + toString(): string; + } + interface InjectableReference {} +} + + + + +/** + * The `angular2` is the single place to import all of the individual types. + */ +declare module ng { + class DehydratedException extends BaseException { + } + + class ExpressionChangedAfterItHasBeenChecked extends BaseException { + } + + class ChangeDetectionError extends BaseException { + + location: string; + } + + + /** + * ON_PUSH means that the change detector's mode will be set to CHECK_ONCE during hydration. + */ + var ON_PUSH:any; + + + /** + * DEFAULT means that the change detector's mode will be set to CHECK_ALWAYS during hydration. + */ + var DEFAULT:any; + + + /** + * Controls change detection. + * + * {@link ChangeDetectorRef} allows requesting checks for detectors that rely on observables. It + * also allows detaching and + * attaching change detector subtrees. + */ + class ChangeDetectorRef { + + + /** + * Request to check all ON_PUSH ancestors. + */ + requestCheck(): void; + + + /** + * Detaches the change detector from the change detector tree. + * + * The detached change detector will not be checked until it is reattached. + */ + detach(): void; + + + /** + * Reattach the change detector to the change detector tree. + * + * This also requests a check of this change detector. This reattached change detector will be + * checked during the + * next change detection run. + */ + reattach(): void; + } + + class Pipes { + + + /** + * Map of {@link Pipe} names to {@link PipeFactory} lists used to configure the + * {@link Pipes} registry. + * + * #Example + * + * ``` + * var pipesConfig = { + * 'json': [jsonPipeFactory] + * } + * @Component({ + * viewInjector: [ + * bind(Pipes).toValue(new Pipes(pipesConfig)) + * ] + * }) + * ``` + */ + config: StringMap; + + get(type: string, obj: any, cdRef?: ChangeDetectorRef, existingPipe?: Pipe): Pipe; + } + + + /** + * Indicates that the result of a {@link Pipe} transformation has changed even though the reference + * has not changed. + * + * The wrapped value will be unwrapped by change detection, and the unwrapped value will be stored. + */ + class WrappedValue { + + wrapped: any; + } + + + /** + * An interface for extending the list of pipes known to Angular. + * + * If you are writing a custom {@link Pipe}, you must extend this interface. + * + * #Example + * + * ``` + * class DoublePipe implements Pipe { + * supports(obj) { + * return true; + * } + * + * onDestroy() {} + * + * transform(value, args = []) { + * return `${value}${value}`; + * } + * } + * ``` + */ + interface Pipe { + + supports(obj: any): boolean; + + onDestroy(): void; + + transform(value: any, args: List): any; + } + + interface PipeFactory { + + supports(obs: any): boolean; + + create(cdRef: ChangeDetectorRef): Pipe; + } + + class NullPipe extends BasePipe { + + called: boolean; + + supports(obj: any): boolean; + + transform(value: any, args?: List): WrappedValue; + } + + class NullPipeFactory implements PipeFactory { + + supports(obj: any): boolean; + + create(cdRef: ChangeDetectorRef): Pipe; + } + + var defaultPipes : Pipes ; + + + /** + * Provides default implementation of supports and onDestroy. + * + * #Example + * + * ``` + * class DoublePipe extends BasePipe {* + * transform(value) { + * return `${value}${value}`; + * } + * } + * ``` + */ + class BasePipe implements Pipe { + + supports(obj: any): boolean; + + onDestroy(): void; + + transform(value: any, args: List): any; + } + + class Locals { + + parent: Locals; + + current: Map; + + contains(name: string): boolean; + + get(name: string): any; + + set(name: string, value: any): void; + + clearValues(): void; + } + + + interface AbstractControl_onlySelfArgs { + onlySelf?: boolean; + } + + interface AbstractControl_updateValueAndValidityArgs { + onlySelf?: boolean; + emitEvent?: boolean; + } + + /** + * Omitting from external API doc as this is really an abstract internal concept. + */ + class AbstractControl { + + validator: Function; + + value: any; + + status: string; + + valid: boolean; + + errors: StringMap; + + pristine: boolean; + + dirty: boolean; + + touched: boolean; + + untouched: boolean; + + valueChanges: Observable; + + markAsTouched(): void; + + markAsDirty(args?:AbstractControl_onlySelfArgs): void; + + setParent(parent: any): void; + + updateValidity(args?:AbstractControl_onlySelfArgs): void; + + updateValueAndValidity(args?:AbstractControl_updateValueAndValidityArgs): void; + + find(path: List| string): AbstractControl; + + getError(errorCode: string, path?: List): any; + + hasError(errorCode: string, path?: List): boolean; + } + + class AbstractControlDirective { + + control: AbstractControl; + + value: any; + + valid: boolean; + + errors: StringMap; + + pristine: boolean; + + dirty: boolean; + + touched: boolean; + + untouched: boolean; + } + + + interface Control_updateValueOptions { + onlySelf?: boolean; + emitEvent?: boolean; + } + /** + * Defines a part of a form that cannot be divided into other controls. + * + * `Control` is one of the three fundamental building blocks used to define forms in Angular, along + * with + * {@link ControlGroup} and {@link ControlArray}. + */ + class Control extends AbstractControl { + + updateValue(value: any, options?: Control_updateValueOptions): void; + + registerOnChange(fn: Function): void; + } + + + /** + * Defines a part of a form, of fixed length, that can contain other controls. + * + * A ControlGroup aggregates the values and errors of each {@link Control} in the group. Thus, if + * one of the controls + * in a group is invalid, the entire group is invalid. Similarly, if a control changes its value, + * the entire group + * changes as well. + * + * `ControlGroup` is one of the three fundamental building blocks used to define forms in Angular, + * along with + * {@link Control} and {@link ControlArray}. {@link ControlArray} can also contain other controls, + * but is of variable + * length. + */ + class ControlGroup extends AbstractControl { + + controls: StringMap; + + addControl(name: string, c: AbstractControl): void; + + removeControl(name: string): void; + + include(controlName: string): void; + + exclude(controlName: string): void; + + contains(controlName: string): boolean; + } + + + /** + * Defines a part of a form, of variable length, that can contain other controls. + * + * A `ControlArray` aggregates the values and errors of each {@link Control} in the group. Thus, if + * one of the controls + * in a group is invalid, the entire group is invalid. Similarly, if a control changes its value, + * the entire group + * changes as well. + * + * `ControlArray` is one of the three fundamental building blocks used to define forms in Angular, + * along with {@link Control} and {@link ControlGroup}. {@link ControlGroup} can also contain + * other controls, but is of fixed length. + */ + class ControlArray extends AbstractControl { + + controls: List; + + at(index: number): AbstractControl; + + push(control: AbstractControl): void; + + insert(index: number, control: AbstractControl): void; + + removeAt(index: number): void; + + length: number; + } + + + /** + * Creates and binds a control with a specified name to a DOM element. + * + * This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}. + * + * # Example + * + * In this example, we create the login and password controls. + * We can work with each control separately: check its validity, get its value, listen to its + * changes. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + *
+ * Login + *
Login is invalid
+ * + * Password + * + * + *
+ * `}) + * class LoginComp { + * onLogIn(value) { + * // value === {login: 'some login', password: 'some password'} + * } + * } + * ``` + * + * We can also use ng-model to bind a domain model to the form. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + *
+ * Login + * Password + * + *
+ * `}) + * class LoginComp { + * credentials: {login:string, password:string}; + * + * onLogIn() { + * // this.credentials.login === "some login" + * // this.credentials.password === "some password" + * } + * } + * ``` + */ + class NgControlName extends NgControl { + + update: void; + + model: any; + + ngValidators: QueryList; + + onChange(c: StringMap): void; + + onDestroy(): void; + + viewToModelUpdate(newValue: any): void; + + path: List; + + formDirective: any; + + control: Control; + + validator: Function; + } + + + /** + * Binds an existing control to a DOM element. + * + * # Example + * + * In this example, we bind the control to an input element. When the value of the input element + * changes, the value of + * the control will reflect that change. Likewise, if the value of the control changes, the input + * element reflects that + * change. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: "" + * }) + * class LoginComp { + * loginControl:Control; + * + * constructor() { + * this.loginControl = new Control(''); + * } + * } + * + * ``` + * + * We can also use ng-model to bind a domain model to the form. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: "" + * }) + * class LoginComp { + * loginControl:Control; + * login:string; + * + * constructor() { + * this.loginControl = new Control(''); + * } + * } + * ``` + */ + class NgFormControl extends NgControl { + + form: Control; + + update: void; + + model: any; + + ngValidators: QueryList; + + onChange(c: any): void; + + path: List; + + control: Control; + + validator: Function; + + viewToModelUpdate(newValue: any): void; + } + + + /** + * Binds a domain model to the form. + * + * # Example + * ``` + * @Component({selector: "search-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + * + * `}) + * class SearchComp { + * searchQuery: string; + * } + * ``` + */ + class NgModel extends NgControl { + + update: void; + + model: any; + + ngValidators: QueryList; + + onChange(c: any): void; + + control: Control; + + path: List; + + validator: Function; + + viewToModelUpdate(newValue: any): void; + } + + + /** + * An abstract class that all control directive extend. + * + * It binds a {@link Control} object to a DOM element. + */ + class NgControl extends AbstractControlDirective { + + name: string; + + valueAccessor: ControlValueAccessor; + + validator: Function; + + path: List; + + viewToModelUpdate(newValue: any): void; + } + + + /** + * Creates and binds a control group to a DOM element. + * + * This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}. + * + * # Example + * + * In this example, we create the credentials and personal control groups. + * We can work with each group separately: check its validity, get its value, listen to its changes. + * + * ``` + * @Component({selector: "signup-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + *
+ *
+ * Login + * Password + *
+ *
Credentials are invalid
+ * + *
+ * Name + *
+ * + *
+ * `}) + * class SignupComp { + * onSignUp(value) { + * // value === {personal: {name: 'some name'}, + * // credentials: {login: 'some login', password: 'some password'}} + * } + * } + * + * ``` + */ + class NgControlGroup extends ControlContainer { + + onInit(): void; + + onDestroy(): void; + + control: ControlGroup; + + path: List; + + formDirective: Form; + } + + + /** + * Binds an existing control group to a DOM element. + * + * # Example + * + * In this example, we bind the control group to the form element, and we bind the login and + * password controls to the + * login and password elements. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: "
" + + * "Login " + + * "Password " + + * "" + + * "
" + * }) + * class LoginComp { + * loginForm:ControlGroup; + * + * constructor() { + * this.loginForm = new ControlGroup({ + * login: new Control(""), + * password: new Control("") + * }); + * } + * + * onLogin() { + * // this.loginForm.value + * } + * } + * + * ``` + * + * We can also use ng-model to bind a domain model to the form. + * + * ``` + * @Component({selector: "login-comp"}) + * @View({ + * directives: [formDirectives], + * template: "
" + + * "Login " + + * "Password " + + * "" + + * "
" + * }) + * class LoginComp { + * credentials:{login:string, password:string} + * loginForm:ControlGroup; + * + * constructor() { + * this.loginForm = new ControlGroup({ + * login: new Control(""), + * password: new Control("") + * }); + * } + * + * onLogin() { + * // this.credentials.login === 'some login' + * // this.credentials.password === 'some password' + * } + * } + * ``` + */ + class NgFormModel extends ControlContainer implements Form { + + form: ControlGroup; + + directives: List; + + ngSubmit: void; + + onChange(_: any): void; + + formDirective: Form; + + control: ControlGroup; + + path: List; + + addControl(dir: NgControl): void; + + getControl(dir: NgControl): Control; + + removeControl(dir: NgControl): void; + + addControlGroup(dir: NgControlGroup): void; + + removeControlGroup(dir: NgControlGroup): void; + + getControlGroup(dir: NgControlGroup): ControlGroup; + + updateModel(dir: NgControl, value: any): void; + + onSubmit(): boolean; + } + + + /** + * Creates and binds a form object to a DOM element. + * + * # Example + * + * ``` + * @Component({selector: "signup-comp"}) + * @View({ + * directives: [formDirectives], + * template: ` + *
+ *
+ * Login + * Password + *
+ *
Credentials are invalid
+ * + *
+ * Name + *
+ * + *
+ * `}) + * class SignupComp { + * onSignUp(value) { + * // value === {personal: {name: 'some name'}, + * // credentials: {login: 'some login', password: 'some password'}} + * } + * } + * + * ``` + */ + class NgForm extends ControlContainer implements Form { + + form: ControlGroup; + + ngSubmit: void; + + formDirective: Form; + + control: ControlGroup; + + path: List; + + controls: StringMap; + + addControl(dir: NgControl): void; + + getControl(dir: NgControl): Control; + + removeControl(dir: NgControl): void; + + addControlGroup(dir: NgControlGroup): void; + + removeControlGroup(dir: NgControlGroup): void; + + getControlGroup(dir: NgControlGroup): ControlGroup; + + updateModel(dir: NgControl, value: any): void; + + onSubmit(): boolean; + } + + + /** + * A bridge between a control and a native element. + * + * Please see {@link DefaultValueAccessor} for more information. + */ + interface ControlValueAccessor { + + writeValue(obj: any): void; + + registerOnChange(fn: any): void; + + registerOnTouched(fn: any): void; + } + + + /** + * The default accessor for writing a value and listening to changes that is used by the + * {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives. + * + * # Example + * ``` + * + * ``` + */ + class DefaultValueAccessor implements ControlValueAccessor { + + value: string; + + onChange: void; + + onTouched: void; + + cd: NgControl; + + renderer: Renderer; + + elementRef: ElementRef; + + writeValue(value: any): void; + + ngClassUntouched: boolean; + + ngClassTouched: boolean; + + ngClassPristine: boolean; + + ngClassDirty: boolean; + + ngClassValid: boolean; + + ngClassInvalid: boolean; + + registerOnChange(fn: any): void; + + registerOnTouched(fn: any): void; + } + + + /** + * The accessor for writing a value and listening to changes on a checkbox input element. + * + * # Example + * ``` + * + * ``` + */ + class CheckboxControlValueAccessor implements ControlValueAccessor { + + checked: boolean; + + onChange: void; + + onTouched: void; + + cd: NgControl; + + renderer: Renderer; + + elementRef: ElementRef; + + writeValue(value: any): void; + + ngClassUntouched: boolean; + + ngClassTouched: boolean; + + ngClassPristine: boolean; + + ngClassDirty: boolean; + + ngClassValid: boolean; + + ngClassInvalid: boolean; + + registerOnChange(fn: any): void; + + registerOnTouched(fn: any): void; + } + + + /** + * The accessor for writing a value and listening to changes on a select element. + */ + class SelectControlValueAccessor implements ControlValueAccessor { + + value: void; + + onChange: void; + + onTouched: void; + + cd: NgControl; + + renderer: Renderer; + + elementRef: ElementRef; + + writeValue(value: any): void; + + ngClassUntouched: boolean; + + ngClassTouched: boolean; + + ngClassPristine: boolean; + + ngClassDirty: boolean; + + ngClassValid: boolean; + + ngClassInvalid: boolean; + + registerOnChange(fn: any): void; + + registerOnTouched(fn: any): void; + } + + + /** + * A list of all the form directives used as part of a `@View` annotation. + * + * This is a shorthand for importing them each individually. + */ + var formDirectives : List ; + + + /** + * Provides a set of validators used by form controls. + * + * # Example + * + * ``` + * var loginControl = new Control("", Validators.required) + * ``` + */ + class Validators { + } + + class NgValidator { + + validator: Function; + } + + class NgRequiredValidator extends NgValidator { + + validator: Function; + } + + + /** + * Creates a form object from a user-specified configuration. + * + * # Example + * + * ``` + * import {Component, View, bootstrap} from 'angular2/angular2'; + * import {FormBuilder, Validators, formDirectives, ControlGroup} from 'angular2/forms'; + * + * @Component({ + * selector: 'login-comp', + * viewInjector: [ + * FormBuilder + * ] + * }) + * @View({ + * template: ` + *
+ * Login + * + *
+ * Password + * Confirm password + *
+ *
+ * `, + * directives: [ + * formDirectives + * ] + * }) + * class LoginComp { + * loginForm: ControlGroup; + * + * constructor(builder: FormBuilder) { + * this.loginForm = builder.group({ + * login: ["", Validators.required], + * + * passwordRetry: builder.group({ + * password: ["", Validators.required], + * passwordConfirmation: ["", Validators.required] + * }) + * }); + * } + * } + * + * bootstrap(LoginComp) + * ``` + * + * This example creates a {@link ControlGroup} that consists of a `login` {@link Control}, and a + * nested + * {@link ControlGroup} that defines a `password` and a `passwordConfirmation` {@link Control}: + * + * ``` + * var loginForm = builder.group({ + * login: ["", Validators.required], + * + * passwordRetry: builder.group({ + * password: ["", Validators.required], + * passwordConfirmation: ["", Validators.required] + * }) + * }); + * + * ``` + */ + class FormBuilder { + + group(controlsConfig: StringMap, extra?: StringMap): ControlGroup; + + control(value: Object, validator?: Function): Control; + + array(controlsConfig: List, validator?: Function): ControlArray; + } + + var formInjectables : List ; + + + /** + * A dispatcher for all events happening in a view. + */ + interface EventDispatcher { + + + /** + * Called when an event was triggered for a on-* attribute on an element. + * @param {Map} locals Locals to be used to evaluate the + * event expressions + */ + dispatchEvent(elementIndex: number, eventName: string, locals: Map): void; + } + + class Renderer { + + + /** + * Creates a root host view that includes the given element. + * @param {RenderProtoViewRef} hostProtoViewRef a RenderProtoViewRef of type + * ProtoViewDto.HOST_VIEW_TYPE + * @param {any} hostElementSelector css selector for the host element (will be queried against the + * main document) + * @return {RenderViewRef} the created view + */ + createRootHostView(hostProtoViewRef: RenderProtoViewRef, hostElementSelector: string): RenderViewRef; + + + /** + * Creates a regular view out of the given ProtoView + */ + createView(protoViewRef: RenderProtoViewRef): RenderViewRef; + + + /** + * Destroys the given view after it has been dehydrated and detached + */ + destroyView(viewRef: RenderViewRef): void; + + + /** + * Attaches a componentView into the given hostView at the given element + */ + attachComponentView(location: RenderElementRef, componentViewRef: RenderViewRef): void; + + + /** + * Detaches a componentView into the given hostView at the given element + */ + detachComponentView(location: RenderElementRef, componentViewRef: RenderViewRef): void; + + + /** + * Attaches a view into a ViewContainer (in the given parentView at the given element) at the + * given index. + */ + attachViewInContainer(location: RenderElementRef, atIndex: number, viewRef: RenderViewRef): void; + + + /** + * Detaches a view into a ViewContainer (in the given parentView at the given element) at the + * given index. + */ + detachViewInContainer(location: RenderElementRef, atIndex: number, viewRef: RenderViewRef): void; + + + /** + * Hydrates a view after it has been attached. Hydration/dehydration is used for reusing views + * inside of the view pool. + */ + hydrateView(viewRef: RenderViewRef): void; + + + /** + * Dehydrates a view after it has been attached. Hydration/dehydration is used for reusing views + * inside of the view pool. + */ + dehydrateView(viewRef: RenderViewRef): void; + + + /** + * Returns the native element at the given location. + * Attention: In a WebWorker scenario, this should always return null! + */ + getNativeElementSync(location: RenderElementRef): any; + + + /** + * Sets a property on an element. + */ + setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void; + + + /** + * Sets an attribute on an element. + */ + setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string): void; + + + /** + * Sets a class on an element. + */ + setElementClass(location: RenderElementRef, className: string, isAdd: boolean): void; + + + /** + * Sets a style on an element. + */ + setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void; + + + /** + * Calls a method on an element. + */ + invokeElementMethod(location: RenderElementRef, methodName: string, args: List): void; + + + /** + * Sets the value of a text node. + */ + setText(viewRef: RenderViewRef, textNodeIndex: number, text: string): void; + + + /** + * Sets the dispatcher for all events of the given view + */ + setEventDispatcher(viewRef: RenderViewRef, dispatcher: EventDispatcher): void; + } + + + /** + * Abstract reference to the element which can be marshaled across web-worker boundry. + * + * This interface is used by the {@link Renderer} api. + */ + interface RenderElementRef { + + + /** + * Reference to the {@link RenderViewRef} where the `RenderElementRef` is inside of. + */ + renderView: RenderViewRef; + + + /** + * Index of the element inside the {@link ViewRef}. + * + * This is used internally by the Angular framework to locate elements. + */ + boundElementIndex: number; + } + + class RenderViewRef { + } + + class RenderProtoViewRef { + } + + class DomRenderer extends Renderer { + + createRootHostView(hostProtoViewRef: RenderProtoViewRef, hostElementSelector: string): RenderViewRef; + + createView(protoViewRef: RenderProtoViewRef): RenderViewRef; + + destroyView(view: RenderViewRef): void; + + getNativeElementSync(location: RenderElementRef): any; + + attachComponentView(location: RenderElementRef, componentViewRef: RenderViewRef): void; + + setComponentViewRootNodes(componentViewRef: RenderViewRef, rootNodes: List): void; + + getRootNodes(viewRef: RenderViewRef): List; + + detachComponentView(location: RenderElementRef, componentViewRef: RenderViewRef): void; + + attachViewInContainer(location: RenderElementRef, atIndex: number, viewRef: RenderViewRef): void; + + detachViewInContainer(location: RenderElementRef, atIndex: number, viewRef: RenderViewRef): void; + + hydrateView(viewRef: RenderViewRef): void; + + dehydrateView(viewRef: RenderViewRef): void; + + setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void; + + setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string): void; + + setElementClass(location: RenderElementRef, className: string, isAdd: boolean): void; + + setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void; + + invokeElementMethod(location: RenderElementRef, methodName: string, args: List): void; + + setText(viewRef: RenderViewRef, textNodeIndex: number, text: string): void; + + setEventDispatcher(viewRef: RenderViewRef, dispatcher: any): void; + } + + var DOCUMENT_TOKEN:any; + + + /** + * Declare reusable UI building blocks for an application. + * + * Each Angular component requires a single `@Component` and at least one `@View` annotation. The + * `@Component` + * annotation specifies when a component is instantiated, and which properties and hostListeners it + * binds to. + * + * When a component is instantiated, Angular + * - creates a shadow DOM for the component. + * - loads the selected template into the shadow DOM. + * - creates all the injectable objects configured with `hostInjector` and `viewInjector`. + * + * All template expressions and statements are then evaluated against the component instance. + * + * For details on the `@View` annotation, see {@link View}. + * + * ## Example + * + * ``` + * @Component({ + * selector: 'greet' + * }) + * @View({ + * template: 'Hello {{name}}!' + * }) + * class Greet { + * name: string; + * + * constructor() { + * this.name = 'World'; + * } + * } + * ``` + */ + class ComponentAnnotation extends DirectiveAnnotation { + + + /** + * Defines the used change detection strategy. + * + * When a component is instantiated, Angular creates a change detector, which is responsible for + * propagating + * the component's bindings. + * + * The `changeDetection` property defines, whether the change detection will be checked every time + * or only when the component + * tells it to do so. + */ + changeDetection: string; + + + /** + * Defines the set of injectable objects that are visible to its view dom children. + * + * ## Simple Example + * + * Here is an example of a class that can be injected: + * + * ``` + * class Greeter { + * greet(name:string) { + * return 'Hello ' + name + '!'; + * } + * } + * + * @Directive({ + * selector: 'needs-greeter' + * }) + * class NeedsGreeter { + * greeter:Greeter; + * + * constructor(greeter:Greeter) { + * this.greeter = greeter; + * } + * } + * + * @Component({ + * selector: 'greet', + * viewInjector: [ + * Greeter + * ] + * }) + * @View({ + * template: ``, + * directives: [NeedsGreeter] + * }) + * class HelloWorld { + * } + * + * ``` + */ + viewInjector: List; + } + + + /** + * Directives allow you to attach behavior to elements in the DOM. + * + * {@link Directive}s with an embedded view are called {@link Component}s. + * + * A directive consists of a single directive annotation and a controller class. When the + * directive's `selector` matches + * elements in the DOM, the following steps occur: + * + * 1. For each directive, the `ElementInjector` attempts to resolve the directive's constructor + * arguments. + * 2. Angular instantiates directives for each matched element using `ElementInjector` in a + * depth-first order, + * as declared in the HTML. + * + * ## Understanding How Injection Works + * + * There are three stages of injection resolution. + * - *Pre-existing Injectors*: + * - The terminal {@link Injector} cannot resolve dependencies. It either throws an error or, if + * the dependency was + * specified as `@Optional`, returns `null`. + * - The platform injector resolves browser singleton resources, such as: cookies, title, + * location, and others. + * - *Component Injectors*: Each component instance has its own {@link Injector}, and they follow + * the same parent-child hierarchy + * as the component instances in the DOM. + * - *Element Injectors*: Each component instance has a Shadow DOM. Within the Shadow DOM each + * element has an `ElementInjector` + * which follow the same parent-child hierarchy as the DOM elements themselves. + * + * When a template is instantiated, it also must instantiate the corresponding directives in a + * depth-first order. The + * current `ElementInjector` resolves the constructor dependencies for each directive. + * + * Angular then resolves dependencies as follows, according to the order in which they appear in the + * {@link View}: + * + * 1. Dependencies on the current element + * 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary + * 3. Dependencies on component injectors and their parents until it encounters the root component + * 4. Dependencies on pre-existing injectors + * + * + * The `ElementInjector` can inject other directives, element-specific special objects, or it can + * delegate to the parent + * injector. + * + * To inject other directives, declare the constructor parameter as: + * - `directive:DirectiveType`: a directive on the current element only + * - `@Ancestor() directive:DirectiveType`: any directive that matches the type between the current + * element and the + * Shadow DOM root. Current element is not included in the resolution, therefore even if it could + * resolve it, it will + * be ignored. + * - `@Parent() directive:DirectiveType`: any directive that matches the type on a direct parent + * element only. + * - `@Query(DirectiveType) query:QueryList`: A live collection of direct child + * directives. + * - `@QueryDescendants(DirectiveType) query:QueryList`: A live collection of any + * child directives. + * + * To inject element-specific special objects, declare the constructor parameter as: + * - `element: ElementRef` to obtain a reference to logical element in the view. + * - `viewContainer: ViewContainerRef` to control child template instantiation, for + * {@link Directive} directives only + * - `bindingPropagation: BindingPropagation` to control change detection in a more granular way. + * + * ## Example + * + * The following example demonstrates how dependency injection resolves constructor arguments in + * practice. + * + * + * Assume this HTML template: + * + * ``` + *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * ``` + * + * With the following `dependency` decorator and `SomeService` injectable class. + * + * ``` + * @Injectable() + * class SomeService { + * } + * + * @Directive({ + * selector: '[dependency]', + * properties: [ + * 'id: dependency' + * ] + * }) + * class Dependency { + * id:string; + * } + * ``` + * + * Let's step through the different ways in which `MyDirective` could be declared... + * + * + * ### No injection + * + * Here the constructor is declared with no arguments, therefore nothing is injected into + * `MyDirective`. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor() { + * } + * } + * ``` + * + * This directive would be instantiated with no dependencies. + * + * + * ### Component-level injection + * + * Directives can inject any injectable instance from the closest component injector or any of its + * parents. + * + * Here, the constructor declares a parameter, `someService`, and injects the `SomeService` type + * from the parent + * component's injector. + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(someService: SomeService) { + * } + * } + * ``` + * + * This directive would be instantiated with a dependency on `SomeService`. + * + * + * ### Injecting a directive from the current element + * + * Directives can inject other directives declared on the current element. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(dependency: Dependency) { + * expect(dependency.id).toEqual(3); + * } + * } + * ``` + * This directive would be instantiated with `Dependency` declared at the same element, in this case + * `dependency="3"`. + * + * + * ### Injecting a directive from a direct parent element + * + * Directives can inject other directives declared on a direct parent element. By definition, a + * directive with a + * `@Parent` annotation does not attempt to resolve dependencies for the current element, even if + * this would satisfy + * the dependency. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Parent() dependency: Dependency) { + * expect(dependency.id).toEqual(2); + * } + * } + * ``` + * This directive would be instantiated with `Dependency` declared at the parent element, in this + * case `dependency="2"`. + * + * + * ### Injecting a directive from any ancestor elements + * + * Directives can inject other directives declared on any ancestor element (in the current Shadow + * DOM), i.e. on the + * parent element and its parents. By definition, a directive with an `@Ancestor` annotation does + * not attempt to + * resolve dependencies for the current element, even if this would satisfy the dependency. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Ancestor() dependency: Dependency) { + * expect(dependency.id).toEqual(2); + * } + * } + * ``` + * + * Unlike the `@Parent` which only checks the parent, `@Ancestor` checks the parent, as well as its + * parents recursively. If `dependency="2"` didn't exist on the direct parent, this injection would + * have returned + * `dependency="1"`. + * + * + * ### Injecting a live collection of direct child directives + * + * + * A directive can also query for other child directives. Since parent directives are instantiated + * before child directives, a directive can't simply inject the list of child directives. Instead, + * the directive injects a {@link QueryList}, which updates its contents as children are added, + * removed, or moved by a directive that uses a {@link ViewContainerRef} such as a `ng-for`, an + * `ng-if`, or an `ng-switch`. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Query(Dependency) dependencies:QueryList) { + * } + * } + * ``` + * + * This directive would be instantiated with a {@link QueryList} which contains `Dependency` 4 and + * 6. Here, `Dependency` 5 would not be included, because it is not a direct child. + * + * ### Injecting a live collection of descendant directives + * + * By passing the descendant flag to `@Query` above, we can include the children of the child + * elements. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Query(Dependency, {descendants: true}) dependencies:QueryList) { + * } + * } + * ``` + * + * This directive would be instantiated with a Query which would contain `Dependency` 4, 5 and 6. + * + * ### Optional injection + * + * The normal behavior of directives is to return an error when a specified dependency cannot be + * resolved. If you + * would like to inject `null` on unresolved dependency instead, you can annotate that dependency + * with `@Optional()`. + * This explicitly permits the author of a template to treat some of the surrounding directives as + * optional. + * + * ``` + * @Directive({ selector: '[my-directive]' }) + * class MyDirective { + * constructor(@Optional() dependency:Dependency) { + * } + * } + * ``` + * + * This directive would be instantiated with a `Dependency` directive found on the current element. + * If none can be + * found, the injector supplies `null` instead of throwing an error. + * + * ## Example + * + * Here we use a decorator directive to simply define basic tool-tip behavior. + * + * ``` + * @Directive({ + * selector: '[tooltip]', + * properties: [ + * 'text: tooltip' + * ], + * hostListeners: { + * 'onmouseenter': 'onMouseEnter()', + * 'onmouseleave': 'onMouseLeave()' + * } + * }) + * class Tooltip{ + * text:string; + * overlay:Overlay; // NOT YET IMPLEMENTED + * overlayManager:OverlayManager; // NOT YET IMPLEMENTED + * + * constructor(overlayManager:OverlayManager) { + * this.overlay = overlay; + * } + * + * onMouseEnter() { + * // exact signature to be determined + * this.overlay = this.overlayManager.open(text, ...); + * } + * + * onMouseLeave() { + * this.overlay.close(); + * this.overlay = null; + * } + * } + * ``` + * In our HTML template, we can then add this behavior to a `
` or any other element with the + * `tooltip` selector, + * like so: + * + * ``` + *
+ * ``` + * + * Directives can also control the instantiation, destruction, and positioning of inline template + * elements: + * + * A directive uses a {@link ViewContainerRef} to instantiate, insert, move, and destroy views at + * runtime. + * The {@link ViewContainerRef} is created as a result of `