Skip to content

Commit 5a21dc5

Browse files
committed
fix(transformer): Add getters for events.
closes angular#2725
1 parent d037c08 commit 5a21dc5

File tree

6 files changed

+118
-18
lines changed

6 files changed

+118
-18
lines changed

modules/angular2/src/transform/bind_generator/generator.dart

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,32 @@ import 'package:barback/barback.dart';
99

1010
import 'visitor.dart';
1111

12-
Future<String> createNgSetters(AssetReader reader, AssetId entryPoint) async {
12+
Future<String> createNgSettersAndGetters(
13+
AssetReader reader, AssetId entryPoint) async {
1314
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint);
1415

1516
String code = ngDeps.code;
16-
var setters = _generateSetters(_createBindMap(ngDeps));
17+
var setters = _generateSetters(_createPropertiesMap(ngDeps));
18+
var getters = _generateGetters(_createEventsMap(ngDeps));
1719

18-
if (setters.length == 0) return code;
20+
if (setters.isEmpty && getters.isEmpty) return code;
21+
var out = new StringBuffer();
1922
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
20-
return '${code.substring(0, codeInjectIdx)}'
21-
'..registerSetters({${setters.join(', ')}})'
22-
'${code.substring(codeInjectIdx)}';
23+
out.write(code.substring(0, codeInjectIdx));
24+
if (setters.isNotEmpty) {
25+
out.write('..registerSetters({${setters.join(', ')}})');
26+
}
27+
if (getters.isNotEmpty) {
28+
out.write('..registerGetters({${getters.join(', ')}})');
29+
}
30+
out.write(code.substring(codeInjectIdx));
31+
return '$out';
2332
}
2433

2534
// TODO(kegluneq): De-dupe from template_compiler/generator.dart.
2635

