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

Commit b23268f

Browse files
mvuksanochirayuk
authored andcommitted
feat(NodeAttrs): Node attributes don't get initialized by NodeAttrs when
setting up observing. In case an element does not have a value set for a propertyv, that property is not automatically initialised. For example: <div test foo="bar"> will cause ElementBinder to register observer (which will in turn cause initialisation) on property foo (of the directive) to bar, but if we try using <div test> foo property will not be initialised. Closes #1001
1 parent 20defb2 commit b23268f

File tree

7 files changed

+69
-18
lines changed

7 files changed

+69
-18
lines changed

lib/core_dom/directive.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ class NodeAttrs {
5757
if (_mustacheAttrs[attrName].isComputed) notifyFn(this[attrName]);
5858
_mustacheAttrs[attrName].notifyFn(true);
5959
} else {
60-
notifyFn(this[attrName]);
60+
if (element.attributes.containsKey(attrName)) {
61+
var value = element.attributes[attrName];
62+
notifyFn(this[attrName]);
63+
}
6164
}
6265
}
6366

lib/core_dom/element_binder.dart

+7-3
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ class ElementBinder {
104104
dstPathFn.assign(controller, _parser(expression).bind(scope.context, ScopeLocals.wrapper));
105105
}
106106

107-
108-
void _createAttrMappings(directive, scope, List<MappingParts> mappings, nodeAttrs, tasks) {
107+
void _createAttrMappings(directive, scope, DirectiveRef ref, nodeAttrs, tasks) {
109108
Scope directiveScope; // Only created if there is a two-way binding in the element.
109+
List<MappingParts> mappings = ref.mappings;
110110
for(var i = 0; i < mappings.length; i++) {
111111
MappingParts p = mappings[i];
112112
var attrName = p.attrName;
@@ -138,6 +138,10 @@ class ElementBinder {
138138
switch (p.mode) {
139139
case '@': // string
140140
var taskId = (tasks != null) ? tasks.registerTask() : 0;
141+
if (ref.element is dom.Element &&
142+
!(ref.element as dom.Element).attributes.containsKey(attrName)) {
143+
tasks.completeTask(taskId);
144+
}
141145
nodeAttrs.observe(attrName, (value) {
142146
dstAST.parsedExp.assign(directive, value);
143147
if (tasks != null) tasks.completeTask(taskId);
@@ -202,7 +206,7 @@ class ElementBinder {
202206

203207
if (ref.mappings.isNotEmpty) {
204208
if (nodeAttrs == null) nodeAttrs = new _AnchorAttrs(ref);
205-
_createAttrMappings(directive, scope, ref.mappings, nodeAttrs, tasks);
209+
_createAttrMappings(directive, scope, ref, nodeAttrs, tasks);
206210
}
207211

208212
if (directive is AttachAware) {

lib/directive/ng_model.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class NgModel extends NgControl implements AttachAware {
7070

7171
void attach() {
7272
watchCollection = false;
73+
_parentControl.addControl(this);
7374
}
7475

7576
/**
@@ -130,7 +131,6 @@ class NgModel extends NgControl implements AttachAware {
130131
String get name => _name;
131132
void set name(value) {
132133
_name = value;
133-
_parentControl.addControl(this);
134134
}
135135

136136
// TODO(misko): could we get rid of watch collection, and just always watch the collection?

lib/directive/ng_model_select.dart

+23-9
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,33 @@ class InputSelect implements AttachAware {
4242
}
4343

4444
attach() {
45+
singleSelectMode() {
46+
_model.watchCollection = false;
47+
_mode = new _SingleSelectMode(expando, _selectElement, _model, _nullOption, _unknownOption);
48+
_scope.rootScope.domRead(() {
49+
_mode.onModelChange(_model.viewValue);
50+
});
51+
}
52+
53+
multiSelectMode() {
54+
_model.watchCollection = true;
55+
_mode = new _MultipleSelectionMode(expando, _selectElement, _model);
56+
_scope.rootScope.domRead(() {
57+
_mode.onModelChange(_model.viewValue);
58+
});
59+
}
60+
61+
if (!_selectElement.attributes.containsKey('multiple')) {
62+
singleSelectMode();
63+
}
64+
4565
_attrs.observe('multiple', (value) {
4666
_mode.destroy();
47-
if (value == null) {
48-
_model.watchCollection = false;
49-
_mode = new _SingleSelectMode(expando, _selectElement, _model,
50-
_nullOption, _unknownOption);
67+
if (value == null || value != '') {
68+
multiSelectMode();
5169
} else {
52-
_model.watchCollection = true;
53-
_mode = new _MultipleSelectionMode(expando, _selectElement, _model);
70+
singleSelectMode();
5471
}
55-
_scope.rootScope.domRead(() {
56-
_mode.onModelChange(_model.viewValue);
57-
});
5872
});
5973

6074
_selectElement.onChange.listen((event) => _mode.onViewChange(event));

lib/directive/ng_model_validators.dart

+1
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ class NgModelMinLengthValidator implements NgValidator {
280280

281281
NgModelMinLengthValidator(NgModel this._ngModel) {
282282
_ngModel.addValidator(this);
283+
_minlength = 0;
283284
}
284285

285286
bool isValid(modelValue) {

test/core_dom/directive_spec.dart

+31-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import '../_specs.dart';
55
main() {
66
describe('NodeAttrs', () {
77
var element;
8-
var nodeAttrs;
8+
NodeAttrs nodeAttrs;
99
TestBed _;
1010

1111
beforeEach((TestBed tb) {
1212
_ = tb;
13-
element = _.compile('<div foo="bar" foo-bar="baz" foo-bar-baz="foo"></div>');
13+
element = _.compile('<div foo="bar" foo-bar="baz" foo-bar-baz="foo" cux></div>');
1414
nodeAttrs = new NodeAttrs(element);
1515
});
1616

@@ -27,7 +27,7 @@ main() {
2727
it('should provide a forEach function to iterate over attributes', () {
2828
Map<String, String> attrMap = new Map();
2929
nodeAttrs.forEach((k, v) => attrMap[k] = v);
30-
expect(attrMap).toEqual({'foo': 'bar', 'foo-bar': 'baz', 'foo-bar-baz': 'foo'});
30+
expect(attrMap).toEqual({'foo': 'bar', 'foo-bar': 'baz', 'foo-bar-baz': 'foo', 'cux': ''});
3131
});
3232

3333
it('should provide a contains method', () {
@@ -38,7 +38,34 @@ main() {
3838
});
3939

4040
it('should return the attribute names', () {
41-
expect(nodeAttrs.keys.toList()..sort()).toEqual(['foo', 'foo-bar', 'foo-bar-baz']);
41+
expect(nodeAttrs.keys.toList()..sort()).toEqual(['cux', 'foo', 'foo-bar', 'foo-bar-baz']);
42+
});
43+
44+
it('should not call function with argument set to null when observing a'
45+
' property', () {
46+
var invoked;
47+
nodeAttrs.observe("a", (arg) {
48+
invoked = true;
49+
});
50+
expect(invoked).toBeFalsy();
51+
});
52+
53+
it('should call function when argument is set when observing a property',
54+
() {
55+
var seenValue = '';
56+
nodeAttrs.observe("foo", (arg) {
57+
seenValue = arg;
58+
});
59+
expect(seenValue).toEqual('bar');
60+
});
61+
62+
it('should call function with argument set to \'\' when observing a boolean attribute',
63+
() {
64+
var seenValue;
65+
nodeAttrs.observe("cux", (arg) {
66+
seenValue = arg;
67+
});
68+
expect(seenValue).toEqual('');
4269
});
4370
});
4471
}

test/directive/ng_form_spec.dart

+2
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,8 @@ void main() {
710710
Probe probe = s.context['i'];
711711
var model = probe.directive(NgModel);
712712

713+
_.rootScope.apply();
714+
713715
expect(s.eval('name')).toEqual('cool');
714716
expect(s.eval('myForm.name')).toEqual('myForm');
715717
expect(s.eval('myForm["name"]')).toBe(model);

0 commit comments

Comments
 (0)