-
Notifications
You must be signed in to change notification settings - Fork 20
Annotation Improvement #10
base: master
Are you sure you want to change the base?
Changes from all commits
0f78561
bb4c7b1
a821021
a3a5ca4
90135ad
7bb90b9
c19d74f
34433c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,29 @@ 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; | ||
) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, drop constructor from this example as current implementation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. I understand, just remove |
||
// do stuff | ||
} | ||
|
||
public someMethod2(anArg: number): boolean { | ||
// do some stuff | ||
|
||
console.log(this.parent.someMethod(anArg)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is parent here ? I do not see any reference in code… There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Look line 318 @at.inject('SomeService') private parent:SomeService; "private" keyword on constructor create a private attribute on your instance. Here, parent is the private attribute create on construtor and it values by SomeService. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe let's use some convention here - There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it's a bad exemple for parent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. $delegate it's better, according to angular doc. |
||
|
||
// do some stuff | ||
return true; | ||
} | ||
|
||
} | ||
|
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,9 +26,15 @@ module at { | |
(t: any, key: string, index: number): void; | ||
} | ||
|
||
function instantiate(moduleName: string, name: string, mode: string): IClassAnnotationDecorator { | ||
function getFuncName(target: any): string { | ||
/* istanbul ignore next */ | ||
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: string = getFuncName(target); | ||
angular.module(moduleName)[mode](name || fnName, target); | ||
}; | ||
} | ||
|
||
|
@@ -55,50 +61,81 @@ module at { | |
} | ||
|
||
export interface IServiceAnnotation { | ||
(moduleName: string, serviceName: string): IClassAnnotationDecorator; | ||
(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; | ||
(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; | ||
(moduleName: string, directiveName: string | IDirectiveProperties): IClassAnnotationDecorator; | ||
} | ||
|
||
export function directive(moduleName: string, directiveName: string): at.IClassAnnotationDecorator { | ||
export interface IDirectiveProperties extends angular.IDirective { | ||
selector: 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; | ||
/* istanbul ignore else */ | ||
|
||
let config: IDirectiveProperties; | ||
|
||
const ctrlName: string = angular.isString(target.controller) | ||
? target.controller.split(' ').shift() | ||
: null; | ||
|
||
let controllerAs: string; | ||
|
||
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)); | ||
// Retrocompatibilty | ||
|
||
if (typeof directiveSettings === 'string') { | ||
directiveSettings = <IDirectiveProperties> { | ||
selector: <string> directiveSettings | ||
}; | ||
} else { | ||
controllerAs = (<IDirectiveProperties> directiveSettings).controllerAs || getFuncName(target); | ||
} | ||
|
||
config = directiveProperties.reduce((config: angular.IDirective, property: string) => { | ||
return angular.isDefined(target[property]) | ||
? angular.extend(config, {[property]: target[property]}) | ||
: config; /* istanbul ignore next */ | ||
|
||
}, angular.extend({}, directiveSettings, { | ||
controllerAs: controllerAs, | ||
controller: target | ||
})); | ||
|
||
/* istanbul ignore else */ | ||
|
||
angular | ||
.module(moduleName) | ||
.directive(config.selector, () => (config)); | ||
}; | ||
} | ||
|
||
export interface IClassFactoryAnnotation { | ||
(moduleName: string, className: string): IClassAnnotationDecorator; | ||
(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); | ||
|
@@ -107,8 +144,43 @@ 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 interface IDecoratorAnnotation { | ||
(moduleName: string, targetProvider: string): IClassAnnotationDecorator; | ||
} | ||
|
||
export function decorator(moduleName: string, targetProvider: string): at.IClassAnnotationDecorator { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it should be generic (parametrized) function: I think it should finally look like: export function decorator<A, B>(moduleName: string, targetProvider: string): at.IClassAnnotationDecorator {
interface IDecoratorStatic<D> extends Function {
new (...args: any[]): D;
}
return (targetClass: IDecoratorStatic<B>): void => {
angular
.module(moduleName)
.config([
'$provide',
function($provide: angular.auto.IProvideService): void {
delegation.$inject = ['$delegate', '$injector'];
function delegation(
$delegate: A,
$injector: angular.auto.IInjectorService
): A & B {
const instance: B = $injector.instantiate<B>(targetClass, {
$delegate: $delegate
});
return angular.extend($delegate, targetClass.prototype, instance);
}
$provide.decorator(targetProvider, delegation);
}]);
};
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And it would be nice if you followed such direction in code style: interface IDecoratorStatic<D> extends Function {
new (...args: any[]): D;
}
export function decorator<A, B>(moduleName: string, targetProvider: string): at.IClassAnnotationDecorator {
return (targetClass: IDecoratorStatic<B>): void => {
angular
.module(moduleName)
.config(['$provide', ($provide: angular.auto.IProvideService): void => {
$provide.decorator(targetProvider, ['$delegate', '$injector', (
$delegate: A,
$injector: angular.auto.IInjectorService
): A & B => angular.extend(
$delegate,
targetClass.prototype,
$injector.instantiate<B>(targetClass, {$delegate: $delegate})
)]);
}]);
};
} :D |
||
|
||
return (targetClass: any): void => { | ||
|
||
angular | ||
.module(moduleName) | ||
.config([ | ||
'$provide', | ||
function($provide: angular.auto.IProvideService): void { | ||
|
||
delegation.$inject = ['$delegate', '$injector']; | ||
|
||
function delegation( | ||
$delegate: angular.ISCEDelegateProvider, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is not a |
||
$injector: angular.auto.IInjectorService | ||
): any { | ||
|
||
let instance: any = $injector.instantiate(targetClass, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This variable is used only once, so it can be removed - just place right hand part of expresion as 3rd param of extend below. |
||
$delegate: $delegate | ||
}); | ||
|
||
return angular.extend($delegate, targetClass.prototype, instance); | ||
} | ||
|
||
$provide.decorator(targetProvider, delegation); | ||
}]); | ||
|
||
}; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Functionality here is now OK :) |
||
} | ||
/* tslint:enable:no-any */ | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1.5.2 maybe ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe. 1.5 give me an error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok leave it on 1.4 I'll fix errors and upgrade deps later. |
||
"angular-resource": "1.4.0" | ||
}, | ||
"devDependencies": { | ||
"angular-mocks": "^1.2.0" | ||
"angular-mocks": "1.4.0" | ||
}, | ||
"authors": ["Jakub Strojewski <[email protected]>"], | ||
"ignore": [ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,4 +19,7 @@ module test { | |
|
||
} | ||
|
||
@classFactory('test') | ||
export class TestClassSecond {} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that static
link
member should be actually moved o@directive
decorator in this example.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think isn't a best practice to declare a function into an annotation. I never see that in Java.
But I think, effectivily, the description isn't clear...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally agree. That is why I decided to move the configuration to static members, not to annotation.
Problem is that this linking function must be in this example in annotation, or it will not work at all…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think work fine because have these instructions (line 116 - 125) :
Link method will be stored in config object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are totally right. I focused on a
if (typeof directiveSettings === 'string') {
guard and didn't notice the part in lines 122 - 126.So my observation is invalid.
Now I see - there is another issue. Look down to those lines…