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

Commit 3692885

Browse files
TEHEKIgorMinar
authored andcommitted
fix(ng:options): compile null/blank option tag
Fixes #562
1 parent 5d43439 commit 3692885

File tree

3 files changed

+107
-15
lines changed

3 files changed

+107
-15
lines changed

src/Compiler.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ Template.prototype = {
3333
paths = this.paths,
3434
length = paths.length;
3535
for (i = 0; i < length; i++) {
36-
children[i].link(jqLite(childNodes[paths[i]]), childScope);
36+
// sometimes `element` can be modified by one of the linker functions in `this.linkFns`
37+
// and childNodes may be added or removed
38+
// TODO: element structure needs to be re-evaluated if new children added
39+
// if the childNode still exists
40+
if (childNodes[paths[i]])
41+
children[i].link(jqLite(childNodes[paths[i]]), childScope);
42+
else
43+
delete paths[i]; // if child no longer available, delete path
3744
}
3845
},
3946

src/widget/select.js

+23-12
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,13 @@ angularWidget('select', function(element){
241241
inChangeEvent;
242242

243243
// find existing special options
244-
forEach(selectElement.children(), function(option){
245-
if (option.value == '')
246-
// User is allowed to select the null.
247-
nullOption = {label:jqLite(option).text(), id:''};
244+
forEach(selectElement.children(), function(option) {
245+
if (option.value == '') {
246+
// developer declared null option, so user should be able to select it
247+
nullOption = jqLite(option).remove();
248+
// compile the element since there might be bindings in it
249+
compile(nullOption)(modelScope);
250+
}
248251
});
249252
selectElement.html(''); // clear contents
250253

@@ -314,7 +317,7 @@ angularWidget('select', function(element){
314317
selectedSet = new HashMap(modelValue);
315318
} else if (modelValue === null || nullOption) {
316319
// if we are not multiselect, and we are null then we have to add the nullOption
317-
optionGroups[''].push(extend({selected:modelValue === null, id:'', label:''}, nullOption));
320+
optionGroups[''].push({selected:modelValue === null, id:'', label:''});
318321
selectedSet = true;
319322
}
320323

@@ -389,13 +392,21 @@ angularWidget('select', function(element){
389392
}
390393
} else {
391394
// grow elements
392-
// jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
393-
// in this version of jQuery on some browser the .text() returns a string
394-
// rather then the element.
395-
(element = optionTemplate.clone())
396-
.val(option.id)
397-
.attr('selected', option.selected)
398-
.text(option.label);
395+
396+
// if it's a null option
397+
if (option.id === '' && nullOption) {
398+
// put back the pre-compiled element
399+
element = nullOption;
400+
} else {
401+
// jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
402+
// in this version of jQuery on some browser the .text() returns a string
403+
// rather then the element.
404+
(element = optionTemplate.clone())
405+
.val(option.id)
406+
.attr('selected', option.selected)
407+
.text(option.label);
408+
}
409+
399410
existingOptions.push(existingOption = {
400411
element: element,
401412
label: option.label,

test/widget/selectSpec.js

+76-2
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ describe('select', function() {
130130
}
131131
});
132132
html += '>' +
133-
(blank ? '<option value="">blank</option>' : '') +
134-
(unknown ? '<option value="?">unknown</option>' : '') +
133+
(blank ? (isString(blank) ? blank : '<option value="">blank</option>') : '') +
134+
(unknown ? (isString(unknown) ? unknown : '<option value="?">unknown</option>') : '') +
135135
'</select>';
136136
select = jqLite(html);
137137
scope = compile(select);
@@ -445,6 +445,80 @@ describe('select', function() {
445445
});
446446
});
447447

448+
449+
describe('blank option', function () {
450+
it('should be compiled as template, be watched and updated', function () {
451+
var option;
452+
453+
createSingleSelect('<option value="">blank is {{blankVal}}</option>');
454+
scope.blankVal = 'so blank';
455+
scope.values = [{name:'A'}];
456+
scope.$digest();
457+
458+
// check blank option is first and is compiled
459+
expect(select.find('option').length == 2);
460+
option = jqLite(select.find('option')[0]);
461+
expect(option.val()).toBe('');
462+
expect(option.text()).toBe('blank is so blank');
463+
464+
// change blankVal and $digest
465+
scope.blankVal = 'not so blank';
466+
scope.$digest();
467+
468+
// check blank option is first and is compiled
469+
expect(select.find('option').length == 2);
470+
option = jqLite(select.find('option')[0]);
471+
expect(option.val()).toBe('');
472+
expect(option.text()).toBe('blank is not so blank');
473+
});
474+
475+
it('should support binding via ng:bind-template attribute', function () {
476+
var option;
477+
478+
createSingleSelect('<option value="" ng:bind-template="blank is {{blankVal}}"></option>');
479+
scope.blankVal = 'so blank';
480+
scope.values = [{name:'A'}];
481+
scope.$digest();
482+
483+
// check blank option is first and is compiled
484+
expect(select.find('option').length == 2);
485+
option = jqLite(select.find('option')[0]);
486+
expect(option.val()).toBe('');
487+
expect(option.text()).toBe('blank is so blank');
488+
});
489+
490+
it('should support biding via ng:bind attribute', function () {
491+
var option;
492+
493+
createSingleSelect('<option value="" ng:bind="blankVal"></option>');
494+
scope.blankVal = 'is blank';
495+
scope.values = [{name:'A'}];
496+
scope.$digest();
497+
498+
// check blank option is first and is compiled
499+
expect(select.find('option').length == 2);
500+
option = jqLite(select.find('option')[0]);
501+
expect(option.val()).toBe('');
502+
expect(option.text()).toBe('is blank');
503+
});
504+
505+
it('should be rendered with the attributes preserved', function () {
506+
var option;
507+
508+
createSingleSelect('<option value="" class="coyote" id="road-runner" ' +
509+
'custom-attr="custom-attr">{{blankVal}}</option>');
510+
scope.blankVal = 'is blank';
511+
scope.$digest();
512+
513+
// check blank option is first and is compiled
514+
option = jqLite(select.find('option')[0]);
515+
expect(option.hasClass('coyote')).toBeTruthy();
516+
expect(option.attr('id')).toBe('road-runner');
517+
expect(option.attr('custom-attr')).toBe('custom-attr');
518+
});
519+
});
520+
521+
448522
describe('on change', function() {
449523
it('should update model on change', function() {
450524
createSingleSelect();

0 commit comments

Comments
 (0)