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

Commit a6749c2

Browse files
committed
handle all cases for multiple select
1 parent 6d29b21 commit a6749c2

File tree

3 files changed

+454
-106
lines changed

3 files changed

+454
-106
lines changed

src/ng/directive/select.js

Lines changed: 44 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ var SelectController =
1414
['$element', '$scope', function($element, $scope) {
1515

1616
var self = this,
17-
optionsMap = new HashMap();
17+
optionsMap = new HashMap(),
18+
handleMultipleDestroy = false; // Flag to run an update to the model after selected options
19+
// in a multiple select have been destroyed
1820

1921
self.selectValueMap = {}; // Keys are the hashed values, values the original values
2022

2123
// If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
2224
self.ngModelCtrl = noopNgModelController;
25+
self.multiple = false;
2326

2427
// The "unknown" option is one that is prepended to the list if the viewValue
2528
// does not match any of the options. When it is rendered the value of the unknown
@@ -52,10 +55,7 @@ var SelectController =
5255
// ngValue added option values are stored in the selectValueMap, normal interpolations are not
5356
var realVal = val in self.selectValueMap ? self.selectValueMap[val] : val;
5457

55-
console.log('read', 'elval', val, 'possiblyhashed', realVal)
5658
if (self.hasOption(realVal)) {
57-
console.log('has selected val', realVal)
58-
// self.removeUnknownOption();
5959
return realVal;
6060
}
6161

@@ -66,25 +66,15 @@ var SelectController =
6666
// Write the value to the select control, the implementation of this changes depending
6767
// upon whether the select can have multiple values and whether ngOptions is at work.
6868
self.writeValue = function writeSingleValue(value) {
69-
console.log('write', value);
7069
if (self.hasOption(value)) {
71-
console.log('hasOption', value);
7270
self.removeUnknownOption();
7371
var hashedVal = hashKey(value);
7472
if (hashedVal in self.selectValueMap) {
7573
$element.val(hashedVal);
7674
} else {
7775
$element.val(value);
7876
}
79-
console.log('after set', $element.val())
80-
// console.log('selectValueMap', self.selectValueMap)
81-
// var items = new HashMap();
82-
// items.put(value, value);
83-
// console.log(items, hashKey(value));
84-
85-
// if (hashKey(value) in self.selectValueMap) {
86-
// console.log('hashed')
87-
// }
77+
8878
if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
8979
} else {
9080
if (value == null && self.emptyOption) {
@@ -131,18 +121,32 @@ var SelectController =
131121
return !!optionsMap.get(value);
132122
};
133123

124+
var handleMultipleChanges = false;
125+
function updateModelAfterOptionChange(renderAfter) {
126+
if (self.multiple) {
127+
if (!handleMultipleChanges) {
128+
handleMultipleChanges = true;
129+
} else {
130+
$scope.$$postDigest(function() {
131+
handleMultipleChanges = false;
132+
self.ngModelCtrl.$setViewValue(self.readValue());
133+
if (renderAfter) self.ngModelCtrl.$render();
134+
});
135+
}
136+
} else {
137+
self.ngModelCtrl.$setViewValue(self.readValue());
138+
}
139+
}
140+
134141

135142
self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
136143

137-
// console.log('attr', optionAttrs)
138144
if (optionAttrs.$attr.ngValue) {
139145
// The value attribute is set by ngValue
140146
var oldVal, hashedVal = NaN;
141147
optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
142-
console.log('ngValue change', 'n', newVal, 'o', oldVal, 'hashed', hashedVal)
143148

144149
var removal;
145-
console.log('val', $element.val());
146150
var previouslySelected = optionElement.prop('selected');
147151

148152
if (isDefined(hashedVal)) {
@@ -155,24 +159,19 @@ var SelectController =
155159
oldVal = newVal;
156160
self.selectValueMap[hashedVal] = newVal;
157161
self.addOption(newVal, optionElement);
158-
console.log('val', $element.val());
159162
// Set the attribute directly instead of using optionAttrs.$set - this stops the observer
160163
// from firing a second time. Other $observers on value will also get the result of the
161164
// ngValue expression, not the hashed value
162165
optionElement.attr('value', hashedVal);
163166

164-
console.log('previouslySelected', previouslySelected, 'removal', removal)
165-
166167
if (removal && previouslySelected) {
167-
console.log('removed val is currently selected', $element.val())
168-
self.ngModelCtrl.$setViewValue(self.readValue());
169-
}
168+
updateModelAfterOptionChange();
169+
}
170170

171171
});
172172
} else if (interpolateValueFn) {
173173
// The value attribute is interpolated
174174
optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
175-
// console.log('value attribute changed', 'viewVal', self.ngModelCtrl.$viewValue, 'index', optionElement[0].index, 'indices', $element[0].selectedIndex, $element[0].selectedOptions)
176175
var currentVal = self.readValue();
177176
var removal;
178177
var previouslySelected = optionElement.prop('selected');
@@ -186,11 +185,9 @@ var SelectController =
186185
oldVal = newVal;
187186
self.addOption(newVal, optionElement);
188187

189-
console.log('updated interpolated value', 'new', newVal, 'removed', removedVal, 'current', currentVal);
190188
if (removal && previouslySelected) {
191-
console.log('removed val is currently selected', $element.val())
192-
self.ngModelCtrl.$setViewValue(self.readValue());
193-
}
189+
updateModelAfterOptionChange();
190+
}
194191
});
195192
} else if (interpolateTextFn) {
196193
// The text content is interpolated
@@ -203,7 +200,7 @@ var SelectController =
203200
self.addOption(newVal, optionElement);
204201

205202
if (oldVal && previouslySelected) {
206-
self.ngModelCtrl.$setViewValue(self.readValue());
203+
updateModelAfterOptionChange();
207204
}
208205
});
209206
} else {
@@ -219,39 +216,33 @@ var SelectController =
219216
// we only have to handle options becomeing disabled, not enabled
220217

221218
if (newVal === 'true' || newVal && optionElement.prop('selected')) {
222-
console.log('disabled')
223-
self.ngModelCtrl.$setViewValue(null);
224-
self.ngModelCtrl.$render();
219+
220+
if (self.multiple) {
221+
updateModelAfterOptionChange(true);
222+
} else {
223+
self.ngModelCtrl.$setViewValue(null);
224+
self.ngModelCtrl.$render();
225+
}
225226
oldDisabled = newVal;
226-
} else if (isDefined(oldDisabled) && !newVal || newVal === 'false') {
227-
// var val = optionAttrs.value;
228-
// console.log('OA', optionAttrs.value);
229-
// var realVal = val in self.selectValueMap ? self.selectValueMap[val] : val;
230-
// console.log('back from disabled', val, realVal, self.ngModelCtrl.$viewValue);
231-
232-
// if (realVal === self.ngModelCtrl.$viewValue) {
233-
// self.writeValue(realVal);
234-
// self.ngModelCtrl.$setViewValue(self.readValue());
235-
// }
236227
}
237228
});
238229

