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(); | 
