X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions modules/angular2/src/core/annotations/annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
export {
Component as ComponentAnnotation,
Directive as DirectiveAnnotation,
ComponentArgs,
DirectiveArgs,
onDestroy,
onChange,
onCheck,
Expand Down
42 changes: 36 additions & 6 deletions modules/angular2/src/core/annotations/decorators.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
import {ComponentAnnotation, DirectiveAnnotation} from './annotations';
import {ViewAnnotation} from './view';
import {
ComponentAnnotation,
DirectiveAnnotation,
ComponentArgs,
DirectiveArgs
} from './annotations';
import {ViewAnnotation, ViewArgs} from './view';
import {
SelfAnnotation,
ParentAnnotation,
AncestorAnnotation,
UnboundedAnnotation
} from './visibility';
import {AttributeAnnotation, QueryAnnotation} from './di';
import {makeDecorator, makeParamDecorator} from '../../util/decorators';
import {makeDecorator, makeParamDecorator, TypeDecorator, Class} from '../../util/decorators';
import {Type} from 'angular2/src/facade/lang';

export interface DirectiveTypeDecorator extends TypeDecorator {}

export interface ComponentTypeDecorator extends TypeDecorator {
View(obj: ViewArgs): ViewTypeDecorator;
}

export interface ViewTypeDecorator extends TypeDecorator { View(obj: ViewArgs): ViewTypeDecorator }

export interface Directive {
(obj: any): DirectiveTypeDecorator;
new (obj: DirectiveAnnotation): DirectiveAnnotation;
}

export interface Component {
(obj: any): ComponentTypeDecorator;
new (obj: ComponentAnnotation): ComponentAnnotation;
}

export interface View {
(obj: ViewArgs): ViewTypeDecorator;
new (obj: ViewArgs): ViewAnnotation;
}


/* from annotations */
export var Component = makeDecorator(ComponentAnnotation);
export var Directive = makeDecorator(DirectiveAnnotation);
export var Component = <Component>makeDecorator(ComponentAnnotation, (fn: any) => fn.View = View);
export var Directive = <Directive>makeDecorator(DirectiveAnnotation);

/* from view */
export var View = makeDecorator(ViewAnnotation);
export var View = <View>makeDecorator(ViewAnnotation, (fn: any) => fn.View = View);

/* from visibility */
export var Self = makeParamDecorator(SelfAnnotation);
Expand Down
4 changes: 1 addition & 3 deletions modules/angular2/src/core/annotations/view.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
export {
View as ViewAnnotation,
} from '../annotations_impl/view';
export {View as ViewAnnotation, ViewArgs} from '../annotations_impl/view';
51 changes: 28 additions & 23 deletions modules/angular2/src/core/annotations_impl/annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -787,16 +787,7 @@ export class Directive extends Injectable {
constructor({
selector, properties, events, host, lifecycle, hostInjector, exportAs,
compileChildren = true,
}: {
selector?: string,
properties?: List<string>,
events?: List<string>,
host?: StringMap<string, string>,
lifecycle?: List<LifecycleEvent>,
hostInjector?: List<any>,
exportAs?: string,
compileChildren?: boolean
} = {}) {
}: ComponentArgs = {}) {
super();
this.selector = selector;
this.properties = properties;
Expand All @@ -809,6 +800,17 @@ export class Directive extends Injectable {
}
}

export interface ComponentArgs {
selector?: string;
properties?: List<string>;
events?: List<string>;
host?: StringMap<string, string>;
lifecycle?: List<LifecycleEvent>;
hostInjector?: List<any>;
exportAs?: string;
compileChildren?: boolean;
}

/**
* Declare reusable UI building blocks for an application.
*
Expand Down Expand Up @@ -1007,19 +1009,8 @@ export class Component extends Directive {
viewInjector: List<any>;

constructor({selector, properties, events, host, exportAs, appInjector, lifecycle, hostInjector,
viewInjector, changeDetection = DEFAULT, compileChildren = true}: {
selector?: string,
properties?: List<string>,
events?: List<string>,
host?: StringMap<string, string>,
exportAs?: string,
appInjector?: List<any>,
lifecycle?: List<LifecycleEvent>,
hostInjector?: List<any>,
viewInjector?: List<any>,
changeDetection?: string,
compileChildren?: boolean
} = {}) {
viewInjector, changeDetection = DEFAULT,
compileChildren = true}: DirectiveArgs = {}) {
super({
selector: selector,
properties: properties,
Expand All @@ -1036,6 +1027,20 @@ export class Component extends Directive {
this.viewInjector = viewInjector;
}
}
export interface DirectiveArgs {
selector?: string;
properties?: List<string>;
events?: List<string>;
host?: StringMap<string, string>;
exportAs?: string;
appInjector?: List<any>;
lifecycle?: List<LifecycleEvent>;
hostInjector?: List<any>;
viewInjector?: List<any>;
changeDetection?: string;
compileChildren?: boolean;
}


/**
* Lifecycle events are guaranteed to be called in the following order:
Expand Down
13 changes: 7 additions & 6 deletions modules/angular2/src/core/annotations_impl/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,16 @@ export class View {
*/
renderer: string;

constructor({templateUrl, template, directives, renderer}: {
templateUrl?: string,
template?: string,
directives?: List<Type | any | List<any>>,
renderer?: string
} = {}) {
constructor({templateUrl, template, directives, renderer}: ViewArgs = {}) {
this.templateUrl = templateUrl;
this.template = template;
this.directives = directives;
this.renderer = renderer;
}
}
export interface ViewArgs {
templateUrl?: string;
template?: string;
directives?: List<Type | any | List<any>>;
renderer?: string;
}
167 changes: 134 additions & 33 deletions modules/angular2/src/util/decorators.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,149 @@
import {global} from 'angular2/src/facade/lang';
import {global, Type, isFunction, stringify} from 'angular2/src/facade/lang';

export function makeDecorator(annotationCls) {
return function(...args) {
var Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
export interface ClassDefinition {
extends?: Type;
constructor: (Function | Array<any>);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if ctor are supported in ifs ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is typed and it passes tsc so it seems to be supported.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forget about it, it's a property, not the ctor method

}

export interface TypeDecorator {
(cls: any): any;
annotations: Array<any>;
Class(obj: ClassDefinition): Type;
}

function extractAnnotation(annotation: any) {
if (isFunction(annotation) && annotation.hasOwnProperty('annotation')) {
// it is a decorator, extract annotation
annotation = annotation.annotation;
}
return annotation;
}

function applyParams(fnOrArray: (Function | Array<any>), key: string): Function {
if (fnOrArray === Object || fnOrArray === String || fnOrArray === Function ||
fnOrArray === Number || fnOrArray === Array) {
throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);
}
if (isFunction(fnOrArray)) {
return <Function>fnOrArray;
} else if (fnOrArray instanceof Array) {
var annotations: Array<any> = fnOrArray;
var fn: Function = fnOrArray[fnOrArray.length - 1];
if (!isFunction(fn)) {
throw new Error(
`Last position of Class method array must be Function in key ${key} was '${stringify(fn)}'`);
}
var annotationInstance = Object.create(annotationCls.prototype);
annotationCls.apply(annotationInstance, args);
return function(cls) {
var annoLength = annotations.length - 1;
if (annoLength != fn.length) {
throw new Error(
`Number of annotations (${annoLength}) does not match number of arguments (${fn.length}) in the function: ${stringify(fn)}`);
}
var paramsAnnotations: Array<Array<any>> = [];
for (var i = 0, ii = annotations.length - 1; i < ii; i++) {
var paramAnnotations: Array<any> = [];
paramsAnnotations.push(paramAnnotations);
var annotation = annotations[i];
if (annotation instanceof Array) {
for (var j = 0; j < annotation.length; j++) {
paramAnnotations.push(extractAnnotation(annotation[j]));
}
} else if (isFunction(annotation)) {
paramAnnotations.push(extractAnnotation(annotation));
} else {
paramAnnotations.push(annotation);
}
}
Reflect.defineMetadata('parameters', paramsAnnotations, fn);
return fn;
} else {
throw new Error(
`Only Function or Array is supported in Class definition for key '${key}' is '${stringify(fnOrArray)}'`);
}
}

var annotations = Reflect.getMetadata('annotations', cls);
annotations = annotations || [];
annotations.push(annotationInstance);
Reflect.defineMetadata('annotations', annotations, cls);
return cls;
export function Class(clsDef: ClassDefinition): Type {
var constructor = applyParams(
clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');
var proto = constructor.prototype;
if (clsDef.hasOwnProperty('extends')) {
if (isFunction(clsDef.extends)) {
(<Function>constructor).prototype = proto =
Object.create((<Function>clsDef.extends).prototype);
} else {
throw new Error(
`Class definition 'extends' property must be a constructor function was: ${stringify(clsDef.extends)}`);
}
}
for (var key in clsDef) {
if (key != 'extends' && key != 'prototype' && clsDef.hasOwnProperty(key)) {
proto[key] = applyParams(clsDef[key], key);
}
}
return <Type>constructor;
}

export function makeParamDecorator(annotationCls): any {
return function(...args) {
var Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using parameter decorators';
var Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
}

export function makeDecorator(annotationCls, chainFn: (fn: Function) => void = null): (...args) =>
(cls: any) => any {
function DecoratorFactory(objOrType): (cls: any) => any {
var annotationInstance = new (<any>annotationCls)(objOrType);
if (this instanceof annotationCls) {
return annotationInstance;
} else {
var chainAnnotation = isFunction(this) && this.annotations instanceof Array ?
this.annotations :
[];
chainAnnotation.push(annotationInstance);
var TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls) {
var annotations = Reflect.getMetadata('annotations', cls);
annotations = annotations || [];
annotations.push(annotationInstance);
Reflect.defineMetadata('annotations', annotations, cls);
return cls;
};
TypeDecorator.annotations = chainAnnotation;
TypeDecorator.Class = Class;
if (chainFn) chainFn(TypeDecorator);
return TypeDecorator;
}
}
DecoratorFactory.prototype = Object.create(annotationCls.prototype);
return DecoratorFactory;
}

export function makeParamDecorator(annotationCls): any {
function ParamDecoratorFactory(...args) {
var annotationInstance = Object.create(annotationCls.prototype);
annotationCls.apply(annotationInstance, args);
return function(cls, unusedKey, index) {
var parameters: Array<Array<any>> = Reflect.getMetadata('parameters', cls);
parameters = parameters || [];

// there might be gaps if some in between parameters do not have annotations.
// we pad with nulls.
while (parameters.length <= index) {
parameters.push(null);
}
if (this instanceof annotationCls) {
return annotationInstance;
} else {
function ParamDecorator(cls, unusedKey, index) {
var parameters: Array<Array<any>> = Reflect.getMetadata('parameters', cls);
parameters = parameters || [];

parameters[index] = parameters[index] || [];
var annotationsForParam: Array<any> = parameters[index];
annotationsForParam.push(annotationInstance);
// there might be gaps if some in between parameters do not have annotations.
// we pad with nulls.
while (parameters.length <= index) {
parameters.push(null);
}

parameters[index] = parameters[index] || [];
var annotationsForParam: Array<any> = parameters[index];
annotationsForParam.push(annotationInstance);

Reflect.defineMetadata('parameters', parameters, cls);
return cls;
}

Reflect.defineMetadata('parameters', parameters, cls);
return cls;
(<any>ParamDecorator).annotation = annotationInstance;
return ParamDecorator;
}
}
ParamDecoratorFactory.prototype = Object.create(annotationCls.prototype);
return ParamDecoratorFactory;
}
5 changes: 5 additions & 0 deletions modules/angular2/test/core/annotations/decorators_spec.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
library angular2.test.core.annotations.decorators_dart_spec;

main() {
// not relavant for dart.
}
28 changes: 28 additions & 0 deletions modules/angular2/test/core/annotations/decorators_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xit,
} from 'angular2/test_lib';

import {Component, View, Directive} from 'angular2/angular2';

export function main() {
describe('es5 decorators', () => {
it('should declare directive class', () => {
var MyDirective = Directive({}).Class({constructor: function() { this.works = true; }});
expect(new MyDirective().works).toEqual(true);
});

it('should declare Component class', () => {
var MyComponent =
Component({}).View({}).View({}).Class({constructor: function() { this.works = true; }});
expect(new MyComponent().works).toEqual(true);
});
});
}
Loading
X Tutup