fix(transformer): Add getters for `events`.
closes https://github.com/angular/angular/issues/2725
This commit is contained in:
parent
d037c082fb
commit
5a21dc5340
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'])
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
|
@ -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});
|
||||||
|
}
|
Loading…
Reference in New Issue