X Tutup
Skip to content

Commit 896add7

Browse files
committed
feat(core): add support for @Property and @event decorators
Example: @directive({selector: 'my-selector'}) class MyDirective { @Property() prop; @Property('el-prop') prop2; @event() event; @event('el-event') event2; } Closes #3992
1 parent 337ce21 commit 896add7

File tree

19 files changed

+511
-89
lines changed

19 files changed

+511
-89
lines changed

modules/angular2/metadata.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ export {
3434
QueryFactory,
3535
ViewQuery,
3636
Pipe,
37-
PipeFactory
37+
PipeFactory,
38+
Property,
39+
PropertyFactory,
40+
PropertyMetadata,
41+
Event,
42+
EventFactory,
43+
EventMetadata
3844
} from './src/core/metadata';
3945

4046
export {

modules/angular2/src/core/compiler/directive_resolver.ts

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import {resolveForwardRef, Injectable} from 'angular2/di';
22
import {Type, isPresent, BaseException, stringify} from 'angular2/src/core/facade/lang';
3-
import {DirectiveMetadata} from 'angular2/metadata';
3+
import {ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
4+
import {
5+
DirectiveMetadata,
6+
ComponentMetadata,
7+
PropertyMetadata,
8+
EventMetadata
9+
} from 'angular2/metadata';
410
import {reflector} from 'angular2/src/core/reflection/reflection';
511

612
/**
@@ -16,15 +22,78 @@ export class DirectiveResolver {
1622
* Return {@link DirectiveMetadata} for a given `Type`.
1723
*/
1824
resolve(type: Type): DirectiveMetadata {
19-
var annotations = reflector.annotations(resolveForwardRef(type));
20-
if (isPresent(annotations)) {
21-
for (var i = 0; i < annotations.length; i++) {
22-
var annotation = annotations[i];
23-
if (annotation instanceof DirectiveMetadata) {
24-
return annotation;
25+
var typeMetadata = reflector.annotations(resolveForwardRef(type));
26+
if (isPresent(typeMetadata)) {
27+
for (var i = 0; i < typeMetadata.length; i++) {
28+
var metadata = typeMetadata[i];
29+
if (metadata instanceof DirectiveMetadata) {
30+
var propertyMetadata = reflector.propMetadata(type);
31+
return this._mergeWithPropertyMetadata(metadata, propertyMetadata);
2532
}
2633
}
2734
}
2835
throw new BaseException(`No Directive annotation found on ${stringify(type)}`);
2936
}
37+
38+
private _mergeWithPropertyMetadata(dm: DirectiveMetadata,
39+
propertyMetadata:
40+
StringMap<string, any[]>): DirectiveMetadata {
41+
var properties = [];
42+
var events = [];
43+
44+
StringMapWrapper.forEach(propertyMetadata, (metadata: any[], propName: string) => {
45+
metadata.forEach(a => {
46+
if (a instanceof PropertyMetadata) {
47+
if (isPresent(a.bindingPropertyName)) {
48+
properties.push(`${propName}: ${a.bindingPropertyName}`);
49+
} else {
50+
properties.push(propName);
51+
}
52+
}
53+
54+
if (a instanceof EventMetadata) {
55+
if (isPresent(a.bindingPropertyName)) {
56+
events.push(`${propName}: ${a.bindingPropertyName}`);
57+
} else {
58+
events.push(propName);
59+
}
60+
}
61+
});
62+
});
63+
64+
return this._merge(dm, properties, events);
65+
}
66+
67+
private _merge(dm: DirectiveMetadata, properties: string[], events: string[]): DirectiveMetadata {
68+
var mergedProperties =
69+
isPresent(dm.properties) ? ListWrapper.concat(dm.properties, properties) : properties;
70+
var mergedEvents = isPresent(dm.events) ? ListWrapper.concat(dm.events, events) : events;
71+
72+
if (dm instanceof ComponentMetadata) {
73+
return new ComponentMetadata({
74+
selector: dm.selector,
75+
properties: mergedProperties,
76+
events: mergedEvents,
77+
host: dm.host,
78+
lifecycle: dm.lifecycle,
79+
bindings: dm.bindings,
80+
exportAs: dm.exportAs,
81+
compileChildren: dm.compileChildren,
82+
changeDetection: dm.changeDetection,
83+
viewBindings: dm.viewBindings
84+
});
85+
86+
} else {
87+
return new DirectiveMetadata({
88+
selector: dm.selector,
89+
properties: mergedProperties,
90+
events: mergedEvents,
91+
host: dm.host,
92+
lifecycle: dm.lifecycle,
93+
bindings: dm.bindings,
94+
exportAs: dm.exportAs,
95+
compileChildren: dm.compileChildren
96+
});
97+
}
98+
}
3099
}

modules/angular2/src/core/metadata.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,19 @@ class ViewQuery extends ViewQueryMetadata {
9696
const ViewQuery(dynamic /*Type | string*/ selector, {bool descendants: false})
9797
: super(selector, descendants: descendants);
9898
}
99+
100+
/**
101+
* See: [PropertyMetadata] for docs.
102+
*/
103+
class Property extends PropertyMetadata {
104+
const Property([String bindingPropertyName])
105+
: super(bindingPropertyName);
106+
}
107+
108+
/**
109+
* See: [EventMetadata] for docs.
110+
*/
111+
class Event extends EventMetadata {
112+
const Event([String bindingPropertyName])
113+
: super(bindingPropertyName);
114+
}

modules/angular2/src/core/metadata.ts

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ export {
1313
ComponentMetadata,
1414
DirectiveMetadata,
1515
PipeMetadata,
16-
LifecycleEvent
16+
LifecycleEvent,
17+
PropertyMetadata,
18+
EventMetadata
1719
} from './metadata/directives';
1820

1921
export {ViewMetadata, ViewEncapsulation} from './metadata/view';
@@ -29,13 +31,21 @@ import {
2931
ComponentMetadata,
3032
DirectiveMetadata,
3133
PipeMetadata,
32-
LifecycleEvent
34+
LifecycleEvent,
35+
PropertyMetadata,
36+
EventMetadata
3337
} from './metadata/directives';
3438

3539
import {ViewMetadata, ViewEncapsulation} from './metadata/view';
3640
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection/change_detection';
3741

38-
import {makeDecorator, makeParamDecorator, TypeDecorator, Class} from './util/decorators';
42+
import {
43+
makeDecorator,
44+
makeParamDecorator,
45+
makePropDecorator,
46+
TypeDecorator,
47+
Class
48+
} from './util/decorators';
3949
import {Type} from 'angular2/src/core/facade/lang';
4050

4151
/**
@@ -397,6 +407,46 @@ export interface PipeFactory {
397407
}): any;
398408
}
399409

410+
/**
411+
* {@link PropertyMetadata} factory for creating decorators.
412+
*
413+
* ## Example as TypeScript Decorator
414+
*
415+
* ```
416+
* @Directive({
417+
* selector: 'sample-dir'
418+
* })
419+
* class SampleDir {
420+
* @Property() property; // Same as @Property('property') property;
421+
* @Property("el-property") dirProperty;
422+
* }
423+
* ```
424+
*/
425+
export interface PropertyFactory {
426+
(bindingPropertyName?: string): any;
427+
new (bindingPropertyName?: string): any;
428+
}
429+
430+
/**
431+
* {@link EventMetadata} factory for creating decorators.
432+
*
433+
* ## Example as TypeScript Decorator
434+
*
435+
* ```
436+
* @Directive({
437+
* selector: 'sample-dir'
438+
* })
439+
* class SampleDir {
440+
* @Event() event = new EventEmitter(); // Same as @Event('event') event = new EventEmitter();
441+
* @Event("el-event") dirEvent = new EventEmitter();
442+
* }
443+
* ```
444+
*/
445+
export interface EventFactory {
446+
(bindingPropertyName?: string): any;
447+
new (bindingPropertyName?: string): any;
448+
}
449+
400450
/**
401451
* {@link ComponentMetadata} factory function.
402452
*/
@@ -433,3 +483,13 @@ export var ViewQuery: QueryFactory = makeParamDecorator(ViewQueryMetadata);
433483
* {@link PipeMetadata} factory function.
434484
*/
435485
export var Pipe: PipeFactory = <PipeFactory>makeDecorator(PipeMetadata);
486+
487+
/**
488+
* {@link PropertyMetadata} factory function.
489+
*/
490+
export var Property: PropertyFactory = makePropDecorator(PropertyMetadata);
491+
492+
/**
493+
* {@link EventMetadata} factory function.
494+
*/
495+
export var Event: EventFactory = makePropDecorator(EventMetadata);

modules/angular2/src/core/metadata/directives.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,3 +1070,43 @@ export class PipeMetadata extends InjectableMetadata {
10701070
this.name = name;
10711071
}
10721072
}
1073+
1074+
/**
1075+
* Declare a bound field.
1076+
*
1077+
* ## Example
1078+
*
1079+
* ```
1080+
* @Directive({
1081+
* selector: 'sample-dir'
1082+
* })
1083+
* class SampleDir {
1084+
* @Property() property; // Same as @Property('property') property;
1085+
* @Property("el-property") dirProperty;
1086+
* }
1087+
* ```
1088+
*/
1089+
@CONST()
1090+
export class PropertyMetadata {
1091+
constructor(public bindingPropertyName?: string) {}
1092+
}
1093+
1094+
/**
1095+
* Declare a bound event.
1096+
*
1097+
* ## Example
1098+
*
1099+
* ```
1100+
* @Directive({
1101+
* selector: 'sample-dir'
1102+
* })
1103+
* class SampleDir {
1104+
* @Event() event = new EventEmitter(); // Same as @Event('event') event = new EventEmitter();
1105+
* @Event("el-event") dirEvent = new EventEmitter();
1106+
* }
1107+
* ```
1108+
*/
1109+
@CONST()
1110+
export class EventMetadata {
1111+
constructor(public bindingPropertyName?: string) {}
1112+
}

modules/angular2/src/core/reflection/debug_reflection_capabilities.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ class ReflectionCapabilities extends standard.ReflectionCapabilities {
4141
return super.annotations(typeOrFunc);
4242
}
4343

44+
Map propMetadata(typeOrFunc) {
45+
_notify('propMetadata', typeOrFunc);
46+
return super.propMetadata(typeOrFunc);
47+
}
48+
4449
GetterFn getter(String name) {
4550
_notify('getter', name);
4651
return super.getter(name);

modules/angular2/src/core/reflection/platform_reflection_capabilities.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ export interface PlatformReflectionCapabilities {
55
isReflectionEnabled(): boolean;
66
factory(type: Type): Function;
77
interfaces(type: Type): any[];
8-
parameters(type: Type): any[][];
9-
annotations(type: Type): any[];
8+
parameters(type: any): any[][];
9+
annotations(type: any): any[];
10+
propMetadata(typeOrFunc: any): StringMap<string, any[]>;
1011
getter(name: string): GetterFn;
1112
setter(name: string): SetterFn;
1213
method(name: string): MethodFn;

modules/angular2/src/core/reflection/reflection.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ class NoReflectionCapabilities implements PlatformReflectionCapabilities {
2727
throw "Cannot find reflection information on ${stringify(type)}";
2828
}
2929

30+
Map propMetadata(Type type) {
31+
throw "Cannot find reflection information on ${stringify(type)}";
32+
}
33+
3034
GetterFn getter(String name) {
3135
throw "Cannot find getter ${name}";
3236
}

modules/angular2/src/core/reflection/reflection_capabilities.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,19 @@ class ReflectionCapabilities implements PlatformReflectionCapabilities {
255255
return meta.map((m) => m.reflectee).toList();
256256
}
257257

258+
Map propMetadata(typeOrFunc) {
259+
final res = {};
260+
reflectClass(typeOrFunc).declarations.forEach((k,v) {
261+
var name = _normalizeName(MirrorSystem.getName(k));
262+
res[name] = v.metadata.map((fm) => fm.reflectee).toList();
263+
});
264+
return res;
265+
}
266+
267+
String _normalizeName(String name) {
268+
return name.endsWith("=") ? name.substring(0, name.length - 1) : name;
269+
}
270+
258271
List interfaces(type) {
259272
ClassMirror classMirror = reflectType(type);
260273
return classMirror.superinterfaces.map((si) => si.reflectedType).toList();

modules/angular2/src/core/reflection/reflection_capabilities.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,37 +109,53 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
109109
return result;
110110
}
111111

112-
parameters(typeOfFunc: Type): any[][] {
112+
parameters(typeOrFunc: Type): any[][] {
113113
// Prefer the direct API.
114-
if (isPresent((<any>typeOfFunc).parameters)) {
115-
return (<any>typeOfFunc).parameters;
114+
if (isPresent((<any>typeOrFunc).parameters)) {
115+
return (<any>typeOrFunc).parameters;
116116
}
117117
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
118-
var paramAnnotations = this._reflect.getMetadata('parameters', typeOfFunc);
119-
var paramTypes = this._reflect.getMetadata('design:paramtypes', typeOfFunc);
118+
var paramAnnotations = this._reflect.getMetadata('parameters', typeOrFunc);
119+
var paramTypes = this._reflect.getMetadata('design:paramtypes', typeOrFunc);
120120
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
121121
return this._zipTypesAndAnnotaions(paramTypes, paramAnnotations);
122122
}
123123
}
124-
return ListWrapper.createFixedSize((<any>typeOfFunc).length);
124+
return ListWrapper.createFixedSize((<any>typeOrFunc).length);
125125
}
126126

127-
annotations(typeOfFunc: Type): any[] {
127+
annotations(typeOrFunc: Type): any[] {
128128
// Prefer the direct API.
129-
if (isPresent((<any>typeOfFunc).annotations)) {
130-
var annotations = (<any>typeOfFunc).annotations;
129+
if (isPresent((<any>typeOrFunc).annotations)) {
130+
var annotations = (<any>typeOrFunc).annotations;
131131
if (isFunction(annotations) && annotations.annotations) {
132132
annotations = annotations.annotations;
133133
}
134134
return annotations;
135135
}
136136
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
137-
var annotations = this._reflect.getMetadata('annotations', typeOfFunc);
137+
var annotations = this._reflect.getMetadata('annotations', typeOrFunc);
138138
if (isPresent(annotations)) return annotations;
139139
}
140140
return [];
141141
}
142142

143+
propMetadata(typeOrFunc: any): StringMap<string, any[]> {
144+
// Prefer the direct API.
145+
if (isPresent((<any>typeOrFunc).propMetadata)) {
146+
var propMetadata = (<any>typeOrFunc).propMetadata;
147+
if (isFunction(propMetadata) && propMetadata.propMetadata) {
148+
propMetadata = propMetadata.propMetadata;
149+
}
150+
return propMetadata;
151+
}
152+
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
153+
var propMetadata = this._reflect.getMetadata('propMetadata', typeOrFunc);
154+
if (isPresent(propMetadata)) return propMetadata;
155+
}
156+
return {};
157+
}
158+
143159
interfaces(type: Type): any[] {
144160
throw new BaseException("JavaScript does not support interfaces");
145161
}

0 commit comments

Comments
 (0)
X Tutup