From 0f78561a7c0ab5a6e54904eda8d5a9a570cd82e4 Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Mon, 7 Mar 2016 12:22:31 +0100 Subject: [PATCH 1/8] Add auto finding CtrlName, ServiceName. Change directive declaration. --- at-angular.ts | 87 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/at-angular.ts b/at-angular.ts index ea099d9..7694f9a 100644 --- a/at-angular.ts +++ b/at-angular.ts @@ -26,9 +26,14 @@ module at { (t: any, key: string, index: number): void; } - function instantiate(moduleName: string, name: string, mode: string): IClassAnnotationDecorator { + function getFuncName(target):string{ + return target.name || target.toString().match(/^function\s*([^\s(]+)/)[1] + } + + function instantiate(moduleName: string, mode: string, name?: string): IClassAnnotationDecorator { return (target: any): void => { - angular.module(moduleName)[mode](name, target); + let fnName = getFuncName(target); + angular.module(moduleName)[mode](name || fnName, target); }; } @@ -58,42 +63,90 @@ module at { (moduleName: string, serviceName: string): IClassAnnotationDecorator; } - export function service(moduleName: string, serviceName: string): at.IClassAnnotationDecorator { - return instantiate(moduleName, serviceName, 'service'); + export function service(moduleName: string, serviceName?: string): at.IClassAnnotationDecorator { + return instantiate(moduleName, 'service', serviceName); } export interface IControllerAnnotation { (moduleName: string, ctrlName: string): IClassAnnotationDecorator; } - export function controller(moduleName: string, ctrlName: string): at.IClassAnnotationDecorator { - return instantiate(moduleName, ctrlName, 'controller'); + export function controller(moduleName: string, ctrlName?: string): at.IClassAnnotationDecorator { + return instantiate(moduleName, 'controller', ctrlName); } export interface IDirectiveAnnotation { (moduleName: string, directiveName: string): IClassAnnotationDecorator; } - export function directive(moduleName: string, directiveName: string): at.IClassAnnotationDecorator { + export interface IDirectiveProperties{ + selector:string; + priority?:number; + replace?:boolean; + require?:string | string[]; + restrict?:string; + scope?:any; + template?:string; + templateUrl?:string; + terminal?:boolean; + transclude?:boolean; + controllerAs?:string; + } + + export function directive(moduleName: string, directiveSettings: string|IDirectiveProperties): at.IClassAnnotationDecorator { + return (target: any): void => { + let config: angular.IDirective; - const ctrlName: string = angular.isString(target.controller) ? target.controller.split(' ').shift() : null; + const ctrlName: string = angular.isString(target.controller) ? target.controller.split(' ').shift() : null; + + + if(typeof directiveSettings == 'string'){ + + //retrocompatibilty + + config = directiveProperties + .reduce((config: angular.IDirective, property: string) => { + return angular.isDefined(target[property]) ? + angular.extend(config, {[property]: target[property]}) : config; + }, { + controller: target + }); + + config.selector = directiveSettings; + + }else{ + + //Generate config from Annotation configuration + + config = angular.copy(directiveSettings); + + // store FuncName as ControllerAs + config.controllerAs = getFuncName(target); + config.controller = target; + + angular.forEach(directiveProperties, function(property){ + if(angular.isDefined(target[property])){ + config[property] = target[property]; + } + }); + } + /* istanbul ignore else */ if (ctrlName) { controller(moduleName, ctrlName)(target); } - config = directiveProperties.reduce(( - config: angular.IDirective, - property: string - ) => { - return angular.isDefined(target[property]) ? angular.extend(config, {[property]: target[property]}) : - config; /* istanbul ignore next */ - }, {controller: target, scope: Boolean(target.templateUrl)}); - - angular.module(moduleName).directive(directiveName, () => (config)); + + + + + angular + .module(moduleName) + .directive(directiveSettings.selector, () =>(config)); }; } + export interface IClassFactoryAnnotation { (moduleName: string, className: string): IClassAnnotationDecorator; } From bb4c7b18a9ef9f029de3a54c12e306623e1a5e3f Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Mon, 7 Mar 2016 12:43:05 +0100 Subject: [PATCH 2/8] Add decorator description, improve readme --- README.md | 105 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f6b4d80..2e0917b 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,14 @@ What ? **angular-typescript** provides annotation like decorators: ``` -@at.service(moduleName: string, serviceName: string) +@at.service(moduleName: string, serviceName?: string) @at.inject(dependencyOne: string, ...dependencies?: string[]) -@at.controller(moduleName: string, controllerName: string) +@at.controller(moduleName: string, controllerName?: string) @at.directive(moduleName: string, directiveName: string) -@at.classFactory(moduleName: string, className: string) -@at.resource(moduleName: string, resourceClassName: string) +@at.directive(moduleName: string, directiveProperties: at.IDirectiveProperties) +@at.classFactory(moduleName: string, className?: string) +@at.resource(moduleName: string, resourceClassName?: string) +@at.decorator(moduleName: string, serviceBaseName: string) ``` Why ? @@ -62,7 +64,7 @@ angular.module('ngModuleName').service('someService', SomeService); Using **angular-typescript** it will look like: ```typescript -@service('ngModuleName', 'someService') +@service('ngModuleName') class SomeService { constructor() { @@ -74,6 +76,22 @@ class SomeService { } } + +// or + +@service('ngModuleName','AliasSomeService') +class SomeService { + + constructor() { + // do stuff + } + + public someMethod(anArg: number): boolean { + // do some stuff + } + +} + ``` *** @@ -123,9 +141,27 @@ class SomeService { ### Controller - ```typescript -@controller('ngModuleName', 'SomeController') +@controller('ngModuleName') +class SomeController { + + constructor( + @inject('$scope') $scope: angular.IScope, + @inject('$parse') private $$parse: angular.IParseService + ) { + // do stuff with $scope and $$parse; + } + + public someMethod(anArg: number): boolean { + // do some stuff with this.$$parse(); + } + +} + + +//or + +@controller('ngModuleName', 'AliasSomeCtrl') class SomeController { constructor( @@ -146,7 +182,35 @@ class SomeController { ### Directive -Static class members of directive controller are used as config directive config. +You can configure your directive with annotation : + +```typescript +@directive('ngModuleName', { + selector: 'atSomeDirective', //required + controllerAs: 'someDirectiveCtrl', //optional + templateUrl: '/partials/some-directive.html' +}) +class SomeDirectiveController { + + public static link: angular.IDirectiveLinkFn = (scope, element, attrs, ctrl: SomeDirectiveController) => { + ctrl.init(attrs.atSomeDirective); + }; + + constructor( + @inject('$scope') private $$scope: angular.IScope, + @inject('$parse') private $$parse: angular.IParseService + ) { + // do stuff with $$scope and $$parse; + } + + public init(anArg: string): boolean { + // do some stuff with this.$$parse and this.$$scope + } + +} +``` + +or with static class members of directive controller are used as config directive config : ```typescript @directive('ngModuleName', 'atSomeDirective') @@ -242,3 +306,28 @@ class UserResource extends at.Resource { *** +### Decorator + +Angular provide a decorator feature to extends a service/factory. Use @decorator to use them and adding some others methods : + +```typescript +@decorator('ngModuleName', 'SomeService') +class SomeServiceDecorator { + + constructor( + @at.inject('SomeService') private parent:SomeService; + ) { + // do stuff + } + + public someMethod2(anArg: number): boolean { + // do some stuff + + console.log(this.parent.someMethod(anArg)); + + // do some stuff + return true; + } + +} +``` \ No newline at end of file From a82102194fa257f663d45785dacccc1f5397dc7d Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Mon, 7 Mar 2016 18:07:40 +0100 Subject: [PATCH 3/8] Fix some bugs --- README.md | 1 + at-angular.ts | 87 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 2e0917b..022ee81 100644 --- a/README.md +++ b/README.md @@ -330,4 +330,5 @@ class SomeServiceDecorator { } } + ``` \ No newline at end of file diff --git a/at-angular.ts b/at-angular.ts index 7694f9a..eddb967 100644 --- a/at-angular.ts +++ b/at-angular.ts @@ -26,13 +26,13 @@ module at { (t: any, key: string, index: number): void; } - function getFuncName(target):string{ - return target.name || target.toString().match(/^function\s*([^\s(]+)/)[1] + function getFuncName(target: any): string { + return target.name || target.toString().match(/^function\s*([^\s(]+)/)[1]; } function instantiate(moduleName: string, mode: string, name?: string): IClassAnnotationDecorator { return (target: any): void => { - let fnName = getFuncName(target); + let fnName: string = getFuncName(target); angular.module(moduleName)[mode](name || fnName, target); }; } @@ -79,31 +79,25 @@ module at { (moduleName: string, directiveName: string): IClassAnnotationDecorator; } - export interface IDirectiveProperties{ - selector:string; - priority?:number; - replace?:boolean; - require?:string | string[]; - restrict?:string; - scope?:any; - template?:string; - templateUrl?:string; - terminal?:boolean; - transclude?:boolean; - controllerAs?:string; + export interface IDirectiveProperties extends angular.IDirective { + selector: string; } - export function directive(moduleName: string, directiveSettings: string|IDirectiveProperties): at.IClassAnnotationDecorator { + export function directive( + moduleName: string, + directiveSettings: string|IDirectiveProperties + ): at.IClassAnnotationDecorator { return (target: any): void => { - let config: angular.IDirective; - const ctrlName: string = angular.isString(target.controller) ? target.controller.split(' ').shift() : null; + let config: IDirectiveProperties; + const ctrlName: string = angular.isString(target.controller) + ? target.controller.split(' ').shift() + : null; + if (typeof directiveSettings === 'string') { - if(typeof directiveSettings == 'string'){ - - //retrocompatibilty + // Retrocompatibilty config = directiveProperties .reduce((config: angular.IDirective, property: string) => { @@ -115,18 +109,18 @@ module at { config.selector = directiveSettings; - }else{ + } else { - //Generate config from Annotation configuration + // Generate config from Annotation configuration - config = angular.copy(directiveSettings); + config = angular.copy(directiveSettings); - // store FuncName as ControllerAs + // Store FuncName as ControllerAs config.controllerAs = getFuncName(target); config.controller = target; - angular.forEach(directiveProperties, function(property){ - if(angular.isDefined(target[property])){ + angular.forEach(directiveProperties, function(property: string): any { + if (angular.isDefined(target[property])) { config[property] = target[property]; } }); @@ -137,21 +131,18 @@ module at { controller(moduleName, ctrlName)(target); } - - - angular .module(moduleName) - .directive(directiveSettings.selector, () =>(config)); + .directive(config.selector, () => (config)); }; } - export interface IClassFactoryAnnotation { (moduleName: string, className: string): IClassAnnotationDecorator; } - export function classFactory(moduleName: string, className: string): at.IClassAnnotationDecorator { + export function classFactory(moduleName: string, className?: string): at.IClassAnnotationDecorator { + return (target: any): void => { function factory(...args: any[]): any { return at.attachInjects(target, ...args); @@ -160,9 +151,37 @@ module at { if (target.$inject && target.$inject.length > 0) { factory.$inject = target.$inject.slice(0); } - angular.module(moduleName).factory(className, factory); + angular.module(moduleName).factory(className || getFuncName(target), factory); }; } + + export function decorator(moduleName: string, targetProvider: string): at.IClassAnnotationDecorator { + + return (targetClass: any): void => { + + angular + .module(moduleName) + .config([ + '$provide', + function($provide: angular.auto.IProvideService): void { + + $provide.decorator(targetProvider, [ + '$delegate', + ($delegate: any): void => { + + $delegate[targetProvider] = $delegate; + + angular.extend($delegate, new targetClass()); + + return $delegate; + } + ]); + + }]); + + }; + + } /* tslint:enable:no-any */ } From a3a5ca438ddd2a1c133d6f8c8833a300afdad43d Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Mon, 7 Mar 2016 19:59:30 +0100 Subject: [PATCH 4/8] Fix angular version --- bower.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bower.json b/bower.json index 0d9b5f9..23a2f7d 100644 --- a/bower.json +++ b/bower.json @@ -2,11 +2,11 @@ "name": "angular-typescript", "version": "0.0.8", "dependencies": { - "angular": "^1.2.0", - "angular-resource": "^1.2.0" + "angular": "1.4.0", + "angular-resource": "1.4.0" }, "devDependencies": { - "angular-mocks": "^1.2.0" + "angular-mocks": "1.4.0" }, "authors": ["Jakub Strojewski "], "ignore": [ From 90135add663cf1e6643d339425eb65f994b4e4fe Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Mon, 7 Mar 2016 20:06:59 +0100 Subject: [PATCH 5/8] Fix karma version (see https://github.com/karma-runner/karma/issues/1782) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f8d4468..42935c4 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "gulp-tslint": "4.2.2", "gulp-typescript": "2.10.0", "jasmine-core": "2.4.1", - "karma": "0.13.16", + "karma": "0.13.19", "karma-chrome-launcher": "0.2.2", "karma-coffee-preprocessor": "0.3.0", "karma-coverage": "0.5.3", From 7bb90b9082d060f02a695fb1dde97f30644616b2 Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Wed, 9 Mar 2016 21:04:33 +0100 Subject: [PATCH 6/8] Fix directive, Fix decorator instantiation Add Unit test for directive. --- at-angular.ts | 74 ++++++++++++++++++-------------------- test/directive-spec.coffee | 41 +++++++++++++++++++++ test/directive.ts | 40 +++++++++++++++++++++ 3 files changed, 116 insertions(+), 39 deletions(-) diff --git a/at-angular.ts b/at-angular.ts index eddb967..4a622e6 100644 --- a/at-angular.ts +++ b/at-angular.ts @@ -76,7 +76,7 @@ module at { } export interface IDirectiveAnnotation { - (moduleName: string, directiveName: string): IClassAnnotationDecorator; + (moduleName: string, directiveName: string | IDirectiveProperties): IClassAnnotationDecorator; } export interface IDirectiveProperties extends angular.IDirective { @@ -85,51 +85,43 @@ module at { export function directive( moduleName: string, - directiveSettings: string|IDirectiveProperties + directiveSettings: string | IDirectiveProperties ): at.IClassAnnotationDecorator { return (target: any): void => { let config: IDirectiveProperties; + const ctrlName: string = angular.isString(target.controller) ? target.controller.split(' ').shift() : null; - if (typeof directiveSettings === 'string') { - - // Retrocompatibilty + let controllerAs: string; - config = directiveProperties - .reduce((config: angular.IDirective, property: string) => { - return angular.isDefined(target[property]) ? - angular.extend(config, {[property]: target[property]}) : config; - }, { - controller: target - }); - - config.selector = directiveSettings; + if (ctrlName) { + controller(moduleName, ctrlName)(target); + } + // Retrocompatibilty + /* istanbul ignore else */ + if (typeof directiveSettings === 'string') { + directiveSettings = { + selector: directiveSettings + }; } else { - - // Generate config from Annotation configuration - - config = angular.copy(directiveSettings); - - // Store FuncName as ControllerAs - config.controllerAs = getFuncName(target); - config.controller = target; - - angular.forEach(directiveProperties, function(property: string): any { - if (angular.isDefined(target[property])) { - config[property] = target[property]; - } - }); + controllerAs = ( directiveSettings).controllerAs || getFuncName(target); } + config = directiveProperties.reduce((config: angular.IDirective, property: string) => { + return angular.isDefined(target[property]) + ? angular.extend(config, {[property]: target[property]}) + : config; + }, angular.extend({}, directiveSettings, { + controllerAs: controllerAs, + controller: target + })); + /* istanbul ignore else */ - if (ctrlName) { - controller(moduleName, ctrlName)(target); - } angular .module(moduleName) @@ -165,19 +157,23 @@ module at { '$provide', function($provide: angular.auto.IProvideService): void { - $provide.decorator(targetProvider, [ - '$delegate', - ($delegate: any): void => { + let indexDelegate: number = targetClass.$inject.indexOf('$delegate'); + delegation.$inject = targetClass.slide(); - $delegate[targetProvider] = $delegate; + if (indexDelegate === -1) { + indexDelegate = delegation.$inject.push('$delegate') - 1; + } - angular.extend($delegate, new targetClass()); + function delegation(...injects: string[]): any { - return $delegate; + let $delegate: any = injects[indexDelegate]; + + return angular.extend($delegate, targetClass.prototype, new targetClass(...injects)); } - ]); - }]); + $provide.decorator(targetProvider, delegation); + + }]); }; diff --git a/test/directive-spec.coffee b/test/directive-spec.coffee index 5f63387..0c42174 100644 --- a/test/directive-spec.coffee +++ b/test/directive-spec.coffee @@ -45,4 +45,45 @@ describe 'annotations:', -> expect element.text() .toBe $scope.name + $scope.ctrl.name + describe '@directive (with @inject)', -> + $scope = null + element = null + + beforeEach inject ($compile, $rootScope) -> + $scope = $rootScope.$new() + element = $compile('')($scope) + $rootScope.$digest() + + it 'should be defined', -> + + expect at.directive + .toEqual jasmine.any Function + + it 'should instantiate decorated class as new service', -> + + expect element + .toBeDefined() + + expect $scope.ctrl + .toEqual jasmine.any test.TestComponentBCtrl + + it 'should assign proper $inject array to service constructor', -> + + expect test.TestComponentBCtrl.$inject + .toEqual ['$scope', '$parse'] + + it 'should execute directive on element', -> + + expect element.hasClass 'test-component' + .toBe true + + expect $scope.name + .toBe 'FirstTestCtrl' + + expect $scope.ctrl.name + .toBe 'FAKE_CTRL_NAME' + + expect element.text() + .toBe $scope.name + $scope.ctrl.name + diff --git a/test/directive.ts b/test/directive.ts index a75f76d..0997cd4 100644 --- a/test/directive.ts +++ b/test/directive.ts @@ -4,6 +4,7 @@ module test { interface IFirstComponentScope extends angular.IScope { name: string; + ctrl: Object; } @directive('test', 'atTestComponent') @@ -46,4 +47,43 @@ module test { } + @directive('test', { + selector: 'atTestComponentb', + controllerAs: 'ctrl', + restrict: 'E' + }) + export class TestComponentBCtrl { + + public static link: angular.IDirectiveLinkFn = ( + scope: IFirstComponentScope, + element: angular.IAugmentedJQuery, + attrs: angular.IAttributes, + ctrl: TestComponentBCtrl + ) => { + ctrl.setCtrlName('FAKE_CTRL_NAME'); + }; + + public static template: angular.IDirectiveCompileFn = (tElement: angular.IAugmentedJQuery) => { + tElement.addClass('test-component'); + return '{{ name }}{{ ctrl.name }}'; + }; + + // And the rest are simple Ctrl instance members + public name: string; + + constructor( + @inject('$scope') $scope: IFirstComponentScope, + /* tslint:disable:variable-name */ + @inject('$parse') private $$parse: angular.IParseService + /* tslint:enable:variable-name */ + ) { + $scope.name = this.name = 'FirstTestCtrl'; + } + + public setCtrlName(name: string): void { + this.$$parse('name').assign(this, name); + } + + } + } From c19d74f991864bb42e540b0cec85878839b8f00f Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Wed, 9 Mar 2016 22:36:32 +0100 Subject: [PATCH 7/8] Add unit test --- at-angular-resource.ts | 11 ++++++--- at-angular.ts | 11 +++++---- test/class-factory-spec.coffee | 13 ++++++++++ test/class-factory.ts | 3 +++ test/controller-spec.coffee | 35 +++++++++++++++++++++++++++ test/controller.ts | 15 ++++++++++++ test/directive-spec.coffee | 44 +++++++++++++++++++++++++++++++++- test/directive.ts | 38 +++++++++++++++++++++++++++++ test/resource-spec.coffee | 10 ++++++++ test/resource.ts | 3 +++ test/service-spec.coffee | 18 ++++++++++++++ test/service.ts | 3 +++ 12 files changed, 195 insertions(+), 9 deletions(-) diff --git a/at-angular-resource.ts b/at-angular-resource.ts index 5452f8c..1b010c5 100644 --- a/at-angular-resource.ts +++ b/at-angular-resource.ts @@ -14,6 +14,11 @@ module at { angular.extend(instance, new instance.$_Resource(model)); } + function getFuncName(target: any): string { + /* istanbul ignore next */ + return target.name || target.toString().match(/^function\s*([^\s(]+)/)[1]; + } + /* istanbul ignore next */ export class Resource implements angular.resource.IResource { public static get: (params?: Object) => Resource; @@ -43,10 +48,10 @@ module at { } export interface IResourceAnnotation { - (moduleName: string, className: string): IClassAnnotationDecorator; + (moduleName: string, className?: string): IClassAnnotationDecorator; } - export function resource(moduleName: string, className: string): IClassAnnotationDecorator { + export function resource(moduleName: string, className?: string): IClassAnnotationDecorator { return (target: any): void => { function resourceClassFactory($resource: ResourceService, ...args: any[]): any { const newResource: ResourceClass = $resource(target.url, target.params, target.actions, target.options); @@ -59,7 +64,7 @@ module at { })), ...args); } resourceClassFactory.$inject = (['$resource']).concat(target.$inject /* istanbul ignore next */ || []); - angular.module(moduleName).factory(className, resourceClassFactory); + angular.module(moduleName).factory(className || getFuncName(target), resourceClassFactory); }; } /* tslint:enable:no-any */ diff --git a/at-angular.ts b/at-angular.ts index 4a622e6..ba9b557 100644 --- a/at-angular.ts +++ b/at-angular.ts @@ -27,6 +27,7 @@ module at { } function getFuncName(target: any): string { + /* istanbul ignore next */ return target.name || target.toString().match(/^function\s*([^\s(]+)/)[1]; } @@ -60,7 +61,7 @@ module at { } export interface IServiceAnnotation { - (moduleName: string, serviceName: string): IClassAnnotationDecorator; + (moduleName: string, serviceName?: string): IClassAnnotationDecorator; } export function service(moduleName: string, serviceName?: string): at.IClassAnnotationDecorator { @@ -68,7 +69,7 @@ module at { } export interface IControllerAnnotation { - (moduleName: string, ctrlName: string): IClassAnnotationDecorator; + (moduleName: string, ctrlName?: string): IClassAnnotationDecorator; } export function controller(moduleName: string, ctrlName?: string): at.IClassAnnotationDecorator { @@ -103,7 +104,6 @@ module at { } // Retrocompatibilty - /* istanbul ignore else */ if (typeof directiveSettings === 'string') { directiveSettings = { selector: directiveSettings @@ -115,7 +115,8 @@ module at { config = directiveProperties.reduce((config: angular.IDirective, property: string) => { return angular.isDefined(target[property]) ? angular.extend(config, {[property]: target[property]}) - : config; + : config; /* istanbul ignore next */ + }, angular.extend({}, directiveSettings, { controllerAs: controllerAs, controller: target @@ -130,7 +131,7 @@ module at { } export interface IClassFactoryAnnotation { - (moduleName: string, className: string): IClassAnnotationDecorator; + (moduleName: string, className?: string): IClassAnnotationDecorator; } export function classFactory(moduleName: string, className?: string): at.IClassAnnotationDecorator { diff --git a/test/class-factory-spec.coffee b/test/class-factory-spec.coffee index 4e71506..4758de5 100644 --- a/test/class-factory-spec.coffee +++ b/test/class-factory-spec.coffee @@ -49,4 +49,17 @@ describe 'annotations:', -> expect testInstanceOne.accept .toBe 'application/json, text/plain, */*' + describe '@classFactory (without Factory name)', -> + TestClassSecond = null + + beforeEach inject (_TestClassSecond_) -> + TestClassSecond = _TestClassSecond_ + + it 'should create class as a service', -> + + expect TestClassSecond + .toBeDefined() + + expect TestClassSecond + .toBe test.TestClassSecond diff --git a/test/class-factory.ts b/test/class-factory.ts index df31163..d793117 100644 --- a/test/class-factory.ts +++ b/test/class-factory.ts @@ -19,4 +19,7 @@ module test { } + @classFactory('test') + export class TestClassSecond {} + } diff --git a/test/controller-spec.coffee b/test/controller-spec.coffee index d6b1478..7e3c85b 100644 --- a/test/controller-spec.coffee +++ b/test/controller-spec.coffee @@ -40,4 +40,39 @@ describe 'annotations:', -> expect $scope.name .toBe 'FirstTestCtrl' + describe '@controller (without ControllerName)', -> + $scope = null + $parse = null + secondTestCtrl = null + + beforeEach inject ($controller, $rootScope, _$parse_) -> + $scope = $rootScope.$new() + $parse = _$parse_ + secondTestCtrl = $controller 'SecondTestCtrl', $scope: $scope + + it 'should be defined', -> + + expect at.controller + .toEqual jasmine.any Function + + it 'should instantiate decorated class as new service', -> + + expect secondTestCtrl + .toBeDefined() + + expect secondTestCtrl + .toEqual jasmine.any test.SecondTestCtrl + + it 'should assign proper $inject array to service constructor', -> + + expect test.SecondTestCtrl.$inject + .toEqual ['$scope', '$parse'] + + it 'should make proper dependencies are passed to service constructor on instantiation', -> + + expect secondTestCtrl.$$parse + .toBe $parse + + expect $scope.name + .toBe 'SecondTestCtrl' diff --git a/test/controller.ts b/test/controller.ts index b7fa4cb..75c01dc 100644 --- a/test/controller.ts +++ b/test/controller.ts @@ -21,4 +21,19 @@ module test { } + @controller('test') + @inject('$scope', '$parse') + export class SecondTestCtrl { + + constructor( + $scope: IFirstScope, + /* tslint:disable:variable-name */ + private $$parse: angular.IParseService + /* tslint:enable:variable-name */ + ) { + $scope.name = 'SecondTestCtrl'; + } + + } + } diff --git a/test/directive-spec.coffee b/test/directive-spec.coffee index 0c42174..7f0722d 100644 --- a/test/directive-spec.coffee +++ b/test/directive-spec.coffee @@ -45,7 +45,7 @@ describe 'annotations:', -> expect element.text() .toBe $scope.name + $scope.ctrl.name - describe '@directive (with @inject)', -> + describe '@directive (case B : Annotation)', -> $scope = null element = null @@ -87,3 +87,45 @@ describe 'annotations:', -> .toBe $scope.name + $scope.ctrl.name + describe '@directive (case C : Auto finding Ctrl name)', -> + $scope = null + element = null + + beforeEach inject ($compile, $rootScope) -> + $scope = $rootScope.$new() + element = $compile('')($scope) + $rootScope.$digest() + + it 'should be defined', -> + + expect at.directive + .toEqual jasmine.any Function + + it 'should instantiate decorated class as new service', -> + + expect element + .toBeDefined() + + expect $scope.TestComponentCCtrl + .toEqual jasmine.any test.TestComponentCCtrl + + it 'should assign proper $inject array to service constructor', -> + + expect test.TestComponentCCtrl.$inject + .toEqual ['$scope', '$parse'] + + it 'should execute directive on element', -> + + expect element.hasClass 'test-component' + .toBe true + + expect $scope.name + .toBe 'FirstTestCtrl' + + expect $scope.TestComponentCCtrl.name + .toBe 'FAKE_CTRL_NAME' + + expect element.text() + .toBe $scope.name + $scope.TestComponentCCtrl.name + + diff --git a/test/directive.ts b/test/directive.ts index 0997cd4..ae8550e 100644 --- a/test/directive.ts +++ b/test/directive.ts @@ -86,4 +86,42 @@ module test { } + @directive('test', { + selector: 'atTestComponentc', + restrict: 'E' + }) + export class TestComponentCCtrl { + + public static link: angular.IDirectiveLinkFn = ( + scope: IFirstComponentScope, + element: angular.IAugmentedJQuery, + attrs: angular.IAttributes, + ctrl: TestComponentBCtrl + ) => { + ctrl.setCtrlName('FAKE_CTRL_NAME'); + }; + + public static template: angular.IDirectiveCompileFn = (tElement: angular.IAugmentedJQuery) => { + tElement.addClass('test-component'); + return '{{ name }}{{ TestComponentCCtrl.name }}'; + }; + + // And the rest are simple Ctrl instance members + public name: string; + + constructor( + @inject('$scope') $scope: IFirstComponentScope, + /* tslint:disable:variable-name */ + @inject('$parse') private $$parse: angular.IParseService + /* tslint:enable:variable-name */ + ) { + $scope.name = this.name = 'FirstTestCtrl'; + } + + public setCtrlName(name: string): void { + this.$$parse('name').assign(this, name); + } + + } + } diff --git a/test/resource-spec.coffee b/test/resource-spec.coffee index d8d9d8b..0a43460 100644 --- a/test/resource-spec.coffee +++ b/test/resource-spec.coffee @@ -69,3 +69,13 @@ describe 'annotations:', -> .toBe 'application/json, text/plain, */* :: THE NAME :: 1001.1' + describe '@resource (without Resource name)', -> + TestResourceTwo = null + + beforeEach inject (_TestResourceTwo_) -> + TestResourceTwo = _TestResourceTwo_ + + it 'should prepare decorated resource class as new service', -> + + expect TestResourceTwo + .toBeDefined() \ No newline at end of file diff --git a/test/resource.ts b/test/resource.ts index 554a223..47bee53 100644 --- a/test/resource.ts +++ b/test/resource.ts @@ -38,4 +38,7 @@ module test { } + @resource('test') + export class TestResourceTwo {} + } diff --git a/test/service-spec.coffee b/test/service-spec.coffee index b62b458..4b06e05 100644 --- a/test/service-spec.coffee +++ b/test/service-spec.coffee @@ -34,3 +34,21 @@ describe 'annotations:', -> .toBe deps[dep] + describe '@service without Service name', -> + testServiceFour = null + + beforeEach inject (_TestServiceFour_) -> + testServiceFour = _TestServiceFour_ + + it 'should be defined', -> + + expect at.service + .toEqual jasmine.any Function + + it 'should instantiate decorated class as new service', -> + + expect testServiceFour + .toBeDefined() + + expect testServiceFour + .toEqual jasmine.any test.TestServiceFour \ No newline at end of file diff --git a/test/service.ts b/test/service.ts index 3ea74dd..df361c8 100644 --- a/test/service.ts +++ b/test/service.ts @@ -18,4 +18,7 @@ module test { } + @service('test') + export class TestServiceFour {} + } From 34433c01df158aaeb691c74ff9787c4c1d94bbfa Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Wed, 9 Mar 2016 23:42:55 +0100 Subject: [PATCH 8/8] Fix at.decorator, add unit test --- at-angular.ts | 23 +++++++++++--------- test/decorator-spec.coffee | 44 ++++++++++++++++++++++++++++++++++++++ test/decorator.ts | 34 +++++++++++++++++++++++++++++ test/module.ts | 1 + 4 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 test/decorator-spec.coffee create mode 100644 test/decorator.ts diff --git a/at-angular.ts b/at-angular.ts index ba9b557..abceb0a 100644 --- a/at-angular.ts +++ b/at-angular.ts @@ -148,6 +148,10 @@ module at { }; } + export interface IDecoratorAnnotation { + (moduleName: string, targetProvider: string): IClassAnnotationDecorator; + } + export function decorator(moduleName: string, targetProvider: string): at.IClassAnnotationDecorator { return (targetClass: any): void => { @@ -158,22 +162,21 @@ module at { '$provide', function($provide: angular.auto.IProvideService): void { - let indexDelegate: number = targetClass.$inject.indexOf('$delegate'); - delegation.$inject = targetClass.slide(); + delegation.$inject = ['$delegate', '$injector']; - if (indexDelegate === -1) { - indexDelegate = delegation.$inject.push('$delegate') - 1; - } + function delegation( + $delegate: angular.ISCEDelegateProvider, + $injector: angular.auto.IInjectorService + ): any { - function delegation(...injects: string[]): any { + let instance: any = $injector.instantiate(targetClass, { + $delegate: $delegate + }); - let $delegate: any = injects[indexDelegate]; - - return angular.extend($delegate, targetClass.prototype, new targetClass(...injects)); + return angular.extend($delegate, targetClass.prototype, instance); } $provide.decorator(targetProvider, delegation); - }]); }; diff --git a/test/decorator-spec.coffee b/test/decorator-spec.coffee new file mode 100644 index 0000000..fe13e07 --- /dev/null +++ b/test/decorator-spec.coffee @@ -0,0 +1,44 @@ +'use strict'; + +describe 'annotations:', -> + + beforeEach module 'test' + + describe '@decorator', -> + TestServiceFive = null + + beforeEach inject (_TestServiceFive_) -> + TestServiceFive = _TestServiceFive_ + + it 'should be defined', -> + + expect at.service + .toEqual jasmine.any Function + + it 'should instantiate decorated class as new service', -> + + expect TestServiceFive + .toBeDefined() + + expect TestServiceFive + .toEqual jasmine.any test.TestServiceFive + + it 'should have a name', -> + + expect TestServiceFive.name + .toEqual 'TestServiceFive' + + it 'should have a method changeName() provided by the decorator', -> + expect TestServiceFive.changeName + .toBeDefined() + + TestServiceFive.changeName(); + expect TestServiceFive.name + .toEqual 'TestServiceFiveDecorated' + + it 'should inject $delegate service', -> + expect TestServiceFive.$delegate + .toBeDefined() + + expect TestServiceFive.$delegate + .toEqual jasmine.any test.TestServiceFive diff --git a/test/decorator.ts b/test/decorator.ts new file mode 100644 index 0000000..b49ec2b --- /dev/null +++ b/test/decorator.ts @@ -0,0 +1,34 @@ +/* istanbul ignore if else */ + +module test { + + 'use strict'; + + @service('test') + export class TestServiceFive { + private name: string; + + constructor() { + this.name = 'TestServiceFive'; + } + + getName(): string { + return this.name; + } + } + + @decorator('test', 'TestServiceFive') + export class TestDecorator { + private name: string; + constructor( + /* tslint:disable:variable-name */ + @inject('$delegate') private $delegate: TestServiceFive + /* tslint:enable:variable-name */ + ) {} + + changeName(): void { + this.name = 'TestServiceFiveDecorated'; + } + } + +} diff --git a/test/module.ts b/test/module.ts index 451c617..863ca3b 100644 --- a/test/module.ts +++ b/test/module.ts @@ -8,6 +8,7 @@ module test { export const directive: at.IDirectiveAnnotation = at.directive; export const classFactory: at.IClassFactoryAnnotation = at.classFactory; export const resource: at.IResourceAnnotation = at.resource; + export const decorator: at.IDecoratorAnnotation = at.decorator; angular.module('test', ['ngResource']);