diff --git a/lib/core_dom/common.dart b/lib/core_dom/common.dart index 012023c00..7c2ff9966 100644 --- a/lib/core_dom/common.dart +++ b/lib/core_dom/common.dart @@ -4,6 +4,17 @@ List cloneElements(elements) { return elements.map((el) => el.clone(true)).toList(); } +void normalizeNgAttributes(dom.Element element) { + Map normalizedAttrs = {}; + element.attributes.forEach((attrName, value) { + if (attrName.startsWith('data-ng-')) { + normalizedAttrs[attrName.replaceFirst('data-', '')] = value; + element.attributes.remove(attrName); + } + }); + element.attributes.addAll(normalizedAttrs); +} + typedef ApplyMapping(NodeAttrs attrs, Scope scope, Object dst); class DirectiveRef { diff --git a/lib/core_dom/directive.dart b/lib/core_dom/directive.dart index 990ba4ca3..fa5c68d4e 100644 --- a/lib/core_dom/directive.dart +++ b/lib/core_dom/directive.dart @@ -22,7 +22,11 @@ class NodeAttrs { Map> _observers; - NodeAttrs(this.element); + NodeAttrs(this.element) { + if (element != null) { + normalizeNgAttributes(element); + } + } operator [](String attributeName) => element.attributes[attributeName]; diff --git a/lib/core_dom/selector.dart b/lib/core_dom/selector.dart index 83c69ac66..d31b09a8b 100644 --- a/lib/core_dom/selector.dart +++ b/lib/core_dom/selector.dart @@ -283,6 +283,9 @@ DirectiveSelector directiveSelectorFactory(DirectiveMap directives) { element.attributes['type'] = 'text'; } + // Normalize ng-* attributes + normalizeNgAttributes(element); + // Select node partialSelection = elementSelector.selectNode(directiveRefs, partialSelection, element, nodeName); diff --git a/test/core_dom/directive_spec.dart b/test/core_dom/directive_spec.dart index 26330eb00..d1dfa5b1e 100644 --- a/test/core_dom/directive_spec.dart +++ b/test/core_dom/directive_spec.dart @@ -10,7 +10,7 @@ main() { beforeEach(inject((TestBed tb) { _ = tb; - element = _.compile('
'); + element = _.compile('
'); nodeAttrs = new NodeAttrs(element); })); @@ -18,6 +18,7 @@ main() { expect(nodeAttrs['foo']).toEqual('bar'); expect(nodeAttrs['foo-bar']).toEqual('baz'); expect(nodeAttrs['foo-bar-baz']).toEqual('foo'); + expect(nodeAttrs['ng-foo']).toEqual('ngfoo'); }); it('should return null for unexistent attributes', () { @@ -27,7 +28,7 @@ main() { it('should provide a forEach function to iterate over attributes', () { Map attrMap = new Map(); nodeAttrs.forEach((k, v) => attrMap[k] = v); - expect(attrMap).toEqual({'foo': 'bar', 'foo-bar': 'baz', 'foo-bar-baz': 'foo'}); + expect(attrMap).toEqual({'foo': 'bar', 'foo-bar': 'baz', 'foo-bar-baz': 'foo', 'ng-foo': 'ngfoo'}); }); it('should provide a contains method', () { @@ -38,7 +39,7 @@ main() { }); it('should return the attribute names', () { - expect(nodeAttrs.keys.toList()..sort()).toEqual(['foo', 'foo-bar', 'foo-bar-baz']); + expect(nodeAttrs.keys.toList()..sort()).toEqual(['foo', 'foo-bar', 'foo-bar-baz', 'ng-foo']); }); }); } diff --git a/test/core_dom/selector_spec.dart b/test/core_dom/selector_spec.dart index f77975d3c..26ff293f4 100644 --- a/test/core_dom/selector_spec.dart +++ b/test/core_dom/selector_spec.dart @@ -13,6 +13,12 @@ import '../_specs.dart'; @NgDirective(selector:':contains(/abc/)') class _ContainsAbc{} @NgDirective(selector:'[*=/xyz/]') class _AttributeContainsXyz{} +@NgDirective(selector:'[ng-directive]') class _NgDirectiveAttr{} +@NgDirective(selector:'[ng-directive=d][foo=f]') class _NgDirectiveFooAttr{} +@NgDirective(selector:'b[ng-directive]') class _NgBElementDirectiveAttr{} +@NgDirective(selector:'[ng-directive=value]') class _NgDirectiveValueAttr{} +@NgDirective(selector:'b[ng-directive=value]') class _NgBElementDirectiveValue{} + @NgComponent(selector:'component') class _Component{} @NgDirective(selector:'[attribute]') class _Attribute{} @NgDirective(selector:'[structural]', @@ -54,6 +60,11 @@ main() { ..type(_BElementDirectiveValue) ..type(_ContainsAbc) ..type(_AttributeContainsXyz) + ..type(_NgDirectiveAttr) + ..type(_NgDirectiveFooAttr) + ..type(_NgBElementDirectiveAttr) + ..type(_NgDirectiveValueAttr) + ..type(_NgBElementDirectiveValue) ..type(_Component) ..type(_Attribute) ..type(_Structural) @@ -125,6 +136,51 @@ main() { ])); }); + + it('should match ng-directive on [attribute] with data- prefix', () { + expect(selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[ng-directive]', "value": 'abc', "element": element, + "name": 'directive' }])); + + expect(selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[ng-directive]', "value": '', "element": element, + "name": 'directive' }])); + }); + + + it('should match ng-directive on element[attribute] with data- prefix', () { + expect(selector(element = e('')), + toEqualsDirectiveInfos([ + { "selector": 'b', "value": null, "element": element}, + { "selector": '[ng-directive]', "value": 'abc', "element": element}, + { "selector": 'b[ng-directive]', "value": 'abc', "element": element} + ])); + }); + + + it('should match ng-directive on [attribute=value] with data- prefix', () { + expect(selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[ng-directive]', "value": 'value', "element": element}, + { "selector": '[ng-directive=value]', "value": 'value', "element": element} + ])); + }); + + + it('should match ng-directive on element[attribute=value] with data- prefix', () { + expect(selector(element = e('')), + toEqualsDirectiveInfos([ + { "selector": 'b', "value": null, "element": element, "name": null}, + { "selector": '[ng-directive]', "value": 'value', "element": element}, + { "selector": '[ng-directive=value]', "value": 'value', "element": element}, + { "selector": 'b[ng-directive]', "value": 'value', "element": element}, + { "selector": 'b[ng-directive=value]', "value": 'value', "element": element} + ])); + }); + + it('should match attributes', () { expect(selector(element = e('
')), toEqualsDirectiveInfos([