fix(dart/transform): Parse directives agnostic of annotation order

Now that we can specify `directives` in either `@View` or `@Component`,
we will try to parse from both.

Previously, we would trash any `directives` parsed in the first
annotation upon encountering the second annotation. This ensures that we
maintain that list of `directives` regardless of annotation ordering.
This commit is contained in:
Tim Blasi 2015-10-14 09:33:00 -07:00
parent f5159389b3
commit efddc9069c
6 changed files with 139 additions and 9 deletions

View File

@ -172,6 +172,7 @@ class _CompileDataCreator {
return retVal; return retVal;
} }
} }
/// Visitor responsible for processing the `annotations` property of a /// Visitor responsible for processing the `annotations` property of a
/// [RegisterType] object, extracting the `directives` dependencies, and adding /// [RegisterType] object, extracting the `directives` dependencies, and adding
/// their associated [CompileDirectiveMetadata] to the `directives` of a /// their associated [CompileDirectiveMetadata] to the `directives` of a
@ -198,8 +199,14 @@ class _DirectiveDependenciesVisitor extends Object
@override @override
Object visitInstanceCreationExpression(InstanceCreationExpression node) { Object visitInstanceCreationExpression(InstanceCreationExpression node) {
if (_isViewAnnotation(node) || _isComponentAnnotation(node)) { if (_isViewAnnotation(node) || _isComponentAnnotation(node)) {
compileData = new NormalizedComponentWithViewDirectives( if (compileData == null) {
null, <CompileDirectiveMetadata>[]); compileData = new NormalizedComponentWithViewDirectives(
null, <CompileDirectiveMetadata>[]);
} else {
// This is set above, after the visitor is finished. If this value is
// non-null it indicates that we forgot to call `reset()`.
assert(compileData.component == null);
}
node.visitChildren(this); node.visitChildren(this);
} }
return null; return null;
@ -212,7 +219,7 @@ class _DirectiveDependenciesVisitor extends Object
if (node.name is! Label || node.name.label is! SimpleIdentifier) { if (node.name is! Label || node.name.label is! SimpleIdentifier) {
logger.error( logger.error(
'Angular 2 currently only supports simple identifiers in directives.' 'Angular 2 currently only supports simple identifiers in directives.'
' Source: ${node}'); ' Source: ${node}');
return null; return null;
} }
if ('${node.name.label}' == 'directives') { if ('${node.name.label}' == 'directives') {
@ -244,7 +251,7 @@ class _DirectiveDependenciesVisitor extends Object
} else { } else {
logger.error( logger.error(
'Angular 2 currently only supports simple and prefixed identifiers ' 'Angular 2 currently only supports simple and prefixed identifiers '
'as values for "directives". Source: $node'); 'as values for "directives". Source: $node');
return; return;
} }
if (ngMeta.types.containsKey(name)) { if (ngMeta.types.containsKey(name)) {

View File

@ -61,12 +61,23 @@ void changeDetectorTests() {
'template_compiler/directive_aliases_files/hello2.ng_deps.dart'; 'template_compiler/directive_aliases_files/hello2.ng_deps.dart';
// Except for the directive argument in the View annotation, the generated // Except for the directive argument in the View annotation, the generated
// change detectors are identical. // change detectors are identical.
var output1 = (await process(new AssetId('a', input1Path))).templatesCode;
var output2 = (await process(new AssetId('a', input2Path))).templatesCode;
_formatThenExpectEquals(output1, output2);
});
it('should handle `directives` regardless of annotation ordering', () async {
// Input 2 is the same as input1, but has the @View annotation listed first.
var input1Path = 'template_compiler/annotation_ordering_files/'
'component_first.ng_deps.dart';
var input2Path = 'template_compiler/annotation_ordering_files/'
'view_first.ng_deps.dart';
// Except for the type name, the generated change detectors are identical.
var output1 = (await process(new AssetId('a', input1Path))) var output1 = (await process(new AssetId('a', input1Path)))
.ngDepsCode .templatesCode
.replaceFirst( .replaceAll('ComponentFirst', 'ViewFirst')
'directives: const [alias1]', 'directives: const [GoodbyeCmp]') .replaceAll('component_first', 'view_first');
.replaceFirst('hello1', 'hello2'); var output2 = (await process(new AssetId('a', input2Path))).templatesCode;
var output2 = (await process(new AssetId('a', input2Path))).ngDepsCode;
_formatThenExpectEquals(output1, output2); _formatThenExpectEquals(output1, output2);
}); });
} }

View File

@ -0,0 +1,24 @@
library test.src.transform.template_compiler.annotation_ordering_files.component_first.ng_deps.dart;
import 'component_first.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
import 'package:angular2/src/directives/ng_for.dart';
export 'component_first.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
ComponentFirst,
new ReflectionInfo(const [
const Component(selector: 'hello-app'),
const View(
template: '<li *ng-for="#thing of things"><div>test</div></li>',
directives: const [NgFor])
], const [
const []
], () => new ComponentFirst()));
}

View File

@ -0,0 +1,32 @@
{
"ComponentFirst":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"hello-app",
"exportAs": null,
"type": {
"id": 1,
"name": "ComponentFirst",
"moduleUrl": "asset:angular2/test/transform/template_compiler/ng_for_files/hello.dart"
},
"changeDetection": 5,
"inputs": {},
"outputs": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "<li *ng-for=\"#thing of things\"><div>test</div></li>",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,24 @@
library test.src.transform.template_compiler.annotation_ordering_files.view_first.ng_deps.dart;
import 'view_first.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
import 'package:angular2/src/directives/ng_for.dart';
export 'view_first.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
ViewFirst,
new ReflectionInfo(const [
const View(
template: '<li *ng-for="#thing of things"><div>test</div></li>',
directives: const [NgFor]),
const Component(selector: 'hello-app')
], const [
const []
], () => new ViewFirst()));
}

View File

@ -0,0 +1,32 @@
{
"ViewFirst":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"hello-app",
"exportAs": null,
"type": {
"id": 1,
"name": "ViewFirst",
"moduleUrl": "asset:angular2/test/transform/template_compiler/ng_for_files/hello.dart"
},
"changeDetection": 5,
"inputs": {},
"outputs": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "<li *ng-for=\"#thing of things\"><div>test</div></li>",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}