27-
/// Consumes the map generated by {@link _createBindMap} to codegen setters.
36+
/// Consumes the map generated by {@link _createPropertiesMap} to codegen
37+
/// setters.
2838
List<String> _generateSetters(Map<String, String> bindMap) {
2939
var setters = [];
3040
// TODO(kegluneq): Include types for receivers. See #886.
@@ -43,8 +53,8 @@ List<String> _generateSetters(Map<String, String> bindMap) {
4353
/// Collapses all `properties` in {@link ngDeps} into a map where the keys are
4454
/// the bind properties and the values are either the one and only type
4555
/// binding to that property or the empty string.
46-
Map<String, String> _createBindMap(NgDeps ngDeps) {
47-
var visitor = new ExtractSettersVisitor();
56+
Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
57+
var visitor = new ExtractNamedExpressionVisitor('properties');
4858
var bindMap = {};
4959
ngDeps.registeredTypes.forEach((RegisteredType t) {
5060
visitor.bindConfig.clear();
@@ -68,3 +78,37 @@ Map<String, String> _createBindMap(NgDeps ngDeps) {
6878
});
6979
return bindMap;
7080
}
81+
82+
/// Consumes the map generated by {@link _createEventsMap} to codegen getters.
83+
List<String> _generateGetters(Map<String, String> bindMap) {
84+
var getters = [];
85+
// TODO(kegluneq): Include types for receivers. See #886.
86+
bindMap.forEach((getterName, eventName) {
87+
if (!prop.isValid(eventName)) {
88+
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
89+
getters.add(prop.lazyInvalidGetter(eventName));
90+
} else {
91+
getters.add(''' '${prop.sanitize(eventName)}': (o) => o.$getterName''');
92+
}
93+
});
94+
return getters;
95+
}
96+
97+
/// Collapses all `events` in {@link ngDeps} into a map where the keys are
98+
/// the property names for the event emitters and the values are the event name.
99+
Map<String, String> _createEventsMap(NgDeps ngDeps) {
100+
var visitor = new ExtractNamedExpressionVisitor('events');
101+
var bindMap = {};
102+
ngDeps.registeredTypes.forEach((RegisteredType t) {
103+
visitor.bindConfig.clear();
104+
t.annotations.accept(visitor);
105+
visitor.bindConfig.forEach((String config) {
106+
// See comments for `Directive` in annotations_impl/annotations.ts for
107+
// details on how `events` is specified.
108+
var parts = config.split(':').map((p) => p.trim()).toList();
109+
bindMap[parts[0]] = parts.length > 1 ? parts[1] : parts[0];
110+
});
111+
});
112+
return bindMap;
113+
}
114+

modules/angular2/src/transform/bind_generator/transformer.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ class BindGenerator extends Transformer {
3131
try {
3232
var id = transform.primaryInput.id;
3333
var reader = new AssetReader.fromTransform(transform);
34-
var transformedCode = await createNgSetters(reader, id);
34+
var transformedCode = await createNgSettersAndGetters(reader, id);
3535
transform.addOutput(new Asset.fromString(
3636
id, formatter.format(transformedCode, uri: id.path)));
3737
} catch (ex, stackTrace) {
38-
log.logger.error('Creating ng setters failed.\n'
38+
log.logger.error('Creating ng setters/getters failed.\n'
3939
'Exception: $ex\n'
4040
'Stack Trace: $stackTrace');
4141
}

modules/angular2/src/transform/bind_generator/visitor.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@ import 'package:angular2/src/transform/common/logging.dart';
66
/// Visitor responsible for crawling the "annotations" value in a
77
/// `registerType` call and pulling out the properties of any "bind"
88
/// values found.
9-
class ExtractSettersVisitor extends Object with RecursiveAstVisitor<Object> {
9+
class ExtractNamedExpressionVisitor extends Object with
10+
RecursiveAstVisitor<Object> {
1011
final ConstantEvaluator _evaluator = new ConstantEvaluator();
1112
final List<String> bindConfig = [];
13+
final String nameToExtract;
14+
15+
ExtractNamedExpressionVisitor(this.nameToExtract);
1216

1317
@override
1418
Object visitNamedExpression(NamedExpression node) {
15-
if ('${node.name.label}' == 'properties') {
19+
if ('${node.name.label}' == nameToExtract) {
1620
var evaluated = node.expression.accept(_evaluator);
1721
if (evaluated is List) {
1822
bindConfig.addAll(evaluated);
1923
} else {
20-
logger.error('`properties` currently only supports List values');
24+
logger.error('`$nameToExtract` currently only supports List values');
2125
}
2226
return null;
2327
}

modules/angular2/test/transform/bind_generator/all_tests.dart

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import '../common/read_file.dart';
99

1010
var formatter = new DartFormatter();
1111

12+
main() => allTests();
13+
1214
void allTests() {
1315
var reader = new TestAssetReader();
1416

@@ -18,8 +20,8 @@ void allTests() {
1820
var expected = formatter.format(
1921
readFile('bind_generator/basic_bind_files/expected/bar.ng_deps.dart'));
2022

21-
var output = formatter
22-
.format(await createNgSetters(reader, new AssetId('a', inputPath)));
23+
var output = formatter.format(
24+
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
2325
expect(output).toEqual(expected);
2426
});
2527

@@ -30,8 +32,19 @@ void allTests() {
3032
var expected = formatter.format(readFile(
3133
'bind_generator/duplicate_bind_name_files/expected/soup.ng_deps.dart'));
3234

33-
var output = formatter
34-
.format(await createNgSetters(reader, new AssetId('a', inputPath)));
35+
var output = formatter.format(
36+
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
37+
expect(output).toEqual(expected);
38+
});
39+
40+
it('should generate a getter for a `events` property in an annotation.',
41+
() async {
42+
var inputPath = 'bind_generator/events_files/bar.ng_deps.dart';
43+
var expected = formatter.format(
44+
readFile('bind_generator/events_files/expected/bar.ng_deps.dart'));
45+
46+
var output = formatter.format(
47+
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
3548
expect(output).toEqual(expected);
3649
});
3750
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
library bar.ng_deps.dart;
2+
3+
import 'bar.dart';
4+
import 'package:angular2/src/core/annotations_impl/annotations.dart';
5+
6+
var _visited = false;
7+
void initReflector(reflector) {
8+
if (_visited) return;
9+
_visited = true;
10+
reflector
11+
..registerType(ToolTip, {
12+
'factory': () => new ToolTip(),
13+
'parameters': const [],
14+
'annotations': const [
15+
const Directive(
16+
selector: '[tool-tip]', events: ['onOpen', 'close: onClose'])
17+
]
18+
});
19+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
library bar.ng_deps.dart;
2+
3+
import 'bar.dart';
4+
import 'package:angular2/src/core/annotations_impl/annotations.dart';
5+
6+
var _visited = false;
7+
void initReflector(reflector) {
8+
if (_visited) return;
9+
_visited = true;
10+
reflector
11+
..registerType(ToolTip, {
12+
'factory': () => new ToolTip(),
13+
'parameters': const [],
14+
'annotations': const [
15+
const Directive(
16+
selector: '[tool-tip]', events: ['onOpen', 'close: onClose'])
17+
]
18+
})
19+
..registerGetters({'onOpen': (o) => o.onOpen, 'onClose': (o) => o.close});
20+
}

0 commit comments

Comments
 (0)