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

fix(select): update option if interpolated value attribute changes #12582

Closed
wants to merge 1 commit into from
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
38 changes: 28 additions & 10 deletions src/ng/directive/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,12 @@ var optionDirective = ['$interpolate', function($interpolate) {
priority: 100,
compile: function(element, attr) {

// If the value attribute is not defined then we fall back to the
// text content of the option element, which may be interpolated
if (isUndefined(attr.value)) {
if (isDefined(attr.value)) {
// If the value attribute is defined, check if it contains an interpolation
var valueInterpolated = $interpolate(attr.value, true);
} else {
// If the value attribute is not defined then we fall back to the
// text content of the option element, which may be interpolated
var interpolateFn = $interpolate(element.text(), true);
if (!interpolateFn) {
attr.$set('value', element.text());
Expand All @@ -288,24 +291,39 @@ var optionDirective = ['$interpolate', function($interpolate) {
selectCtrl = parent.data(selectCtrlName) ||
parent.parent().data(selectCtrlName); // in case we are in optgroup

function addOption(optionValue) {
selectCtrl.addOption(optionValue, element);
selectCtrl.ngModelCtrl.$render();
chromeHack(element);
}

// Only update trigger option updates if this is an option within a `select`
// that also has `ngModel` attached
if (selectCtrl && selectCtrl.ngModelCtrl) {

if (interpolateFn) {
if (valueInterpolated) {
// The value attribute is interpolated
var oldVal;
attr.$observe('value', function valueAttributeObserveAction(newVal) {
if (isDefined(oldVal)) {
selectCtrl.removeOption(oldVal);
}
oldVal = newVal;
addOption(newVal);
});

} else if (interpolateFn) {
// The text content is interpolated
scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
attr.$set('value', newVal);
if (oldVal !== newVal) {
selectCtrl.removeOption(oldVal);
}
selectCtrl.addOption(newVal, element);
selectCtrl.ngModelCtrl.$render();
chromeHack(element);
addOption(newVal);
});
} else {
selectCtrl.addOption(attr.value, element);
selectCtrl.ngModelCtrl.$render();
chromeHack(element);
// The value attribute is static
addOption(attr.value);
}

element.on('$destroy', function() {
Expand Down
60 changes: 60 additions & 0 deletions test/ng/directive/selectSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,66 @@ describe('select', function() {
expect(element).toEqualSelect(['hello']);
});


it('should add options with interpolated value attributes',
inject(function($rootScope, $compile) {
var scope = $rootScope;

scope.option1 = 'option1';
scope.option2 = 'option2';

var element = $compile(
'<select ng-model="selected">' +
'<option value="{{option1}}">Option 1</option>' +
'<option value="{{option2}}">Option 2</option>' +
'</div>')(scope);

scope.$digest();
expect(scope.selected).toBeUndefined();

browserTrigger(element.find('option').eq(0));
expect(scope.selected).toBe('option1');

scope.selected = 'option2';
scope.$digest();
expect(element.find('option').eq(1).prop('selected')).toBe(true);
expect(element.find('option').eq(1).text()).toBe('Option 2');
})
);


it('should update the option when the interpolated value attribute changes',
inject(function($rootScope, $compile) {
var scope = $rootScope;

scope.option1 = 'option1';
scope.option2 = '';

var element = $compile(
'<select ng-model="selected">' +
'<option value="{{option1}}">Option 1</option>' +
'<option value="{{option2}}">Option 2</option>' +
'</div>')(scope);

var selectCtrl = element.controller('select');
spyOn(selectCtrl, 'removeOption').andCallThrough();

scope.$digest();
expect(scope.selected).toBeUndefined();
expect(selectCtrl.removeOption).not.toHaveBeenCalled();

//Change value of option2
scope.option2 = 'option2Changed';
scope.selected = 'option2Changed';
scope.$digest();

expect(selectCtrl.removeOption).toHaveBeenCalledWith('');
expect(element.find('option').eq(1).prop('selected')).toBe(true);
expect(element.find('option').eq(1).text()).toBe('Option 2');
})
);


it('should not blow up when option directive is found inside of a datalist',
inject(function($compile, $rootScope) {
var element = $compile('<div>' +
Expand Down