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

Commit c71b8cf

Browse files
committed
fix(mustache): fix regression that fired an initial empty string
Closes #734
1 parent 61c953d commit c71b8cf

File tree

3 files changed

+50
-17
lines changed

3 files changed

+50
-17
lines changed

lib/core_dom/directive.dart

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
part of angular.core.dom;
22

33
/// Callback function used to notify of attribute changes.
4-
typedef AttributeChanged(String newValue);
4+
typedef void AttributeChanged(String newValue);
55

66
/// Callback function used to notify of observer changes.
7-
typedef void ObserverChanged(bool hasListeners);
7+
typedef void Mustache(bool hasObservers);
88

99
/**
1010
* NodeAttrs is a facade for element attributes. The facade is responsible
@@ -16,13 +16,17 @@ class NodeAttrs {
1616

1717
Map<String, List<AttributeChanged>> _observers;
1818

19-
Map<String, List<ObserverChanged>> _observerListeners;
19+
Map<String, Mustache> _mustacheObservers = {};
20+
Set<String> _mustacheComputedAttrs = new Set<String>();
2021

2122
NodeAttrs(this.element);
2223

2324
operator [](String attributeName) => element.attributes[attributeName];
2425

2526
void operator []=(String attributeName, String value) {
27+
if (_mustacheObservers.containsKey(attributeName)) {
28+
_mustacheComputedAttrs.add(attributeName);
29+
}
2630
if (value == null) {
2731
element.attributes.remove(attributeName);
2832
} else {
@@ -43,11 +47,15 @@ class NodeAttrs {
4347
_observers.putIfAbsent(attributeName, () => <AttributeChanged>[])
4448
.add(notifyFn);
4549

46-
notifyFn(this[attributeName]);
50+
bool hasMustache = _mustacheObservers.containsKey(attributeName);
51+
bool hasMustacheAndIsComputed = _mustacheComputedAttrs.contains(attributeName);
52+
53+
if (!hasMustache || hasMustacheAndIsComputed) {
54+
notifyFn(this[attributeName]);
55+
}
4756

48-
if (_observerListeners != null &&
49-
_observerListeners.containsKey(attributeName)) {
50-
_observerListeners[attributeName].forEach((cb) => cb(true));
57+
if (_mustacheObservers.containsKey(attributeName)) {
58+
_mustacheObservers[attributeName](true);
5159
}
5260
}
5361

@@ -60,12 +68,8 @@ class NodeAttrs {
6068

6169
Iterable<String> get keys => element.attributes.keys;
6270

63-
void listenObserverChanges(String attributeName, ObserverChanged fn) {
64-
if (_observerListeners == null) {
65-
_observerListeners = <String, List<ObserverChanged>>{};
66-
}
67-
_observerListeners.putIfAbsent(attributeName, () => <ObserverChanged>[])
68-
.add(fn);
71+
void listenObserverChanges(String attributeName, Mustache fn) {
72+
_mustacheObservers[attributeName] = fn;
6973
fn(false);
7074
}
7175
}

test/core_dom/ng_mustache_spec.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ main() {
77
TestBed _;
88
beforeEachModule((Module module) {
99
module.type(_HelloFilter);
10+
module.type(_FooDirective);
1011
});
1112
beforeEach(inject((TestBed tb) => _ = tb));
1213

@@ -23,6 +24,23 @@ main() {
2324
expect(element.text()).toEqual('OK!');
2425
}));
2526

27+
describe('observe/flush phase', () {
28+
it('should first only when then value has settled', async((Logger log) {
29+
_.compile('<div dir-foo="{{val}}"></div>');
30+
31+
_.rootScope.apply();
32+
// _FooDirective should NOT have observed any changes.
33+
expect(log).toEqual([]);
34+
expect(_.rootElement.attributes['dir-foo']).toEqual('');
35+
36+
_.rootScope.apply(() {
37+
_.rootScope.context['val'] = 'value';
38+
});
39+
// _FooDirective should have observed exactly one change.
40+
expect(_.rootElement.attributes['dir-foo']).toEqual('value');
41+
expect(log).toEqual(['value']);
42+
}));
43+
});
2644

2745
it('should replace {{}} in attribute', inject((Compiler $compile, Scope rootScope, Injector injector, DirectiveMap directives) {
2846
var element = $('<div some-attr="{{name}}" other-attr="{{age}}"></div>');
@@ -117,3 +135,15 @@ class _HelloFilter {
117135
}
118136
}
119137

138+
@NgComponent(selector: '[dir-foo]')
139+
class _FooDirective implements NgAttachAware {
140+
NodeAttrs attrs;
141+
Logger log;
142+
143+
_FooDirective(this.attrs, this.log);
144+
145+
@override
146+
void attach() {
147+
attrs.observe('dir-foo', (val) => log(val));
148+
}
149+
}

test/directive/ng_src_boolean_spec.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,17 @@ main() {
9393
TestBed _;
9494
beforeEach((TestBed tb) => _ = tb);
9595

96-
it('should interpolate the expression and bind to src with raw same-domain value',
97-
inject(() {
96+
it('should interpolate the expression and bind to src with raw same-domain value', () {
9897
_.compile('<div ng-src="{{id}}"></div>');
9998

10099
_.rootScope.apply();
101-
expect(_.rootElement.attributes['src']).toEqual('');
100+
expect(_.rootElement.attributes['src']).toEqual(null);
102101

103102
_.rootScope.apply(() {
104103
_.rootScope.context['id'] = '/somewhere/here';
105104
});
106105
expect(_.rootElement.attributes['src']).toEqual('/somewhere/here');
107-
}));
106+
});
108107

109108

110109
xit('should interpolate the expression and bind to src with a trusted value', ($sce) {

0 commit comments

Comments
 (0)