239230
optionElement.on('$destroy', function() {
240231
var currentValue = self.readValue();
241232
var removeValue = optionAttrs.value;
242233

243-
console.log('destroy', removeValue, 'elval', $element.val())
244-
// console.log('viewValue', self.ngModelCtrl.$viewValue)
245234
self.removeOption(removeValue);
246235
self.ngModelCtrl.$render();
247236

248-
if (currentValue === removeValue) {
249-
// console.log('removed val is currently selected', $element.val())
250-
// console.log('self.readValue()', self.readValue());
237+
if (self.multiple && currentValue && currentValue.indexOf(removeValue) !== -1) {
238+
// When multiple (selected) options are destroyed at the same time, we don't want
239+
// to run a model update for each of them. Instead, run a single update in the $$postDigest
240+
// NOTE: Will that interfere with the regular model update? No - $setViewValue always triggers a digest
241+
// However, it might not work if someones removes an option outside of a digest
242+
updateModelAfterOptionChange();
243+
} else if (currentValue === removeValue) {
251244
self.ngModelCtrl.$setViewValue(self.readValue());
252-
253245
}
254-
// console.log('read after render', self.readValue())
255246
});
256247
};
257248
}];
@@ -487,7 +478,6 @@ var selectDirective = function() {
487478
// to the `readValue` method, which can be changed if the select can have multiple
488479
// selected values or if the options are being generated by `ngOptions`
489480
element.on('change', function() {
490-
console.log('on change', element.val())
491481
selectCtrl.removeUnknownOption();
492482
scope.$apply(function() {
493483
ngModelCtrl.$setViewValue(selectCtrl.readValue());
@@ -499,12 +489,13 @@ var selectDirective = function() {
499489
// we have to add an extra watch since ngModel doesn't work well with arrays - it
500490
// doesn't trigger rendering if only an item in the array changes.
501491
if (attr.multiple) {
492+
selectCtrl.multiple = true;
502493

503494
// Read value now needs to check each option to see if it is selected
504495
selectCtrl.readValue = function readMultipleValue() {
505496
var array = [];
506497
forEach(element.find('option'), function(option) {
507-
if (option.selected) {
498+
if (option.selected && !option.disabled) {
508499
var val = option.value;
509500
array.push(val in selectCtrl.selectValueMap ? selectCtrl.selectValueMap[val] : val);
510501
}
@@ -570,6 +561,7 @@ var optionDirective = ['$interpolate', function($interpolate) {
570561
var interpolateValueFn, interpolateTextFn;
571562

572563
if (isDefined(attr.ngValue)) {
564+
// jshint noempty: false
573565
// Will be handled by registerOption
574566
} else if (isDefined(attr.value)) {
575567
// If the value attribute is defined, check if it contains an interpolation

src/ngMock/browserTrigger.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
evnt = window.document.createEvent('MouseEvents');
9696
x = x || 0;
9797
y = y || 0;
98+
// console.log('trigger', element.relatedTarget, eventType, pressed('ctrl'))
9899
evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
99100
pressed('alt'), pressed('shift'), pressed('meta'), 0, relatedTarget);
100101
}

0 commit comments

Comments
 (0)