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';
|
||||
|
||||
Future<String> createNgSetters(AssetReader reader, AssetId entryPoint) async {
|
||||
Future<String> createNgSettersAndGetters(
|
||||
AssetReader reader, AssetId entryPoint) async {
|
||||
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint);
|
||||
|
||||
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;
|
||||
return '${code.substring(0, codeInjectIdx)}'
|
||||
'..registerSetters({${setters.join(', ')}})'
|
||||
'${code.substring(codeInjectIdx)}';
|
||||
out.write(code.substring(0, codeInjectIdx));
|
||||
if (setters.isNotEmpty) {
|
||||
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.
|
||||
|
||||
/// 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) {
|
||||
var setters = [];
|
||||
// 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
|
||||
/// the bind properties and the values are either the one and only type
|
||||
/// binding to that property or the empty string.
|
||||
Map<String, String> _createBindMap(NgDeps ngDeps) {
|
||||
var visitor = new ExtractSettersVisitor();
|
||||
Map<String, String> _createPropertiesMap(NgDeps ngDeps) {
|
||||
var visitor = new ExtractNamedExpressionVisitor('properties');
|
||||
var bindMap = {};
|
||||
ngDeps.registeredTypes.forEach((RegisteredType t) {
|
||||
visitor.bindConfig.clear();
|
||||
|
@ -68,3 +78,37 @@ Map<String, String> _createBindMap(NgDeps ngDeps) {
|
|||
});
|
||||
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 {
|
||||
var id = transform.primaryInput.id;
|
||||
var reader = new AssetReader.fromTransform(transform);
|
||||
var transformedCode = await createNgSetters(reader, id);
|
||||
var transformedCode = await createNgSettersAndGetters(reader, id);
|
||||
transform.addOutput(new Asset.fromString(
|
||||
id, formatter.format(transformedCode, uri: id.path)));
|
||||
} catch (ex, stackTrace) {
|
||||
log.logger.error('Creating ng setters failed.\n'
|
||||
log.logger.error('Creating ng setters/getters failed.\n'
|
||||
'Exception: $ex\n'
|
||||
'Stack Trace: $stackTrace');
|
||||
}
|
||||
|
|
|
@ -6,18 +6,22 @@ import 'package:angular2/src/transform/common/logging.dart';
|
|||
/// Visitor responsible for crawling the "annotations" value in a
|
||||
/// `registerType` call and pulling out the properties of any "bind"
|
||||
/// values found.
|
||||
class ExtractSettersVisitor extends Object with RecursiveAstVisitor<Object> {
|
||||
class ExtractNamedExpressionVisitor extends Object with
|
||||
RecursiveAstVisitor<Object> {
|
||||
final ConstantEvaluator _evaluator = new ConstantEvaluator();
|
||||
final List<String> bindConfig = [];
|
||||
final String nameToExtract;
|
||||
|
||||
ExtractNamedExpressionVisitor(this.nameToExtract);
|
||||
|
||||
@override
|
||||
Object visitNamedExpression(NamedExpression node) {
|
||||
if ('${node.name.label}' == 'properties') {
|
||||
if ('${node.name.label}' == nameToExtract) {
|
||||
var evaluated = node.expression.accept(_evaluator);
|
||||
if (evaluated is List) {
|
||||
bindConfig.addAll(evaluated);
|
||||
} else {
|
||||
logger.error('`properties` currently only supports List values');
|
||||
logger.error('`$nameToExtract` currently only supports List values');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import '../common/read_file.dart';
|
|||
|
||||
var formatter = new DartFormatter();
|
||||
|
||||
main() => allTests();
|
||||
|
||||
void allTests() {
|
||||
var reader = new TestAssetReader();
|
||||
|
||||
|
@ -18,8 +20,8 @@ void allTests() {
|
|||
var expected = formatter.format(
|
||||
readFile('bind_generator/basic_bind_files/expected/bar.ng_deps.dart'));
|
||||
|
||||
var output = formatter
|
||||
.format(await createNgSetters(reader, new AssetId('a', inputPath)));
|
||||
var output = formatter.format(
|
||||
await createNgSettersAndGetters(reader, new AssetId('a', inputPath)));
|
||||
expect(output).toEqual(expected);
|
||||
});
|
||||
|
||||
|
@ -30,8 +32,19 @@ void allTests() {
|
|||
var expected = formatter.format(readFile(
|
||||
'bind_generator/duplicate_bind_name_files/expected/soup.ng_deps.dart'));
|
||||
|
||||
var output = formatter
|
||||
.format(await createNgSetters(reader, new AssetId('a', inputPath)));
|
||||
var output = formatter.format(
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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