diff --git a/modules_dart/transform/lib/src/transform/bind_generator/generator.dart b/modules_dart/transform/lib/src/transform/bind_generator/generator.dart index f90670571d..efa401f540 100644 --- a/modules_dart/transform/lib/src/transform/bind_generator/generator.dart +++ b/modules_dart/transform/lib/src/transform/bind_generator/generator.dart @@ -2,6 +2,7 @@ library angular2.transform.bind_generator.generator; import 'dart:async'; +import 'package:analyzer/analyzer.dart'; import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/ng_deps.dart'; import 'package:angular2/src/transform/common/property_utils.dart' as prop; @@ -9,6 +10,62 @@ import 'package:barback/barback.dart'; import 'visitor.dart'; +class _ExtractQueryFieldsFromAnnotation extends Object + with RecursiveAstVisitor { + final ConstantEvaluator _evaluator = new ConstantEvaluator(); + final List queryFields = []; + + @override + Object visitNamedExpression(NamedExpression node) { + if ('${node.name.label}' == "queries") { + if (node.expression is! MapLiteral) { + throw new FormatException( + 'Expected a map value for "queries", but got ${node.expression}', + node.toSource()); + } + MapLiteral queries = node.expression; + queryFields.addAll(queries.entries.map((e) => e.key.accept(_evaluator))); + } + return super.visitNamedExpression(node); + } + + Map asMap() { + return new Map.fromIterable(queryFields, value: (_) => 'Object'); + } +} + + +class _ExtractQueryFieldsFromPropMetadata extends Object + with RecursiveAstVisitor { + final ConstantEvaluator _evaluator = new ConstantEvaluator(); + final List queryFields = []; + + @override + Object visitMapLiteralEntry(MapLiteralEntry node) { + if (_hasQueryAnnotation(node.value)) { + queryFields.add(node.key.accept(_evaluator)); + } + return super.visitMapLiteralEntry(node); + } + + bool _hasQueryAnnotation(list) { + var res = false; + list.elements.forEach((item) { + var n = item.constructorName.toString(); + if(n == "ContentChild" || n == "ViewChild" || n == "ContentChildren" || n == "ViewChildren") { + res = true; + } + }); + + return res; + } + + asMap() { + return new Map.fromIterable(queryFields, value: (_) => 'Object'); + } +} + + Future createNgSettersAndGetters( AssetReader reader, AssetId entryPoint) async { NgDeps ngDeps = await NgDeps.parse(reader, entryPoint); @@ -17,6 +74,18 @@ Future createNgSettersAndGetters( var setters = _generateSetters(_createPropertiesMap(ngDeps)); var getters = _generateGetters(_createEventPropertiesList(ngDeps)); + ngDeps.registeredTypes.forEach((t) { + final fromAnnotation = new _ExtractQueryFieldsFromAnnotation(); + t.annotations.accept(fromAnnotation); + + final fromPropMetadata = new _ExtractQueryFieldsFromPropMetadata(); + if (t.propMetadata != null) { + t.propMetadata.accept(fromPropMetadata); + } + setters.addAll(_generateSetters(fromAnnotation.asMap())); + setters.addAll(_generateSetters(fromPropMetadata.asMap())); + }); + if (setters.isEmpty && getters.isEmpty) return code; var out = new StringBuffer(); var codeInjectIdx = ngDeps.registeredTypes.last.registerMethod.end; diff --git a/modules_dart/transform/lib/src/transform/common/registered_type.dart b/modules_dart/transform/lib/src/transform/common/registered_type.dart index e8d680a026..cd37d6a0f3 100644 --- a/modules_dart/transform/lib/src/transform/common/registered_type.dart +++ b/modules_dart/transform/lib/src/transform/common/registered_type.dart @@ -26,6 +26,9 @@ class RegisteredType { /// The annotations registered. final Expression annotations; + /// The property metadata registered. + final Expression propMetadata; + RenderDirectiveMetadata _directiveMetadata = null; RegisteredType._( @@ -34,7 +37,8 @@ class RegisteredType { this.reflectionInfoCreate, this.factoryFn, this.parameters, - this.annotations); + this.annotations, + this.propMetadata); /// Creates a {@link RegisteredType} given a {@link MethodInvocation} node representing /// a call to `registerType`. @@ -42,7 +46,7 @@ class RegisteredType { var visitor = new _ParseRegisterTypeVisitor(); registerMethod.accept(visitor); return new RegisteredType._(visitor.typeName, registerMethod, visitor.info, - visitor.factoryFn, visitor.parameters, visitor.annotations); + visitor.factoryFn, visitor.parameters, visitor.annotations, visitor.propMetadata); } RenderDirectiveMetadata get directiveMetadata { @@ -68,6 +72,7 @@ class _ParseRegisterTypeVisitor extends Object Expression factoryFn; Expression parameters; Expression annotations; + Expression propMetadata; @override Object visitMethodInvocation(MethodInvocation node) { @@ -90,6 +95,8 @@ class _ParseRegisterTypeVisitor extends Object parameters = arg; } else if (i == 2) { factoryFn = arg; + } else if (i == 4) { + propMetadata = arg; } } diff --git a/modules_dart/transform/test/transform/bind_generator/all_tests.dart b/modules_dart/transform/test/transform/bind_generator/all_tests.dart index 86a48db90d..0c6f8fdb93 100644 --- a/modules_dart/transform/test/transform/bind_generator/all_tests.dart +++ b/modules_dart/transform/test/transform/bind_generator/all_tests.dart @@ -48,4 +48,26 @@ void allTests() { await createNgSettersAndGetters(reader, new AssetId('a', inputPath))); expect(output).toEqual(expected); }); + + it('should generate setters for queries defined in the class annotation.', + () async { + var inputPath = 'bind_generator/queries_class_annotation_files/bar.ng_deps.dart'; + var expected = formatter.format( + readFile('bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart')); + + var output = formatter.format( + await createNgSettersAndGetters(reader, new AssetId('a', inputPath))); + expect(output).toEqual(expected); + }); + + it('should generate setters for queries defined via prop annotations.', + () async { + var inputPath = 'bind_generator/queries_prop_annotations_files/bar.ng_deps.dart'; + var expected = formatter.format( + readFile('bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart')); + + var output = formatter.format( + await createNgSettersAndGetters(reader, new AssetId('a', inputPath))); + expect(output).toEqual(expected); + }); } diff --git a/modules_dart/transform/test/transform/bind_generator/queries_class_annotation_files/bar.ng_deps.dart b/modules_dart/transform/test/transform/bind_generator/queries_class_annotation_files/bar.ng_deps.dart new file mode 100644 index 0000000000..6f6f45c16e --- /dev/null +++ b/modules_dart/transform/test/transform/bind_generator/queries_class_annotation_files/bar.ng_deps.dart @@ -0,0 +1,18 @@ +library bar.ng_deps.dart; + +import 'bar.dart'; +import 'package:angular2/src/core/metadata.dart'; + +var _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType( + ToolTip, + new ReflectionInfo(const [ + const Directive( + selector: '[tool-tip]', + queries: const {'queryField': const ContentChild('child')}) + ], const [], () => new ToolTip())); +} diff --git a/modules_dart/transform/test/transform/bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart b/modules_dart/transform/test/transform/bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart new file mode 100644 index 0000000000..555fbd6ff1 --- /dev/null +++ b/modules_dart/transform/test/transform/bind_generator/queries_class_annotation_files/expected/bar.ng_deps.dart @@ -0,0 +1,19 @@ +library bar.ng_deps.dart; + +import 'bar.dart'; +import 'package:angular2/src/core/metadata.dart'; + +var _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType( + ToolTip, + new ReflectionInfo(const [ + const Directive( + selector: '[tool-tip]', + queries: const {'queryField': const ContentChild('child')}) + ], const [], () => new ToolTip())) + ..registerSetters({'queryField': (o, v) => o.queryField = v}); +} diff --git a/modules_dart/transform/test/transform/bind_generator/queries_prop_annotations_files/bar.ng_deps.dart b/modules_dart/transform/test/transform/bind_generator/queries_prop_annotations_files/bar.ng_deps.dart new file mode 100644 index 0000000000..96e0132162 --- /dev/null +++ b/modules_dart/transform/test/transform/bind_generator/queries_prop_annotations_files/bar.ng_deps.dart @@ -0,0 +1,17 @@ +library bar.ng_deps.dart; + +import 'bar.dart'; +import 'package:angular2/src/core/metadata.dart'; + +var _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType( + ToolTip, + new ReflectionInfo(const [ + const Directive( + selector: '[tool-tip]') + ], const [], () => new ToolTip(), null, const {'queryField': const [const ContentChild('child')]})); +} diff --git a/modules_dart/transform/test/transform/bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart b/modules_dart/transform/test/transform/bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart new file mode 100644 index 0000000000..3129e9f34d --- /dev/null +++ b/modules_dart/transform/test/transform/bind_generator/queries_prop_annotations_files/expected/bar.ng_deps.dart @@ -0,0 +1,18 @@ +library bar.ng_deps.dart; + +import 'bar.dart'; +import 'package:angular2/src/core/metadata.dart'; + +var _visited = false; +void initReflector(reflector) { + if (_visited) return; + _visited = true; + reflector + ..registerType( + ToolTip, + new ReflectionInfo(const [ + const Directive( + selector: '[tool-tip]') + ], const [], () => new ToolTip(), null, const {'queryField': const [const ContentChild('child')]})) + ..registerSetters({'queryField': (o, v) => o.queryField = v}); +}