aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTEHEK Firefox2011-10-11 22:37:00 +0000
committerIgor Minar2011-10-19 22:52:14 -0700
commit36928858104ba793dfc7372dbaa28048123d6e63 (patch)
treea797722f18f0da73316e5660f046f336c9e0c474
parent5d43439dbe764a4c7227f51b34a81b044f13901b (diff)
downloadangular.js-36928858104ba793dfc7372dbaa28048123d6e63.tar.bz2
fix(ng:options): compile null/blank option tag
Fixes #562
-rw-r--r--src/Compiler.js9
-rw-r--r--src/widget/select.js35
-rw-r--r--test/widget/selectSpec.js78
3 files changed, 107 insertions, 15 deletions
diff --git a/src/Compiler.js b/src/Compiler.js
index 23bce084..1b079fc2 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -33,7 +33,14 @@ Template.prototype = {
paths = this.paths,
length = paths.length;
for (i = 0; i < length; i++) {
- children[i].link(jqLite(childNodes[paths[i]]), childScope);
+ // sometimes `element` can be modified by one of the linker functions in `this.linkFns`
+ // and childNodes may be added or removed
+ // TODO: element structure needs to be re-evaluated if new children added
+ // if the childNode still exists
+ if (childNodes[paths[i]])
+ children[i].link(jqLite(childNodes[paths[i]]), childScope);
+ else
+ delete paths[i]; // if child no longer available, delete path
}
},
diff --git a/src/widget/select.js b/src/widget/select.js
index 2448d40c..ba340490 100644
--- a/src/widget/select.js
+++ b/src/widget/select.js
@@ -241,10 +241,13 @@ angularWidget('select', function(element){
inChangeEvent;
// find existing special options
- forEach(selectElement.children(), function(option){
- if (option.value == '')
- // User is allowed to select the null.
- nullOption = {label:jqLite(option).text(), id:''};
+ forEach(selectElement.children(), function(option) {
+ if (option.value == '') {
+ // developer declared null option, so user should be able to select it
+ nullOption = jqLite(option).remove();
+ // compile the element since there might be bindings in it
+ compile(nullOption)(modelScope);
+ }
});
selectElement.html(''); // clear contents
@@ -314,7 +317,7 @@ angularWidget('select', function(element){
selectedSet = new HashMap(modelValue);
} else if (modelValue === null || nullOption) {
// if we are not multiselect, and we are null then we have to add the nullOption
- optionGroups[''].push(extend({selected:modelValue === null, id:'', label:''}, nullOption));
+ optionGroups[''].push({selected:modelValue === null, id:'', label:''});
selectedSet = true;
}
@@ -389,13 +392,21 @@ angularWidget('select', function(element){
}
} else {
// grow elements
- // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
- // in this version of jQuery on some browser the .text() returns a string
- // rather then the element.
- (element = optionTemplate.clone())
- .val(option.id)
- .attr('selected', option.selected)
- .text(option.label);
+
+ // if it's a null option
+ if (option.id === '' && nullOption) {
+ // put back the pre-compiled element
+ element = nullOption;
+ } else {
+ // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
+ // in this version of jQuery on some browser the .text() returns a string
+ // rather then the element.
+ (element = optionTemplate.clone())
+ .val(option.id)
+ .attr('selected', option.selected)
+ .text(option.label);
+ }
+
existingOptions.push(existingOption = {
element: element,
label: option.label,
diff --git a/test/widget/selectSpec.js b/test/widget/selectSpec.js
index 63699d01..c3fdc2e6 100644
--- a/test/widget/selectSpec.js
+++ b/test/widget/selectSpec.js
@@ -130,8 +130,8 @@ describe('select', function() {
}
});
html += '>' +
- (blank ? '<option value="">blank</option>' : '') +
- (unknown ? '<option value="?">unknown</option>' : '') +
+ (blank ? (isString(blank) ? blank : '<option value="">blank</option>') : '') +
+ (unknown ? (isString(unknown) ? unknown : '<option value="?">unknown</option>') : '') +
'</select>';
select = jqLite(html);
scope = compile(select);
@@ -445,6 +445,80 @@ describe('select', function() {
});
});
+
+ describe('blank option', function () {
+ it('should be compiled as template, be watched and updated', function () {
+ var option;
+
+ createSingleSelect('<option value="">blank is {{blankVal}}</option>');
+ scope.blankVal = 'so blank';
+ scope.values = [{name:'A'}];
+ scope.$digest();
+
+ // check blank option is first and is compiled
+ expect(select.find('option').length == 2);
+ option = jqLite(select.find('option')[0]);
+ expect(option.val()).toBe('');
+ expect(option.text()).toBe('blank is so blank');
+
+ // change blankVal and $digest
+ scope.blankVal = 'not so blank';
+ scope.$digest();
+
+ // check blank option is first and is compiled
+ expect(select.find('option').length == 2);
+ option = jqLite(select.find('option')[0]);
+ expect(option.val()).toBe('');
+ expect(option.text()).toBe('blank is not so blank');
+ });
+
+ it('should support binding via ng:bind-template attribute', function () {
+ var option;
+
+ createSingleSelect('<option value="" ng:bind-template="blank is {{blankVal}}"></option>');
+ scope.blankVal = 'so blank';
+ scope.values = [{name:'A'}];
+ scope.$digest();
+
+ // check blank option is first and is compiled
+ expect(select.find('option').length == 2);
+ option = jqLite(select.find('option')[0]);
+ expect(option.val()).toBe('');
+ expect(option.text()).toBe('blank is so blank');
+ });
+
+ it('should support biding via ng:bind attribute', function () {
+ var option;
+
+ createSingleSelect('<option value="" ng:bind="blankVal"></option>');
+ scope.blankVal = 'is blank';
+ scope.values = [{name:'A'}];
+ scope.$digest();
+
+ // check blank option is first and is compiled
+ expect(select.find('option').length == 2);
+ option = jqLite(select.find('option')[0]);
+ expect(option.val()).toBe('');
+ expect(option.text()).toBe('is blank');
+ });
+
+ it('should be rendered with the attributes preserved', function () {
+ var option;
+
+ createSingleSelect('<option value="" class="coyote" id="road-runner" ' +
+ 'custom-attr="custom-attr">{{blankVal}}</option>');
+ scope.blankVal = 'is blank';
+ scope.$digest();
+
+ // check blank option is first and is compiled
+ option = jqLite(select.find('option')[0]);
+ expect(option.hasClass('coyote')).toBeTruthy();
+ expect(option.attr('id')).toBe('road-runner');
+ expect(option.attr('custom-attr')).toBe('custom-attr');
+ });
+ });
+
+
describe('on change', function() {
it('should update model on change', function() {
createSingleSelect();