fix(transformer): Add getters for `events`.

closes https://github.com/angular/angular/issues/2725
This commit is contained in:
Jacob MacDonald 2015-06-25 07:29:20 -07:00
parent d037c082fb
commit 5a21dc5340
6 changed files with 118 additions and 18 deletions

View File

@ -9,22 +9,32 @@ import 'package:barback/barback.dart';
import 'visitor.dart'; import 'visitor.dart';
Future<String> createNgSetters(AssetReader reader, AssetId entryPoint) async { Future<String> createNgSettersAndGetters(
AssetReader reader, AssetId entryPoint) async {
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint); NgDeps ngDeps = await NgDeps.parse(reader, entryPoint);
String code = ngDeps.code; String code = ngDeps.code;
var setters = _generateSetters(_createBindMap(ngDeps)); var setters = _generateSetters(_createPropertiesMap(ngDeps));
var getters = _generateGetters(_createEventsMap(ngDeps));
if (setters.length == 0) return code; if (setters.isEmpty && getters.isEmpty) return code;
var out = new StringBuffer();
var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end; var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end;
return '${code.substring(0, codeInjectIdx)}' out.write(code.substring(0, codeInjectIdx));
'..registerSetters({${setters.join(', ')}})' if (setters.isNotEmpty) {
'${code.substring(codeInjectIdx)}'; out.write('..registerSetters({${setters.join(', ')}})');
}
if (getters.isNotEmpty) {
out.write('..registerGetters({${getters.join(', ')}})');
}
out.write(code.substring(codeInjectIdx));
return '$out';
} }
// TODO(kegluneq): De-dupe from template_compiler/generator.dart. // TODO(kegluneq): De-dupe from template_compiler/generator.dart.
/// Consumes the map generated by {@link _createBindMap} to codegen setters. /// Consumes the map generated by {@link _createPropertiesMap} to codegen
/// setters.
List<String> _generateSetters(Map<String, String> bindMap) { List<String> _generateSetters(Map<String, String> bindMap) {
var setters = []; var setters = [];
// TODO(kegluneq): Include types for receivers. See #886. // TODO(kegluneq): Include types for receivers. See #886.
@ -43,8 +53,8 @@ List<String> _generateSetters(Map<String, String> bindMap) {
/// Collapses all `properties` in {@link ngDeps} into a map where the keys are /// Collapses all `properties` in {@link ngDeps} into a map where the keys are
/// the bind properties and the values are either the one and only type /// the bind properties and the values are either the one and only type
/// binding to that property or the empty string. /// binding to that property or the empty string.
Map<String, String> _createBindMap(NgDeps ngDeps) { Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
var visitor = new ExtractSettersVisitor(); var visitor = new ExtractNamedExpressionVisitor('properties');
var bindMap = {}; var bindMap = {};
ngDeps.registeredTypes.forEach((RegisteredType t) { ngDeps.registeredTypes.forEach((RegisteredType t) {
visitor.bindConfig.clear(); visitor.bindConfig.clear();
@ -68,3 +78,37 @@ Map<String, String> _createBindMap(NgDeps ngDeps) {
}); });
return bindMap; return bindMap;
} }
/// Consumes the map generated by {@link _createEventsMap} to codegen getters.
List<String> _generateGetters(Map<String, String> bindMap) {
var getters = [];
// TODO(kegluneq): Include types for receivers. See #886.
bindMap.forEach((getterName, eventName) {
if (!prop.isValid(eventName)) {
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
getters.add(prop.lazyInvalidGetter(eventName));
} else {
getters.add(''' '${prop.sanitize(eventName)}': (o) => o.$getterName''');
}
});
return getters;
}
/// Collapses all `events` in {@link ngDeps} into a map where the keys are
/// the property names for the event emitters and the values are the event name.
Map<String, String> _createEventsMap(NgDeps ngDeps) {
var visitor = new ExtractNamedExpressionVisitor('events');
var bindMap = {};
ngDeps.registeredTypes.forEach((RegisteredType t) {
visitor.bindConfig.clear();
t.annotations.accept(visitor);
visitor.bindConfig.forEach((String config) {
// See comments for `Directive` in annotations_impl/annotations.ts for
// details on how `events` is specified.
var parts = config.split(':').map((p) => p.trim()).toList();
bindMap[parts[0]] = parts.length > 1 ? parts[1] : parts[0];
});
});
return bindMap;
}

View File

@ -31,11 +31,11 @@ class BindGenerator extends Transformer {
try { try {
var id = transform.primaryInput.id; var id = transform.primaryInput.id;
var reader = new AssetReader.fromTransform(transform); var reader = new AssetReader.fromTransform(transform);
var transformedCode = await createNgSetters(reader, id); var transformedCode = await createNgSettersAndGetters(reader, id);
transform.addOutput(new Asset.fromString( transform.addOutput(new Asset.fromString(
id, formatter.format(transformedCode, uri: id.path))); id, formatter.format(transformedCode, uri: id.path)));
} catch (ex, stackTrace) { } catch (ex, stackTrace) {
log.logger.error('Creating ng setters failed.\n' log.logger.error('Creating ng setters/getters failed.\n'
'Exception: $ex\n' 'Exception: $ex\n'
'Stack Trace: $stackTrace'); 'Stack Trace: $stackTrace');
} }

View File

@ -6,18 +6,22 @@ import 'package:angular2/src/transform/common/logging.dart';
/// Visitor responsible for crawling the "annotations" value in a /// Visitor responsible for crawling the "annotations" value in a
/// `registerType` call and pulling out the properties of any "bind" /// `registerType` call and pulling out the properties of any "bind"
/// values found. /// values found.
class ExtractSettersVisitor extends Object with RecursiveAstVisitor<Object> { class ExtractNamedExpressionVisitor extends Object with
RecursiveAstVisitor<Object> {
final ConstantEvaluator _evaluator = new ConstantEvaluator(); final ConstantEvaluator _evaluator = new ConstantEvaluator();
final List<String> bindConfig = []; final List<String> bindConfig = [];
final String nameToExtract;
ExtractNamedExpressionVisitor(this.nameToExtract);
@override @override
Object visitNamedExpression(NamedExpression node) { Object visitNamedExpression(NamedExpression node) {
if ('${node.name.label}' == 'properties') { if ('${node.name.label}' == nameToExtract) {
var evaluated = node.expression.accept(_evaluator); var evaluated = node.expression.accept(_evaluator);
if (evaluated is List) { if (evaluated is List) {
bindConfig.addAll(evaluated); bindConfig.addAll(evaluated);
} else { } else {
logger.error('`properties` currently only supports List values'); logger.error('`$nameToExtract` currently only supports List values');
} }
return null; return null;
} }

View File

@ -9,6 +9,8 @@ import '../common/read_file.dart';
var formatter = new DartFormatter(); var formatter = new DartFormatter();
main() => allTests();
void allTests() { void allTests() {
var reader = new TestAssetReader(); var reader = new TestAssetReader();
@ -18,8 +20,8 @@ void allTests() {
var expected = formatter.format( var expected = formatter.format(
readFile('bind_generator/basic_bind_files/expected/bar.ng_deps.dart')); readFile('bind_generator/basic_bind_files/expected/bar.ng_deps.dart'));
var output = formatter var output = formatter.format(
.format(await createNgSetters(reader, new AssetId('a', inputPath))); await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
expect(output).toEqual(expected); expect(output).toEqual(expected);
}); });
@ -30,8 +32,19 @@ void allTests() {
var expected = formatter.format(readFile( var expected = formatter.format(readFile(
'bind_generator/duplicate_bind_name_files/expected/soup.ng_deps.dart')); 'bind_generator/duplicate_bind_name_files/expected/soup.ng_deps.dart'));
var output = formatter var output = formatter.format(
.format(await createNgSetters(reader, new AssetId('a', inputPath))); await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
expect(output).toEqual(expected);
});
it('should generate a getter for a `events` property in an annotation.',
() async {
var inputPath = 'bind_generator/events_files/bar.ng_deps.dart';
var expected = formatter.format(
readFile('bind_generator/events_files/expected/bar.ng_deps.dart'));
var output = formatter.format(
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
expect(output).toEqual(expected); expect(output).toEqual(expected);
}); });
} }

View File

@ -0,0 +1,19 @@
library bar.ng_deps.dart;
import 'bar.dart';
import 'package:angular2/src/core/annotations_impl/annotations.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(ToolTip, {
'factory': () => new ToolTip(),
'parameters': const [],
'annotations': const [
const Directive(
selector: '[tool-tip]', events: ['onOpen', 'close: onClose'])
]
});
}

View File

@ -0,0 +1,20 @@
library bar.ng_deps.dart;
import 'bar.dart';
import 'package:angular2/src/core/annotations_impl/annotations.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(ToolTip, {
'factory': () => new ToolTip(),
'parameters': const [],
'annotations': const [
const Directive(
selector: '[tool-tip]', events: ['onOpen', 'close: onClose'])
]
})
..registerGetters({'onOpen': (o) => o.onOpen, 'onClose': (o) => o.close});
}