diff --git a/lib/core_dom/directive.dart b/lib/core_dom/directive.dart index 1a8bfada6..c61ba5e90 100644 --- a/lib/core_dom/directive.dart +++ b/lib/core_dom/directive.dart @@ -17,10 +17,18 @@ class NodeAttrs { NodeAttrs(this.element); - operator [](String attributeName) => - element.attributes[attributeName]; + operator [](String attributeName) { + var value = element.attributes[attributeName]; + if (value == null && attributeName.startsWith('ng-')) { + value = element.attributes['data-$attributeName']; + } + return value; + } operator []=(String attributeName, String value) { + if (attributeName.startsWith('ng-')) { + element.attributes.remove('data-$attributeName'); + } if (value == null) { element.attributes.remove(attributeName); } else { diff --git a/lib/core_dom/selector.dart b/lib/core_dom/selector.dart index 83c69ac66..8e8ace6f8 100644 --- a/lib/core_dom/selector.dart +++ b/lib/core_dom/selector.dart @@ -28,6 +28,31 @@ part of angular.core.dom; */ typedef List DirectiveSelector(dom.Node node); +String _normalizeKey(String key) => + key.startsWith('data-ng-') ? key.substring('data-'.length) : key; + +class _AttrValueMap implements Map { + var _map = {}; + + void addAll(Map other) { _map.addAll(other); } + bool containsKey(String key) => _map.containsKey(_normalizeKey(key)); + bool containsValue(Object value) => _map.containsValue(value); + void clear() { _map.clear(); } + void forEach(Function f) { _map.forEach(f); } + V putIfAbsent(String key, Function ifAbsent) => + _map.putIfAbsent(_normalizeKey(key), ifAbsent); + V remove(String key) => _map.remove(_normalizeKey(key)); + + V operator[](String key) => _map[_normalizeKey(key)]; + void operator[]=(String key, V value) { _map[_normalizeKey(key)] = value; } + + bool get isEmpty => _map.isEmpty; + bool get isNotEmpty => _map.isNotEmpty; + int get length => _map.length; + Iterable get keys => _map.keys; + Iterable get values => _map.values; +} + class _Directive { final Type type; final NgAnnotation annotation; @@ -76,7 +101,6 @@ class _SelectorPart { : element; } - class _ElementSelector { final String name; @@ -86,8 +110,8 @@ class _ElementSelector { var classMap = >{}; var classPartialMap = {}; - var attrValueMap = >>{}; - var attrValuePartialMap = >{}; + var attrValueMap = new _AttrValueMap>>(); + var attrValuePartialMap = new _AttrValueMap>(); _ElementSelector(this.name); @@ -175,7 +199,7 @@ class _ElementSelector { dom.Node node, String attrName, String attrValue) { - String matchingKey = _matchingKey(attrValueMap.keys, attrName); + String matchingKey = _matchingKey(attrValueMap.keys, _normalizeKey(attrName)); if (matchingKey != null) { Map> valuesMap = attrValueMap[matchingKey]; @@ -306,6 +330,8 @@ DirectiveSelector directiveSelectorFactory(DirectiveMap directives) { // we need to pass the name to the directive by prefixing it to // the value. Yes it is a bit of a hack. directives[selectorRegExp.annotation].forEach((type) { + if (attrName.startsWith('data-ng-')) + attrName = attrName.substring('data-'.length); directiveRefs.add(new DirectiveRef( node, type, selectorRegExp.annotation, '$attrName=$value')); }); diff --git a/lib/directive/ng_cloak.dart b/lib/directive/ng_cloak.dart index a661af8e5..ab2caae82 100644 --- a/lib/directive/ng_cloak.dart +++ b/lib/directive/ng_cloak.dart @@ -27,6 +27,7 @@ part of angular.directive; class NgCloakDirective { NgCloakDirective(dom.Element element) { element.attributes.remove('ng-cloak'); + element.attributes.remove('data-ng-cloak'); element.classes.remove('ng-cloak'); } } diff --git a/lib/directive/ng_src_boolean.dart b/lib/directive/ng_src_boolean.dart index f2d57ead6..e9d8812a7 100644 --- a/lib/directive/ng_src_boolean.dart +++ b/lib/directive/ng_src_boolean.dart @@ -95,9 +95,15 @@ class NgAttributeDirective implements NgAttachAware { void attach() { String ngAttrPrefix = 'ng-attr-'; + String dataNgAttrPrefix = 'data-ng-attr-'; _attrs.forEach((key, value) { + var newKey; if (key.startsWith(ngAttrPrefix)) { - var newKey = key.substring(ngAttrPrefix.length); + newKey = key.substring(ngAttrPrefix.length); + } else if (key.startsWith(dataNgAttrPrefix)) { + newKey = key.substring(dataNgAttrPrefix.length); + } + if (newKey != null) { _attrs[newKey] = value; _attrs.observe(key, (newValue) => _attrs[newKey] = newValue ); } diff --git a/test/core_dom/directive_spec.dart b/test/core_dom/directive_spec.dart index 26330eb00..9728eff9b 100644 --- a/test/core_dom/directive_spec.dart +++ b/test/core_dom/directive_spec.dart @@ -10,16 +10,21 @@ main() { beforeEach(inject((TestBed tb) { _ = tb; - element = _.compile('
'); + element = _.compile('
'); nodeAttrs = new NodeAttrs(element); })); - it('should transform names to camel case', () { + it('should return attribute value', () { expect(nodeAttrs['foo']).toEqual('bar'); expect(nodeAttrs['foo-bar']).toEqual('baz'); expect(nodeAttrs['foo-bar-baz']).toEqual('foo'); }); + it('should ignore data prefix', () { + expect(nodeAttrs['data-ng-qux']).toEqual('qux'); + expect(nodeAttrs['ng-qux']).toEqual('qux'); + }); + it('should return null for unexistent attributes', () { expect(nodeAttrs['baz']).toBeNull(); }); @@ -27,7 +32,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', 'data-ng-qux': 'qux'}); }); it('should provide a contains method', () { @@ -38,7 +43,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(['data-ng-qux', 'foo', 'foo-bar', 'foo-bar-baz']); }); }); } diff --git a/test/core_dom/selector_spec.dart b/test/core_dom/selector_spec.dart index f77975d3c..6165a2da6 100644 --- a/test/core_dom/selector_spec.dart +++ b/test/core_dom/selector_spec.dart @@ -30,6 +30,7 @@ import '../_specs.dart'; @NgDirective(selector: '[two-directives]') class _OneOfTwoDirectives {} @NgDirective(selector: '[two-directives]') class _TwoOfTwoDirectives {} +@NgDirective(selector:'[ng-directive]') class _NgDirectiveAttr{} main() { describe('Selector', () { @@ -60,7 +61,8 @@ main() { ..type(_IgnoreChildren) ..type(_TwoDirectives) ..type(_OneOfTwoDirectives) - ..type(_TwoOfTwoDirectives); + ..type(_TwoOfTwoDirectives) + ..type(_NgDirectiveAttr); })); beforeEach(inject((DirectiveMap directives) { selector = directiveSelectorFactory(directives); @@ -195,6 +197,18 @@ main() { { "selector": '[two-directives]', "value": '', "element": element} ])); }); + + it('should match directive on [data-ng-attribute] ignoring data prefix', () { + expect(selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[ng-directive]', "value": 'abc', "element": element, + "name": 'ng-directive' }])); + + expect(selector(element = e('
')), + toEqualsDirectiveInfos([ + { "selector": '[ng-directive]', "value": '', "element": element, + "name": 'ng-directive' }])); + }); }); } diff --git a/test/directive/ng_a_spec.dart b/test/directive/ng_a_spec.dart index 5a8fd0fb6..f64e720d9 100644 --- a/test/directive/ng_a_spec.dart +++ b/test/directive/ng_a_spec.dart @@ -42,5 +42,12 @@ main() { _.triggerEvent(_.rootElement, 'click', 'MouseEvent'); expect(window.location.href.endsWith("#url")).toEqual(true); })); + + it('should bind click listener with data attribute', inject((Scope scope) { + _.compile(''); + _.triggerEvent(_.rootElement, 'click', 'MouseEvent'); + expect(_.rootScope.context['abc']).toEqual(true); + expect(_.rootScope.context['event'] is dom.UIEvent).toEqual(true); + })); }); } diff --git a/test/directive/ng_bind_html_spec.dart b/test/directive/ng_bind_html_spec.dart index 791bc7df0..8f077b316 100644 --- a/test/directive/ng_bind_html_spec.dart +++ b/test/directive/ng_bind_html_spec.dart @@ -34,6 +34,16 @@ main() { expect(element.html()).toEqual('Google!'); }); })); + + it('should sanitize and set innerHtml and sanitize and set html with data attribute', + inject((Scope scope, Injector injector, Compiler compiler, DirectiveMap directives) { + var element = $('
'); + compiler(element, directives)(injector, element); + scope.context['htmlVar'] = 'Google!'; + scope.apply(); + // Sanitization removes the href attribute on the tag. + expect(element.html()).toEqual('Google!'); + })); }); } diff --git a/test/directive/ng_bind_spec.dart b/test/directive/ng_bind_spec.dart index f08509d7e..8432242a7 100644 --- a/test/directive/ng_bind_spec.dart +++ b/test/directive/ng_bind_spec.dart @@ -35,5 +35,13 @@ main() { }); expect(element.text).toEqual('1'); })); + + it('should set.text with data attribute', inject((Scope scope, Injector injector, Compiler compiler, DirectiveMap directives) { + var element = $('
'); + compiler(element, directives)(injector, element); + scope.context['a'] = "abc123"; + scope.apply(); + expect(element.text()).toEqual('abc123'); + })); }); } diff --git a/test/directive/ng_bind_template_spec.dart b/test/directive/ng_bind_template_spec.dart index aad7af02d..a9e517463 100644 --- a/test/directive/ng_bind_template_spec.dart +++ b/test/directive/ng_bind_template_spec.dart @@ -22,5 +22,20 @@ main() { expect(element.text).toEqual('Good-Bye Heisenberg!'); })); + + it('should bind template with data attribute', + inject((Scope scope, Injector injector, Compiler compiler) { + var element = _.compile('
'); + scope.context['salutation'] = 'Hello'; + scope.context['name'] = 'Heisenberg'; + scope.apply(); + + expect(element.text).toEqual('Hello Heisenberg!'); + + scope.context['salutation'] = 'Good-Bye'; + scope.apply(); + + expect(element.text).toEqual('Good-Bye Heisenberg!'); + })); }); } diff --git a/test/directive/ng_class_spec.dart b/test/directive/ng_class_spec.dart index 9d6914a46..0f29d7b16 100644 --- a/test/directive/ng_class_spec.dart +++ b/test/directive/ng_class_spec.dart @@ -308,5 +308,25 @@ main() { expect(e2.classes.contains('even')).toBeTruthy(); expect(e2.classes.contains('odd')).toBeFalsy(); }); + + it('should add new and remove old classes dynamically with data attribute', () { + var element = _.compile('
'); + _.rootScope.context['dynClass'] = 'A'; + _.rootScope.apply(); + expect(element.classes.contains('existing')).toBe(true); + expect(element.classes.contains('A')).toBe(true); + + _.rootScope.context['dynClass'] = 'B'; + _.rootScope.apply(); + expect(element.classes.contains('existing')).toBe(true); + expect(element.classes.contains('A')).toBe(false); + expect(element.classes.contains('B')).toBe(true); + + _.rootScope.context['dynClass'] = null; + _.rootScope.apply(); + expect(element.classes.contains('existing')).toBe(true); + expect(element.classes.contains('A')).toBe(false); + expect(element.classes.contains('B')).toBe(false); + }); }); } diff --git a/test/directive/ng_cloak_spec.dart b/test/directive/ng_cloak_spec.dart index bbf35d6c9..7781fb80c 100644 --- a/test/directive/ng_cloak_spec.dart +++ b/test/directive/ng_cloak_spec.dart @@ -45,5 +45,12 @@ main() { expect(element.hasClass('ng-cloak')).toBe(false); expect(element.hasClass('bar')).toBe(true); }); + + it('should get removed when an element is compiled with data attribute', () { + var element = $('
'); + expect(element.attr('data-ng-cloak')).toEqual(''); + _.compile(element); + expect(element.attr('data-ng-cloak')).toBeNull(); + }); }); } diff --git a/test/directive/ng_events_spec.dart b/test/directive/ng_events_spec.dart index 39cdfee35..9f220754f 100644 --- a/test/directive/ng_events_spec.dart +++ b/test/directive/ng_events_spec.dart @@ -20,6 +20,19 @@ void addTest(String name, [String eventType='MouseEvent', String eventName]) { expect(_.rootScope.context['event'] is dom.UIEvent).toEqual(true); })); }); + + describe('data-ng-$name', () { + TestBed _; + + beforeEach(inject((TestBed tb) => _ = tb)); + + it('should evaluate the expression on data-$name', inject(() { + _.compile(''); + _.triggerEvent(_.rootElement, eventName, eventType); + expect(_.rootScope.context['abc']).toEqual(true); + expect(_.rootScope.context['event'] is dom.UIEvent).toEqual(true); + })); + }); } main() { diff --git a/test/directive/ng_if_spec.dart b/test/directive/ng_if_spec.dart index ec2a92f51..a968c6710 100644 --- a/test/directive/ng_if_spec.dart +++ b/test/directive/ng_if_spec.dart @@ -222,4 +222,29 @@ main() { expect(element.find('span').html()).toEqual(''); } ); + + they('should add/remove the element with data attribute', + [ '
content
', + '
content
'], + (html) { + compile(html); + // The span node should NOT exist in the DOM. + expect(element.contents().length).toEqual(1); + expect(element.find('span').html()).toEqual(''); + + rootScope.apply(() { + rootScope.context['isVisible'] = true; + }); + + // The span node SHOULD exist in the DOM. + expect(element.contents().length).toEqual(2); + expect(element.find('span').html()).toEqual('content'); + + rootScope.apply(() { + rootScope.context['isVisible'] = false; + }); + + expect(element.find('span').html()).toEqual(''); + } + ); } diff --git a/test/directive/ng_include_spec.dart b/test/directive/ng_include_spec.dart index 625821bdc..6dc6a30ee 100644 --- a/test/directive/ng_include_spec.dart +++ b/test/directive/ng_include_spec.dart @@ -42,5 +42,19 @@ main() { expect(element.text).toEqual('I am Vojta'); }))); + it('should fetch template from literal url with data attribute', async(inject((Scope scope, TemplateCache cache) { + cache.put('tpl.html', new HttpResponse(200, 'my name is {{name}}')); + + var element = _.compile('
'); + + expect(element.innerHtml).toEqual(''); + + microLeap(); // load the template from cache. + scope.applyInZone(() { + scope.context['name'] = 'Vojta'; + }); + expect(element.text).toEqual('my name is Vojta'); + }))); + }); } diff --git a/test/directive/ng_model_spec.dart b/test/directive/ng_model_spec.dart index f3634151e..2821fd678 100644 --- a/test/directive/ng_model_spec.dart +++ b/test/directive/ng_model_spec.dart @@ -109,6 +109,16 @@ describe('ng-model', () { expect(element.selectionStart).toEqual(3); expect(element.selectionEnd).toEqual(3); })); + + it('should update input value from model with data attribute', inject(() { + _.compile(''); + _.rootScope.apply(); + + expect((_.rootElement as dom.InputElement).value).toEqual(''); + + _.rootScope.apply('model = "misko"'); + expect((_.rootElement as dom.InputElement).value).toEqual('misko'); + })); }); /* This function simulates typing the given text into the input diff --git a/test/directive/ng_model_validators_spec.dart b/test/directive/ng_model_validators_spec.dart index d42e5fb5c..3ca47d8ef 100644 --- a/test/directive/ng_model_validators_spec.dart +++ b/test/directive/ng_model_validators_spec.dart @@ -70,6 +70,34 @@ describe('ngModel validators', () { expect(model.valid).toEqual(true); expect(model.invalid).toEqual(false); })); + + it('should validate the input field depending on if data-ng-required is true', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + _.rootScope.apply(); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['requireMe'] = true; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['requireMe'] = false; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); }); describe('[type="url"]', () { @@ -203,6 +231,43 @@ describe('ngModel validators', () { expect(model.invalid).toEqual(false); })); + it('should validate the input field if a data-ng-pattern attribute is provided', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + _.rootScope.context['myPattern'] = "[a-z]+"; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + _.rootScope.context['myPattern'] = "[0-9]+"; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "123"; + _.rootScope.context['myPattern'] = "123"; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + })); + it('should validate the input field if a pattern attribute is provided', inject((RootScope scope) { _.compile(''); Probe probe = _.rootScope.context['i']; @@ -292,6 +357,34 @@ describe('ngModel validators', () { expect(model.valid).toEqual(false); expect(model.invalid).toEqual(true); })); + + it('should validate the input field if a data-ng-minlength attribute is provided', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abcdef"; + _.rootScope.context['len'] = 3; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + _.rootScope.context['len'] = 5; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + })); }); describe('maxlength', () { @@ -348,5 +441,33 @@ describe('ngModel validators', () { expect(model.valid).toEqual(false); expect(model.invalid).toEqual(true); })); + + it('should validate the input field if a data-ng-maxlength attribute is provided', inject((RootScope scope) { + _.compile(''); + Probe probe = _.rootScope.context['i']; + var model = probe.directive(NgModel); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abcdef"; + _.rootScope.context['len'] = 6; + }); + + model.validate(); + expect(model.valid).toEqual(true); + expect(model.invalid).toEqual(false); + + _.rootScope.apply(() { + _.rootScope.context['val'] = "abc"; + _.rootScope.context['len'] = 1; + }); + + model.validate(); + expect(model.valid).toEqual(false); + expect(model.invalid).toEqual(true); + })); }); }); diff --git a/test/directive/ng_non_bindable_spec.dart b/test/directive/ng_non_bindable_spec.dart index 867716c9d..e4223d362 100644 --- a/test/directive/ng_non_bindable_spec.dart +++ b/test/directive/ng_non_bindable_spec.dart @@ -35,5 +35,33 @@ main() { // Bindings on the same node are processed. expect(nonBindableDiv.attr('foo')).toEqual('one'); })); + + it('should set ignore all other markup/directives on the descendent nodes with data attribute', + inject((Scope scope, Injector injector, Compiler compiler, DirectiveMap directives) { + var element = $('
' + + ' {{a}}' + + ' ' + + '
' + + ' {{b}}' + + '
' + + ' {{a}}' + + ' ' + + '
'); + compiler(element, directives)(injector, element); + scope.context['a'] = "one"; + scope.context['b'] = "two"; + scope.apply(); + // Bindings not contained by data-ng-non-bindable should resolve. + expect(element.find("#s1").text().trim()).toEqual('one'); + expect(element.find("#s2").text().trim()).toEqual('two'); + expect(element.find("#s3").text().trim()).toEqual('one'); + expect(element.find("#s4").text().trim()).toEqual('two'); + // Bindings contained by data-ng-non-bindable should be left alone. + var nonBindableDiv = element.find("div"); + expect(nonBindableDiv.html().trim()).toEqual('{{b}}'); + expect(nonBindableDiv.text().trim()).toEqual('{{b}}'); + // Bindings on the same node are processed. + expect(nonBindableDiv.attr('foo')).toEqual('one'); + })); }); } diff --git a/test/directive/ng_pluralize_spec.dart b/test/directive/ng_pluralize_spec.dart index 951e1c80a..1a06e9f42 100644 --- a/test/directive/ng_pluralize_spec.dart +++ b/test/directive/ng_pluralize_spec.dart @@ -8,6 +8,7 @@ main() { describe('deal with pluralized strings without offset', () { var element; var elementAlt; + var elementData; var elt; TestBed _; @@ -31,6 +32,15 @@ main() { "when-other='You have {} new emails'>" + '

' ); + + elementData = _.compile( + '

" + + '

' + ); })); it('should show single/plural strings', () { @@ -38,51 +48,61 @@ main() { _.rootScope.apply(); expect(element.text).toEqual('You have no new email'); expect(elementAlt.text).toEqual('You have no new email'); + expect(elementData.text).toEqual('You have no new email'); _.rootScope.context['email'] = '0'; _.rootScope.apply(); expect(element.text).toEqual('You have no new email'); expect(elementAlt.text).toEqual('You have no new email'); + expect(elementData.text).toEqual('You have no new email'); _.rootScope.context['email'] = 1; _.rootScope.apply(); expect(element.text).toEqual('You have one new email'); expect(elementAlt.text).toEqual('You have one new email'); + expect(elementData.text).toEqual('You have one new email'); _.rootScope.context['email'] = 0.01; _.rootScope.apply(); expect(element.text).toEqual('You have 0.01 new emails'); expect(elementAlt.text).toEqual('You have 0.01 new emails'); + expect(elementData.text).toEqual('You have 0.01 new emails'); _.rootScope.context['email'] = '0.1'; _.rootScope.apply(); expect(element.text).toEqual('You have 0.1 new emails'); expect(elementAlt.text).toEqual('You have 0.1 new emails'); + expect(elementData.text).toEqual('You have 0.1 new emails'); _.rootScope.context['email'] = 2; _.rootScope.apply(); expect(element.text).toEqual('You have 2 new emails'); expect(elementAlt.text).toEqual('You have 2 new emails'); + expect(elementData.text).toEqual('You have 2 new emails'); _.rootScope.context['email'] = -0.1; _.rootScope.apply(); expect(element.text).toEqual('You have -0.1 new emails'); expect(elementAlt.text).toEqual('You have -0.1 new emails'); + expect(elementData.text).toEqual('You have -0.1 new emails'); _.rootScope.context['email'] = '-0.01'; _.rootScope.apply(); expect(element.text).toEqual('You have -0.01 new emails'); expect(elementAlt.text).toEqual('You have -0.01 new emails'); + expect(elementData.text).toEqual('You have -0.01 new emails'); _.rootScope.context['email'] = -2; _.rootScope.apply(); expect(element.text).toEqual('You have -2 new emails'); expect(elementAlt.text).toEqual('You have -2 new emails'); + expect(elementData.text).toEqual('You have -2 new emails'); _.rootScope.context['email'] = -1; _.rootScope.apply(); expect(element.text).toEqual('You have negative email. Whohoo!'); expect(elementAlt.text).toEqual('You have negative email. Whohoo!'); + expect(elementData.text).toEqual('You have negative email. Whohoo!'); }); it('should show single/plural strings with mal-formed inputs', () { @@ -90,41 +110,49 @@ main() { _.rootScope.apply(); expect(element.text).toEqual(''); expect(elementAlt.text).toEqual(''); + expect(elementData.text).toEqual(''); _.rootScope.context['email'] = null; _.rootScope.apply(); expect(element.text).toEqual(''); expect(elementAlt.text).toEqual(''); + expect(elementData.text).toEqual(''); _.rootScope.context['email'] = 'a3'; _.rootScope.apply(); expect(element.text).toEqual(''); expect(elementAlt.text).toEqual(''); + expect(elementData.text).toEqual(''); _.rootScope.context['email'] = '011'; _.rootScope.apply(); expect(element.text).toEqual('You have 11 new emails'); expect(elementAlt.text).toEqual('You have 11 new emails'); + expect(elementData.text).toEqual('You have 11 new emails'); _.rootScope.context['email'] = '-011'; _.rootScope.apply(); expect(element.text).toEqual('You have -11 new emails'); expect(elementAlt.text).toEqual('You have -11 new emails'); + expect(elementData.text).toEqual('You have -11 new emails'); _.rootScope.context['email'] = '1fff'; _.rootScope.apply(); expect(element.text).toEqual(''); expect(elementAlt.text).toEqual(''); + expect(elementData.text).toEqual(''); _.rootScope.context['email'] = '0aa22'; _.rootScope.apply(); expect(element.text).toEqual(''); expect(elementAlt.text).toEqual(''); + expect(elementData.text).toEqual(''); _.rootScope.context['email'] = '000001'; _.rootScope.apply(); expect(element.text).toEqual('You have one new email'); expect(elementAlt.text).toEqual('You have one new email'); + expect(elementData.text).toEqual('You have one new email'); }); }); diff --git a/test/directive/ng_repeat_spec.dart b/test/directive/ng_repeat_spec.dart index eb0fa376c..d56a9f1cf 100644 --- a/test/directive/ng_repeat_spec.dart +++ b/test/directive/ng_repeat_spec.dart @@ -394,5 +394,14 @@ main() { }); }); + it(r'should set create a list of items with data attribute', inject((Scope scope, Compiler compiler, Injector injector) { + var element = $('
{{item}}
'); + BlockFactory blockFactory = compiler(element, directives); + Block block = blockFactory(injector, element); + scope.context['items'] = ['a', 'b']; + scope.apply(); + expect(element.text()).toEqual('ab'); + })); + }); } diff --git a/test/directive/ng_show_hide_spec.dart b/test/directive/ng_show_hide_spec.dart index c0c01d66f..9498a0eff 100644 --- a/test/directive/ng_show_hide_spec.dart +++ b/test/directive/ng_show_hide_spec.dart @@ -23,6 +23,22 @@ main() { }); expect(_.rootElement).not.toHaveClass('ng-hide'); }); + + it('should add/remove ng-hide class with data attribute', () { + _.compile('
'); + + expect(_.rootElement).not.toHaveClass('ng-hide'); + + _.rootScope.apply(() { + _.rootScope.context['isHidden'] = true; + }); + expect(_.rootElement).toHaveClass('ng-hide'); + + _.rootScope.apply(() { + _.rootScope.context['isHidden'] = false; + }); + expect(_.rootElement).not.toHaveClass('ng-hide'); + }); }); describe('NgShow', () { @@ -44,5 +60,21 @@ main() { }); expect(_.rootElement).toHaveClass('ng-hide'); }); + + it('should add/remove ng-hide class with data attribute', () { + _.compile('
'); + + expect(_.rootElement).not.toHaveClass('ng-hide'); + + _.rootScope.apply(() { + _.rootScope.context['isShown'] = true; + }); + expect(_.rootElement).not.toHaveClass('ng-hide'); + + _.rootScope.apply(() { + _.rootScope.context['isShown'] = false; + }); + expect(_.rootElement).toHaveClass('ng-hide'); + }); }); } diff --git a/test/directive/ng_src_boolean_spec.dart b/test/directive/ng_src_boolean_spec.dart index e9a8a1795..2f3556f67 100644 --- a/test/directive/ng_src_boolean_spec.dart +++ b/test/directive/ng_src_boolean_spec.dart @@ -245,5 +245,11 @@ main() { _.rootScope.apply(); expect(_.rootElement.attributes['quack']).toEqual('vanilla'); })); + + it('should bind * even if no interpolation with data attribute', inject(() { + _.compile(''); + _.rootScope.apply(); + expect(_.rootElement.attributes['quack']).toEqual('vanilla'); + })); }); } diff --git a/test/directive/ng_style_spec.dart b/test/directive/ng_style_spec.dart index 8406a14cc..4ac204cce 100644 --- a/test/directive/ng_style_spec.dart +++ b/test/directive/ng_style_spec.dart @@ -14,6 +14,12 @@ main() => describe('NgStyle', () { expect(element.style.height).toEqual('40px'); }); + it('should set with data attribute', () { + dom.Element element = _.compile('
'); + _.rootScope.apply(); + expect(element.style.height).toEqual('40px'); + }); + it('should silently ignore undefined style', () { dom.Element element = _.compile('
'); diff --git a/test/directive/ng_switch_spec.dart b/test/directive/ng_switch_spec.dart index 55f0c70ff..b1fff72b2 100644 --- a/test/directive/ng_switch_spec.dart +++ b/test/directive/ng_switch_spec.dart @@ -200,4 +200,30 @@ main() => describe('ngSwitch', () { expect(child2).toBeDefined(); expect(child2).not.toBe(child1); })); + + it('should switch on value change with data attribute', inject(() { + var element = _.compile( + '
' + + '
first:{{name}}
' + + '
second:{{name}}
' + + '
true:{{name}}
' + + '
'); + expect(element.innerHtml).toEqual( + ''); + _.rootScope.context['select'] = 1; + _.rootScope.apply(); + expect(element.text).toEqual('first:'); + _.rootScope.context['name'] = "shyam"; + _.rootScope.apply(); + expect(element.text).toEqual('first:shyam'); + _.rootScope.context['select'] = 2; + _.rootScope.apply(); + expect(element.text).toEqual('second:shyam'); + _.rootScope.context['name'] = 'misko'; + _.rootScope.apply(); + expect(element.text).toEqual('second:misko'); + _.rootScope.context['select'] = true; + _.rootScope.apply(); + expect(element.text).toEqual('true:misko'); + })); });