Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 581861e

Browse files
vicbmhevery
authored andcommitted
feat(ngElement): add support for attributes
1 parent a36d2f2 commit 581861e

File tree

3 files changed

+161
-55
lines changed

3 files changed

+161
-55
lines changed

lib/core_dom/ng_element.dart

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,65 @@ part of angular.core.dom_internal;
22

33
@NgInjectableService()
44
class NgElement {
5+
static const _TO_BE_REMOVED = const Object();
56

67
final dom.Element node;
78
final Scope _scope;
89
final NgAnimate _animate;
9-
final _classes = new Map<String, bool>();
1010

11-
NgElement(this.node, this._scope, NgAnimate this._animate);
11+
final _classesToUpdate = <String, bool>{};
12+
final _attributesToUpdate = <String, dynamic>{};
1213

13-
addClass(String className) {
14-
if(_classes.isEmpty) {
15-
_listenOnWrite();
16-
}
17-
_classes[className] = true;
14+
bool _writeScheduled = false;
15+
16+
NgElement(this.node, this._scope, this._animate);
17+
18+
void addClass(String className) {
19+
_scheduleDomWrite();
20+
_classesToUpdate[className] = true;
1821
}
1922

20-
removeClass(String className) {
21-
if(_classes.isEmpty) {
22-
_listenOnWrite();
23-
}
24-
_classes[className] = false;
23+
void removeClass(String className) {
24+
_scheduleDomWrite();
25+
_classesToUpdate[className] = false;
26+
}
27+
28+
// todo(vicb) add tests
29+
void setAttribute(String attrName, [value = '']) {
30+
_scheduleDomWrite();
31+
_attributesToUpdate[attrName] = value == null ? '' : value;
2532
}
2633

27-
_listenOnWrite() {
28-
_scope.rootScope.domWrite(() => flush());
34+
void removeAttribute(String attrName) {
35+
_scheduleDomWrite();
36+
_attributesToUpdate[attrName] = _TO_BE_REMOVED;
2937
}
3038

31-
flush() {
32-
_classes.forEach((className, status) {
33-
status == true
39+
_scheduleDomWrite() {
40+
if (!_writeScheduled) {
41+
_writeScheduled = true;
42+
_scope.rootScope.domWrite(() {
43+
_writeToDom();
44+
_writeScheduled = false;
45+
});
46+
}
47+
}
48+
49+
_writeToDom() {
50+
_classesToUpdate.forEach((String className, bool toBeAdded) {
51+
toBeAdded
3452
? _animate.addClass(node, className)
3553
: _animate.removeClass(node, className);
3654
});
37-
_classes.clear();
55+
_classesToUpdate.clear();
56+
57+
_attributesToUpdate.forEach((String attrName, value) {
58+
if (value == _TO_BE_REMOVED) {
59+
node.attributes.remove(attrName);
60+
} else {
61+
node.attributes[attrName] = value;
62+
}
63+
});
64+
_attributesToUpdate.clear();
3865
}
3966
}

test/_specs.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ class Expect {
8888
toHaveClass(cls) => unit.expect(actual.classes.contains(cls), true,
8989
reason: ' Expected ${actual} to have css class ${cls}');
9090

91+
toHaveAttribute(name, [value = null]) {
92+
unit.expect(actual.attributes.containsKey(name), true,
93+
reason: 'Epxected $actual to have attribute $name');
94+
if (value != null) {
95+
unit.expect(actual.attributes[name], value,
96+
reason: 'Epxected $actual attribute "$name" to be "$value"');
97+
}
98+
}
99+
91100
toEqualSelect(options) {
92101
var actualOptions = [];
93102

@@ -164,6 +173,8 @@ class NotExpect {
164173

165174
toHaveClass(cls) => unit.expect(actual.classes.contains(cls), false,
166175
reason: ' Expected ${actual} to not have css class ${cls}');
176+
toHaveAttribute(name) => unit.expect(actual.attributes.containsKey(name),
177+
false, reason: ' Expected $actual to not have attribute "$name"');
167178
toBe(expected) => unit.expect(actual,
168179
unit.predicate((actual) => !identical(expected, actual), 'not $expected'));
169180
toEqual(expected) => unit.expect(actual,

test/core_dom/ng_element_spec.dart

Lines changed: 105 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,75 +6,143 @@ import 'dart:html' as dom;
66
void main() {
77
describe('ngElement', () {
88

9-
it('should add classes on domWrite to the element',
10-
(TestBed _, NgAnimate animate) {
9+
describe('classes', () {
10+
it('should add classes to the element on domWrite',
11+
(TestBed _, NgAnimate animate) {
12+
13+
var scope = _.rootScope;
14+
var element = e('<div></div>');
15+
var ngElement = new NgElement(element, scope, animate);
16+
17+
ngElement..addClass('one')..addClass('two three');
18+
19+
['one', 'two', 'three'].forEach((className) {
20+
expect(element).not.toHaveClass(className);
21+
});
22+
23+
scope.apply();
24+
25+
['one', 'two', 'three'].forEach((className) {
26+
expect(element).toHaveClass(className);
27+
});
28+
});
29+
30+
it('should remove classes from the element on domWrite',
31+
(TestBed _, NgAnimate animate) {
32+
33+
var scope = _.rootScope;
34+
var element = e('<div class="one two three four"></div>');
35+
var ngElement = new NgElement(element, scope, animate);
36+
37+
ngElement..removeClass('one')
38+
..removeClass('two')
39+
..removeClass('three');
40+
41+
['one', 'two', 'three', 'four'].forEach((className) {
42+
expect(element).toHaveClass(className);
43+
});
44+
45+
scope.apply();
46+
47+
['one', 'two', 'three'].forEach((className) {
48+
expect(element).not.toHaveClass(className);
49+
});
50+
expect(element).toHaveClass('four');
51+
});
52+
53+
it('should always apply the last dom operation on the given className',
54+
(TestBed _, NgAnimate animate) {
55+
56+
var scope = _.rootScope;
57+
var element = e('<div></div>');
58+
var ngElement = new NgElement(element, scope, animate);
59+
60+
ngElement..addClass('one')
61+
..addClass('one')
62+
..removeClass('one');
63+
64+
expect(element).not.toHaveClass('one');
65+
66+
scope.apply();
67+
68+
expect(element).not.toHaveClass('one');
69+
70+
ngElement..removeClass('one')
71+
..removeClass('one')
72+
..addClass('one');
73+
74+
scope.apply();
75+
76+
expect(element.classes.contains('one')).toBe(true);
77+
});
78+
});
79+
});
80+
81+
describe('attributes', () {
82+
it('should set attributes on domWrite to the element',
83+
(TestBed _, NgAnimate animate) {
1184

1285
var scope = _.rootScope;
13-
var element = _.compile('<div></div>');
86+
var element = e('<div></div>');
1487
var ngElement = new NgElement(element, scope, animate);
1588

16-
ngElement.addClass('one');
17-
ngElement.addClass('two three');
89+
ngElement.setAttribute('id', 'foo');
90+
ngElement.setAttribute('title', 'bar');
1891

19-
['one','two','three'].forEach((className) {
20-
expect(element.classes.contains(className)).toBe(false);
92+
['id', 'title'].forEach((name) {
93+
expect(element).not.toHaveAttribute(name);
2194
});
2295

2396
scope.apply();
2497

25-
['one','two','three'].forEach((className) {
26-
expect(element.classes.contains(className)).toBe(true);
27-
});
98+
expect(element).toHaveAttribute('id', 'foo');
99+
expect(element).toHaveAttribute('title', 'bar');
28100
});
29101

30-
it('should remove classes on domWrite to the element',
31-
(TestBed _, NgAnimate animate) {
102+
it('should remove attributes from the element on domWrite ',
103+
(TestBed _, NgAnimate animate) {
32104

33105
var scope = _.rootScope;
34-
var element = _.compile('<div class="one two three four"></div>');
106+
var element = e('<div id="foo" title="bar"></div>');
35107
var ngElement = new NgElement(element, scope, animate);
36108

37-
ngElement.removeClass('one');
38-
ngElement.removeClass('two');
39-
ngElement.removeClass('three');
109+
ngElement..removeAttribute('id')
110+
..removeAttribute('title');
40111

41-
['one','two','three'].forEach((className) {
42-
expect(element.classes.contains(className)).toBe(true);
43-
});
44-
expect(element.classes.contains('four')).toBe(true);
112+
expect(element).toHaveAttribute('id', 'foo');
113+
expect(element).toHaveAttribute('title', 'bar');
45114

46115
scope.apply();
47116

48-
['one','two','three'].forEach((className) {
49-
expect(element.classes.contains(className)).toBe(false);
50-
});
51-
expect(element.classes.contains('four')).toBe(true);
117+
expect(element).not.toHaveAttribute('id');
118+
expect(element).not.toHaveAttribute('title');
52119
});
53120

54-
it('should always apply the last dom operation on the given className',
55-
(TestBed _, NgAnimate animate) {
121+
it('should always apply the last operation on the attribute',
122+
(TestBed _, NgAnimate animate) {
56123

57124
var scope = _.rootScope;
58-
var element = _.compile('<div></div>');
125+
var element = e('<div></div>');
59126
var ngElement = new NgElement(element, scope, animate);
60127

61-
ngElement.addClass('one');
62-
ngElement.addClass('one');
63-
ngElement.removeClass('one');
128+
ngElement..setAttribute('id', 'foo')
129+
..setAttribute('id', 'foo')
130+
..removeAttribute('id');
64131

65-
expect(element.classes.contains('one')).toBe(false);
132+
expect(element).not.toHaveAttribute('id');
66133

67134
scope.apply();
68135

69-
expect(element.classes.contains('one')).toBe(false);
136+
expect(element).not.toHaveAttribute('id');
137+
138+
ngElement..removeAttribute('id')
139+
..setAttribute('id', 'foobar')
140+
..setAttribute('id', 'foo');
70141

71-
element.classes.add('one');
142+
scope.apply();
72143

73-
ngElement.removeClass('one');
74-
ngElement.removeClass('one');
75-
ngElement.addClass('one');
144+
expect(element).toHaveAttribute('id', 'foo');
76145

77-
expect(element.classes.contains('one')).toBe(true);
78146
});
79147
});
80148
}

0 commit comments

Comments
 (0)