diff options
| author | TEHEK Firefox | 2011-10-11 22:37:00 +0000 |
|---|---|---|
| committer | Igor Minar | 2011-10-19 22:52:14 -0700 |
| commit | 36928858104ba793dfc7372dbaa28048123d6e63 (patch) | |
| tree | a797722f18f0da73316e5660f046f336c9e0c474 | |
| parent | 5d43439dbe764a4c7227f51b34a81b044f13901b (diff) | |
| download | angular.js-36928858104ba793dfc7372dbaa28048123d6e63.tar.bz2 | |
fix(ng:options): compile null/blank option tag
Fixes #562
| -rw-r--r-- | src/Compiler.js | 9 | ||||
| -rw-r--r-- | src/widget/select.js | 35 | ||||
| -rw-r--r-- | test/widget/selectSpec.js | 78 |
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(); |
