Skip to content

Commit aee241f

Browse files
authored
Merge pull request angular-ui#1712 from user378230/fix-is-disabled
Fix isDisabled and prevent modifying items
2 parents e179dc6 + c01d363 commit aee241f

File tree

3 files changed

+126
-24
lines changed

3 files changed

+126
-24
lines changed

src/uiSelectController.js

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -324,18 +324,44 @@ uis.controller('uiSelectCtrl',
324324
ctrl.selected.filter(function (selection) { return angular.equals(selection, item); }).length > 0);
325325
};
326326

327+
var disabledItems = [];
328+
329+
function _updateItemDisabled(item, isDisabled) {
330+
var disabledItemIndex = disabledItems.indexOf(item);
331+
if (isDisabled && disabledItemIndex === -1) {
332+
disabledItems.push(item);
333+
}
334+
335+
if (!isDisabled && disabledItemIndex > -1) {
336+
disabledItems.splice(disabledItemIndex, 0);
337+
}
338+
}
339+
340+
function _isItemDisabled(item) {
341+
return disabledItems.indexOf(item) > -1;
342+
}
343+
327344
ctrl.isDisabled = function(itemScope) {
328345

329346
if (!ctrl.open) return;
330347

331-
var itemIndex = ctrl.items.indexOf(itemScope[ctrl.itemProperty]);
348+
var item = itemScope[ctrl.itemProperty];
349+
var itemIndex = ctrl.items.indexOf(item);
332350
var isDisabled = false;
333-
var item;
351+
352+
if (itemIndex >= 0 && (angular.isDefined(ctrl.disableChoiceExpression) || ctrl.multiple)) {
353+
354+
if (item.isTag) return false;
355+
356+
if (ctrl.multiple) {
357+
isDisabled = _isItemSelected(item);
358+
}
334359

335-
if (itemIndex >= 0 && (!angular.isUndefined(ctrl.disableChoiceExpression) || ctrl.multiple)) {
336-
item = ctrl.items[itemIndex];
337-
isDisabled = !!(itemScope.$eval(ctrl.disableChoiceExpression)) || _isItemSelected(item); // force the boolean value
338-
item._uiSelectChoiceDisabled = isDisabled; // store this for later reference
360+
if (!isDisabled && angular.isDefined(ctrl.disableChoiceExpression)) {
361+
isDisabled = !!(itemScope.$eval(ctrl.disableChoiceExpression));
362+
}
363+
364+
_updateItemDisabled(item, isDisabled);
339365
}
340366

341367
return isDisabled;
@@ -344,11 +370,11 @@ uis.controller('uiSelectCtrl',
344370

345371
// When the user selects an item with ENTER or clicks the dropdown
346372
ctrl.select = function(item, skipFocusser, $event) {
347-
if (item === undefined || !item._uiSelectChoiceDisabled) {
373+
if (item === undefined || !_isItemDisabled(item)) {
348374

349375
if ( ! ctrl.items && ! ctrl.search && ! ctrl.tagging.isActivated) return;
350376

351-
if (!item || !item._uiSelectChoiceDisabled) {
377+
if (!item || !_isItemDisabled(item)) {
352378
if(ctrl.tagging.isActivated) {
353379
// if taggingLabel is disabled and item is undefined we pull from ctrl.search
354380
if ( ctrl.taggingLabel === false ) {
@@ -446,16 +472,53 @@ uis.controller('uiSelectCtrl',
446472
}
447473
};
448474

449-
ctrl.isLocked = function(itemScope, itemIndex) {
450-
var isLocked, item = ctrl.selected[itemIndex];
475+
// Set default function for locked choices - avoids unnecessary
476+
// logic if functionality is not being used
477+
ctrl.isLocked = function () {
478+
return false;
479+
};
480+
481+
$scope.$watch(function () {
482+
return angular.isDefined(ctrl.lockChoiceExpression) && ctrl.lockChoiceExpression !== "";
483+
}, _initaliseLockedChoices);
451484

452-
if (item && !angular.isUndefined(ctrl.lockChoiceExpression)) {
453-
isLocked = !!(itemScope.$eval(ctrl.lockChoiceExpression)); // force the boolean value
454-
item._uiSelectChoiceLocked = isLocked; // store this for later reference
485+
function _initaliseLockedChoices(doInitalise) {
486+
if(!doInitalise) return;
487+
488+
var lockedItems = [];
489+
490+
function _updateItemLocked(item, isLocked) {
491+
var lockedItemIndex = lockedItems.indexOf(item);
492+
if (isLocked && lockedItemIndex === -1) {
493+
lockedItems.push(item);
494+
}
495+
496+
if (!isLocked && lockedItemIndex > -1) {
497+
lockedItems.splice(lockedItemIndex, 0);
498+
}
499+
}
500+
501+
function _isItemlocked(item) {
502+
return lockedItems.indexOf(item) > -1;
503+
}
504+
505+
ctrl.isLocked = function (itemScope, itemIndex) {
506+
var isLocked = false,
507+
item = ctrl.selected[itemIndex];
508+
509+
if(item) {
510+
if (itemScope) {
511+
isLocked = !!(itemScope.$eval(ctrl.lockChoiceExpression));
512+
_updateItemLocked(item, isLocked);
513+
} else {
514+
isLocked = _isItemlocked(item);
515+
}
455516
}
456517

457518
return isLocked;
458-
};
519+
};
520+
}
521+
459522

460523
var sizeWatch = null;
461524
var updaterScheduled = false;

src/uiSelectMultipleDirective.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
3737
// Remove item from multiple select
3838
ctrl.removeChoice = function(index){
3939

40-
var removedChoice = $select.selected[index];
40+
// if the choice is locked, don't remove it
41+
if($select.isLocked(null, index)) return false;
4142

42-
// if the choice is locked, can't remove it
43-
if(removedChoice._uiSelectChoiceLocked) return;
43+
var removedChoice = $select.selected[index];
4444

4545
var locals = {};
4646
locals[$select.parserResult.itemName] = removedChoice;
@@ -59,6 +59,7 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
5959

6060
ctrl.updateModel();
6161

62+
return true;
6263
};
6364

6465
ctrl.getPlaceholder = function(){
@@ -246,11 +247,16 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
246247
case KEY.BACKSPACE:
247248
// Remove selected item and select previous/first
248249
if(~$selectMultiple.activeMatchIndex){
249-
$selectMultiple.removeChoice(curr);
250-
return prev;
251-
}
252-
// Select last item
253-
else return last;
250+
if($selectMultiple.removeChoice(curr)) {
251+
return prev;
252+
} else {
253+
return curr;
254+
}
255+
256+
} else {
257+
// If nothing yet selected, select last item
258+
return last;
259+
}
254260
break;
255261
case KEY.DELETE:
256262
// Remove selected item and select next item

test/select.spec.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,7 +1745,8 @@ describe('ui-select tests', function() {
17451745

17461746
function createUiSelectMultiple(attrs) {
17471747
var attrsHtml = '',
1748-
choicesAttrsHtml = '';
1748+
choicesAttrsHtml = '',
1749+
matchesAttrsHtml = '';
17491750
if (attrs !== undefined) {
17501751
if (attrs.disabled !== undefined) { attrsHtml += ' ng-disabled="' + attrs.disabled + '"'; }
17511752
if (attrs.required !== undefined) { attrsHtml += ' ng-required="' + attrs.required + '"'; }
@@ -1756,11 +1757,12 @@ describe('ui-select tests', function() {
17561757
if (attrs.taggingLabel !== undefined) { attrsHtml += ' tagging-label="' + attrs.taggingLabel + '"'; }
17571758
if (attrs.inputId !== undefined) { attrsHtml += ' input-id="' + attrs.inputId + '"'; }
17581759
if (attrs.groupBy !== undefined) { choicesAttrsHtml += ' group-by="' + attrs.groupBy + '"'; }
1760+
if (attrs.lockChoice !== undefined) { matchesAttrsHtml += ' ui-lock-choice="' + attrs.lockChoice + '"'; }
17591761
}
17601762

17611763
return compileTemplate(
17621764
'<ui-select multiple ng-model="selection.selectedMultiple"' + attrsHtml + ' theme="bootstrap" style="width: 800px;"> \
1763-
<ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
1765+
<ui-select-match "' + matchesAttrsHtml + ' placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
17641766
<ui-select-choices repeat="person in people | filter: $select.search"' + choicesAttrsHtml + '> \
17651767
<div ng-bind-html="person.name | highlight: $select.search"></div> \
17661768
<div ng-bind-html="person.email | highlight: $select.search"></div> \
@@ -1922,6 +1924,37 @@ describe('ui-select tests', function() {
19221924

19231925
});
19241926

1927+
it('should NOT remove highlighted match when pressing BACKSPACE key on a locked choice', function() {
1928+
1929+
scope.selection.selectedMultiple = [scope.people[4], scope.people[5], scope.people[6]]; //Wladimir, Samantha & Nicole
1930+
var el = createUiSelectMultiple({lockChoice: "$item.name == '" + scope.people[6].name + "'"});
1931+
var searchInput = el.find('.ui-select-search');
1932+
1933+
expect(isDropdownOpened(el)).toEqual(false);
1934+
triggerKeydown(searchInput, Key.Left);
1935+
triggerKeydown(searchInput, Key.Backspace);
1936+
expect(el.scope().$select.selected).toEqual([scope.people[4], scope.people[5], scope.people[6]]); //Wladimir, Samantha & Nicole
1937+
1938+
expect(el.scope().$selectMultiple.activeMatchIndex).toBe(scope.selection.selectedMultiple.length - 1);
1939+
1940+
});
1941+
1942+
it('should NOT remove highlighted match when pressing DELETE key on a locked choice', function() {
1943+
1944+
scope.selection.selectedMultiple = [scope.people[4], scope.people[5], scope.people[6]]; //Wladimir, Samantha & Nicole
1945+
var el = createUiSelectMultiple({lockChoice: "$item.name == '" + scope.people[6].name + "'"});
1946+
var searchInput = el.find('.ui-select-search');
1947+
1948+
expect(isDropdownOpened(el)).toEqual(false);
1949+
triggerKeydown(searchInput, Key.Left);
1950+
triggerKeydown(searchInput, Key.Delete);
1951+
expect(el.scope().$select.selected).toEqual([scope.people[4], scope.people[5], scope.people[6]]); //Wladimir, Samantha & Nicole
1952+
1953+
expect(el.scope().$selectMultiple.activeMatchIndex).toBe(scope.selection.selectedMultiple.length - 1);
1954+
1955+
});
1956+
1957+
19251958
it('should move to last match when pressing LEFT key from search', function() {
19261959

19271960
var el = createUiSelectMultiple();

0 commit comments

Comments
 (0)