Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Select option fix with compiler with issue 599 #603

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/Compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ 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
// if child no longer available, delete path
delete paths[i];
}
},

Expand Down
34 changes: 23 additions & 11 deletions src/widget/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,13 @@ angularWidget('select', function(element){

// find existing special options
forEach(selectElement.children(), function(option){
if (option.value == '')
if (option.value == '') {
// User is allowed to select the null.
nullOption = {label:jqLite(option).text(), id:''};
// save <option> element
nullOption = jqLite(option).remove();
// compile it in model scope
compile(nullOption)(modelScope);
}
});
selectElement.html(''); // clear contents

Expand Down Expand Up @@ -315,7 +319,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;
}

Expand Down Expand Up @@ -385,18 +389,26 @@ angularWidget('select', function(element){
if (existingOption.id !== option.id) {
lastElement.val(existingOption.id = option.id);
}
if (existingOption.selected !== option.selected) {
if (existingOption.element.selected !== option.selected) {
lastElement.prop('selected', (existingOption.selected = option.selected));
}
} 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,
Expand Down
98 changes: 98 additions & 0 deletions test/widget/selectSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,104 @@ describe('select', function() {
expect(select.find('option').length).toEqual(1);
});
});

describe('blank option', function () {
// redefine createSelect for this test a bit
function createSelect(attrs, blank, unknown){
var html = '<select';
forEach(attrs, function(value, key){
if (isBoolean(value)) {
if (value) html += ' ' + key;
} else {
html += ' ' + key + '="' + value + '"';
}
});
html += '>' +
(blank ? blank : '') + // blank can be specified in details
(unknown ? unknown : '') +
'</select>';
select = jqLite(html);
scope = compile(select);
}

function createSingleSelect(blank, unknown){
createSelect({
'ng:model':'selected',
'ng:options':'value.name for value in values'
}, blank, unknown);
}

it('should be compiled as template, be watched and updated', function () {
var option;

createSingleSelect('<option value="">blank is {{blankTemplate}}</option>');
scope.blankTemplate = '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 blankTemplate and $digest
scope.blankTemplate = '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 be compiled from ng:bind-template attribute if given instead of text', function () {
var option;

createSingleSelect('<option value="" ng:bind-template="blank is {{blankTemplate}}"></option>');
scope.blankTemplate = '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 be compiled from ng:bind attribute if given', function () {
var option;

createSingleSelect('<option value="" ng:bind="blankTemplate"></option>');
scope.blankTemplate = '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">{{blankTemplate}}</option>');
scope.blankTemplate = '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() {
Expand Down