From 4c5ed51669c93d60c2760cbb2f97075f09c56a55 Mon Sep 17 00:00:00 2001 From: Wladimir Coka Date: Fri, 6 Mar 2015 23:46:16 -0500 Subject: [PATCH 1/4] feat(focus): allow to set focus when loaded or listening a scope event --- src/uiSelectController.js | 4 + src/uiSelectDirective.js | 568 +++++++++++++++++++------------------- src/uisFocusDirectives.js | 25 ++ 3 files changed, 318 insertions(+), 279 deletions(-) create mode 100644 src/uisFocusDirectives.js diff --git a/src/uiSelectController.js b/src/uiSelectController.js index 00c55ddce..0aae010a4 100644 --- a/src/uiSelectController.js +++ b/src/uiSelectController.js @@ -309,6 +309,10 @@ uis.controller('uiSelectCtrl', } }; + ctrl.setFocus = function(){ + if (!ctrl.focus) ctrl.focusser[0].focus(); + }; + ctrl.clear = function($event) { ctrl.select(undefined); $event.stopPropagation(); diff --git a/src/uiSelectDirective.js b/src/uiSelectDirective.js index 92d2a3885..e3dd756c9 100644 --- a/src/uiSelectDirective.js +++ b/src/uiSelectDirective.js @@ -15,342 +15,352 @@ uis.directive('uiSelect', controller: 'uiSelectCtrl', controllerAs: '$select', + compile: function(tElement, tAttrs) { - link: function(scope, element, attrs, ctrls, transcludeFn) { - var $select = ctrls[0]; - var ngModel = ctrls[1]; + //autofocus attribute to uisAutofocusAttr directive + if (angular.isDefined(tElement.attr('autofocus'))) + tElement.append("").removeAttr('autofocus'); - var searchInput = element.querySelectorAll('input.ui-select-search'); + //focusOn attribute to uisFocusOnAttr directive + if (angular.isDefined(tElement.attr('focus-on'))) + tElement.append("
").removeAttr('focus-on'); - $select.generatedId = uiSelectConfig.generateId(); - $select.baseTitle = attrs.title || 'Select box'; - $select.focusserTitle = $select.baseTitle + ' focus'; - $select.focusserId = 'focusser-' + $select.generatedId; + return function(scope, element, attrs, ctrls, transcludeFn) { + var $select = ctrls[0]; + var ngModel = ctrls[1]; - $select.multiple = angular.isDefined(attrs.multiple) && ( - attrs.multiple === '' || - attrs.multiple.toLowerCase() === 'multiple' || - attrs.multiple.toLowerCase() === 'true' - ); + var searchInput = element.querySelectorAll('input.ui-select-search'); - $select.closeOnSelect = function() { - if (angular.isDefined(attrs.closeOnSelect)) { - return $parse(attrs.closeOnSelect)(); - } else { - return uiSelectConfig.closeOnSelect; - } - }(); + $select.generatedId = uiSelectConfig.generateId(); + $select.baseTitle = attrs.title || 'Select box'; + $select.focusserTitle = $select.baseTitle + ' focus'; + $select.focusserId = 'focusser-' + $select.generatedId; - $select.onSelectCallback = $parse(attrs.onSelect); - $select.onRemoveCallback = $parse(attrs.onRemove); + $select.multiple = angular.isDefined(attrs.multiple) && ( + attrs.multiple === '' || + attrs.multiple.toLowerCase() === 'multiple' || + attrs.multiple.toLowerCase() === 'true' + ); - //From view --> model - ngModel.$parsers.unshift(function (inputValue) { - var locals = {}, - result; - if ($select.multiple){ - var resultMultiple = []; - for (var j = $select.selected.length - 1; j >= 0; j--) { - locals = {}; - locals[$select.parserResult.itemName] = $select.selected[j]; - result = $select.parserResult.modelMapper(scope, locals); - resultMultiple.unshift(result); + $select.closeOnSelect = function() { + if (angular.isDefined(attrs.closeOnSelect)) { + return $parse(attrs.closeOnSelect)(); + } else { + return uiSelectConfig.closeOnSelect; } - return resultMultiple; - }else{ - locals = {}; - locals[$select.parserResult.itemName] = inputValue; - result = $select.parserResult.modelMapper(scope, locals); - return result; - } - }); - - //From model --> view - ngModel.$formatters.unshift(function (inputValue) { - var data = $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search - locals = {}, - result; - if (data){ + }(); + + $select.onSelectCallback = $parse(attrs.onSelect); + $select.onRemoveCallback = $parse(attrs.onRemove); + + //From view --> model + ngModel.$parsers.unshift(function (inputValue) { + var locals = {}, + result; if ($select.multiple){ var resultMultiple = []; - var checkFnMultiple = function(list, value){ - //if the list is empty add the value to the list - if (!list || !list.length){ - resultMultiple.unshift(value); - return true; - } - for (var p = list.length - 1; p >= 0; p--) { - locals[$select.parserResult.itemName] = list[p]; - result = $select.parserResult.modelMapper(scope, locals); - if($select.parserResult.trackByExp){ - var matches = /\.(.+)/.exec($select.parserResult.trackByExp); - if(matches.length>0 && result[matches[1]] == value[matches[1]]){ - resultMultiple.unshift(list[p]); - return true; - } + for (var j = $select.selected.length - 1; j >= 0; j--) { + locals = {}; + locals[$select.parserResult.itemName] = $select.selected[j]; + result = $select.parserResult.modelMapper(scope, locals); + resultMultiple.unshift(result); + } + return resultMultiple; + }else{ + locals = {}; + locals[$select.parserResult.itemName] = inputValue; + result = $select.parserResult.modelMapper(scope, locals); + return result; + } + }); + + //From model --> view + ngModel.$formatters.unshift(function (inputValue) { + var data = $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search + locals = {}, + result; + if (data){ + if ($select.multiple){ + var resultMultiple = []; + var checkFnMultiple = function(list, value){ + //if the list is empty add the value to the list + if (!list || !list.length){ + resultMultiple.unshift(value); + return true; } - if (result == value){ - resultMultiple.unshift(list[p]); - return true; + for (var p = list.length - 1; p >= 0; p--) { + locals[$select.parserResult.itemName] = list[p]; + result = $select.parserResult.modelMapper(scope, locals); + if($select.parserResult.trackByExp){ + var matches = /\.(.+)/.exec($select.parserResult.trackByExp); + if(matches.length>0 && result[matches[1]] == value[matches[1]]){ + resultMultiple.unshift(list[p]); + return true; + } + } + if (result == value){ + resultMultiple.unshift(list[p]); + return true; + } + } + return false; + }; + if (!inputValue) return resultMultiple; //If ngModel was undefined + for (var k = inputValue.length - 1; k >= 0; k--) { + if (!checkFnMultiple($select.selected, inputValue[k])){ + checkFnMultiple(data, inputValue[k]); } } - return false; - }; - if (!inputValue) return resultMultiple; //If ngModel was undefined - for (var k = inputValue.length - 1; k >= 0; k--) { - if (!checkFnMultiple($select.selected, inputValue[k])){ - checkFnMultiple(data, inputValue[k]); + return resultMultiple; + }else{ + var checkFnSingle = function(d){ + locals[$select.parserResult.itemName] = d; + result = $select.parserResult.modelMapper(scope, locals); + return result == inputValue; + }; + //If possible pass same object stored in $select.selected + if ($select.selected && checkFnSingle($select.selected)) { + return $select.selected; + } + for (var i = data.length - 1; i >= 0; i--) { + if (checkFnSingle(data[i])) return data[i]; } - } - return resultMultiple; - }else{ - var checkFnSingle = function(d){ - locals[$select.parserResult.itemName] = d; - result = $select.parserResult.modelMapper(scope, locals); - return result == inputValue; - }; - //If possible pass same object stored in $select.selected - if ($select.selected && checkFnSingle($select.selected)) { - return $select.selected; - } - for (var i = data.length - 1; i >= 0; i--) { - if (checkFnSingle(data[i])) return data[i]; } } - } - return inputValue; - }); + return inputValue; + }); - //Set reference to ngModel from uiSelectCtrl - $select.ngModel = ngModel; + //Set reference to ngModel from uiSelectCtrl + $select.ngModel = ngModel; - $select.choiceGrouped = function(group){ - return $select.isGrouped && group && group.name; - }; + $select.choiceGrouped = function(group){ + return $select.isGrouped && group && group.name; + }; - //Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954 - var focusser = angular.element(""); + //Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954 + var focusser = angular.element(""); - if(attrs.tabindex){ - //tabindex might be an expression, wait until it contains the actual value before we set the focusser tabindex - attrs.$observe('tabindex', function(value) { - //If we are using multiple, add tabindex to the search input - if($select.multiple){ - searchInput.attr("tabindex", value); - } else { - focusser.attr("tabindex", value); - } - //Remove the tabindex on the parent so that it is not focusable - element.removeAttr("tabindex"); - }); - } + if(attrs.tabindex){ + //tabindex might be an expression, wait until it contains the actual value before we set the focusser tabindex + attrs.$observe('tabindex', function(value) { + //If we are using multiple, add tabindex to the search input + if($select.multiple){ + searchInput.attr("tabindex", value); + } else { + focusser.attr("tabindex", value); + } + //Remove the tabindex on the parent so that it is not focusable + element.removeAttr("tabindex"); + }); + } - $compile(focusser)(scope); - $select.focusser = focusser; + $compile(focusser)(scope); + $select.focusser = focusser; - if (!$select.multiple){ + if (!$select.multiple){ - element.append(focusser); - focusser.bind("focus", function(){ - scope.$evalAsync(function(){ - $select.focus = true; + element.append(focusser); + focusser.bind("focus", function(){ + scope.$evalAsync(function(){ + $select.focus = true; + }); }); - }); - focusser.bind("blur", function(){ - scope.$evalAsync(function(){ - $select.focus = false; + focusser.bind("blur", function(){ + scope.$evalAsync(function(){ + $select.focus = false; + }); }); - }); - focusser.bind("keydown", function(e){ - - if (e.which === KEY.BACKSPACE) { - e.preventDefault(); - e.stopPropagation(); - $select.select(undefined); - scope.$apply(); - return; - } + focusser.bind("keydown", function(e){ + + if (e.which === KEY.BACKSPACE) { + e.preventDefault(); + e.stopPropagation(); + $select.select(undefined); + scope.$apply(); + return; + } - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { - return; - } + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { + return; + } - if (e.which == KEY.DOWN || e.which == KEY.UP || e.which == KEY.ENTER || e.which == KEY.SPACE){ - e.preventDefault(); - e.stopPropagation(); - $select.activate(); - } + if (e.which == KEY.DOWN || e.which == KEY.UP || e.which == KEY.ENTER || e.which == KEY.SPACE){ + e.preventDefault(); + e.stopPropagation(); + $select.activate(); + } - scope.$digest(); - }); + scope.$digest(); + }); - focusser.bind("keyup input", function(e){ + focusser.bind("keyup input", function(e){ - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || e.which == KEY.ENTER || e.which === KEY.BACKSPACE) { - return; - } + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || e.which == KEY.ENTER || e.which === KEY.BACKSPACE) { + return; + } - $select.activate(focusser.val()); //User pressed some regular key, so we pass it to the search input - focusser.val(''); - scope.$digest(); + $select.activate(focusser.val()); //User pressed some regular key, so we pass it to the search input + focusser.val(''); + scope.$digest(); - }); - - } + }); + } - scope.$watch('searchEnabled', function() { - var searchEnabled = scope.$eval(attrs.searchEnabled); - $select.searchEnabled = searchEnabled !== undefined ? searchEnabled : uiSelectConfig.searchEnabled; - }); - scope.$watch('sortable', function() { - var sortable = scope.$eval(attrs.sortable); - $select.sortable = sortable !== undefined ? sortable : uiSelectConfig.sortable; - }); + scope.$watch('searchEnabled', function() { + var searchEnabled = scope.$eval(attrs.searchEnabled); + $select.searchEnabled = searchEnabled !== undefined ? searchEnabled : uiSelectConfig.searchEnabled; + }); - attrs.$observe('disabled', function() { - // No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string - $select.disabled = attrs.disabled !== undefined ? attrs.disabled : false; - // As the search input field may now become visible, it may be necessary to recompute its size - $select.sizeSearchInput(); - }); + scope.$watch('sortable', function() { + var sortable = scope.$eval(attrs.sortable); + $select.sortable = sortable !== undefined ? sortable : uiSelectConfig.sortable; + }); - attrs.$observe('resetSearchInput', function() { - // $eval() is needed otherwise we get a string instead of a boolean - var resetSearchInput = scope.$eval(attrs.resetSearchInput); - $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; - }); + attrs.$observe('disabled', function() { + // No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string + $select.disabled = attrs.disabled !== undefined ? attrs.disabled : false; + // As the search input field may now become visible, it may be necessary to recompute its size + $select.sizeSearchInput(); + }); - attrs.$observe('tagging', function() { - if(attrs.tagging !== undefined) - { + attrs.$observe('resetSearchInput', function() { // $eval() is needed otherwise we get a string instead of a boolean - var taggingEval = scope.$eval(attrs.tagging); - $select.tagging = {isActivated: true, fct: taggingEval !== true ? taggingEval : undefined}; - } - else - { - $select.tagging = {isActivated: false, fct: undefined}; - } - }); - - attrs.$observe('taggingLabel', function() { - if(attrs.tagging !== undefined ) - { - // check eval for FALSE, in this case, we disable the labels - // associated with tagging - if ( attrs.taggingLabel === 'false' ) { - $select.taggingLabel = false; + var resetSearchInput = scope.$eval(attrs.resetSearchInput); + $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; + }); + + attrs.$observe('tagging', function() { + if(attrs.tagging !== undefined) + { + // $eval() is needed otherwise we get a string instead of a boolean + var taggingEval = scope.$eval(attrs.tagging); + $select.tagging = {isActivated: true, fct: taggingEval !== true ? taggingEval : undefined}; } else { - $select.taggingLabel = attrs.taggingLabel !== undefined ? attrs.taggingLabel : '(new)'; + $select.tagging = {isActivated: false, fct: undefined}; } - } - }); - - attrs.$observe('taggingTokens', function() { - if (attrs.tagging !== undefined) { - var tokens = attrs.taggingTokens !== undefined ? attrs.taggingTokens.split('|') : [',','ENTER']; - $select.taggingTokens = {isActivated: true, tokens: tokens }; - } - }); - - if ($select.multiple){ - scope.$watchCollection(function(){ return ngModel.$modelValue; }, function(newValue, oldValue) { - if (oldValue != newValue) - ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters }); - $select.firstPass = true; // so the form doesn't get dirty as soon as it loads - scope.$watchCollection('$select.selected', function() { - if (!$select.firstPass) { - ngModel.$setViewValue(Date.now()); //Set timestamp as a unique string to force changes - } else { - $select.firstPass = false; + + attrs.$observe('taggingLabel', function() { + if(attrs.tagging !== undefined ) + { + // check eval for FALSE, in this case, we disable the labels + // associated with tagging + if ( attrs.taggingLabel === 'false' ) { + $select.taggingLabel = false; + } + else + { + $select.taggingLabel = attrs.taggingLabel !== undefined ? attrs.taggingLabel : '(new)'; + } } }); - focusser.prop('disabled', true); //Focusser isn't needed if multiple - }else{ - scope.$watch('$select.selected', function(newValue) { - if (ngModel.$viewValue !== newValue) { - ngModel.$setViewValue(newValue); + + attrs.$observe('taggingTokens', function() { + if (attrs.tagging !== undefined) { + var tokens = attrs.taggingTokens !== undefined ? attrs.taggingTokens.split('|') : [',','ENTER']; + $select.taggingTokens = {isActivated: true, tokens: tokens }; } }); - } - - ngModel.$render = function() { - if($select.multiple){ - // Make sure that model value is array - if(!angular.isArray(ngModel.$viewValue)){ - // Have tolerance for null or undefined values - if(angular.isUndefined(ngModel.$viewValue) || ngModel.$viewValue === null){ - $select.selected = []; + + if ($select.multiple){ + scope.$watchCollection(function(){ return ngModel.$modelValue; }, function(newValue, oldValue) { + if (oldValue != newValue) + ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters + }); + $select.firstPass = true; // so the form doesn't get dirty as soon as it loads + scope.$watchCollection('$select.selected', function() { + if (!$select.firstPass) { + ngModel.$setViewValue(Date.now()); //Set timestamp as a unique string to force changes } else { - throw uiSelectMinErr('multiarr', "Expected model value to be array but got '{0}'", ngModel.$viewValue); + $select.firstPass = false; } - } + }); + focusser.prop('disabled', true); //Focusser isn't needed if multiple + }else{ + scope.$watch('$select.selected', function(newValue) { + if (ngModel.$viewValue !== newValue) { + ngModel.$setViewValue(newValue); + } + }); } - $select.selected = ngModel.$viewValue; - }; - function onDocumentClick(e) { - if (!$select.open) return; //Skip it if dropdown is close + ngModel.$render = function() { + if($select.multiple){ + // Make sure that model value is array + if(!angular.isArray(ngModel.$viewValue)){ + // Have tolerance for null or undefined values + if(angular.isUndefined(ngModel.$viewValue) || ngModel.$viewValue === null){ + $select.selected = []; + } else { + throw uiSelectMinErr('multiarr', "Expected model value to be array but got '{0}'", ngModel.$viewValue); + } + } + } + $select.selected = ngModel.$viewValue; + }; - var contains = false; + function onDocumentClick(e) { + if (!$select.open) return; //Skip it if dropdown is close - if (window.jQuery) { - // Firefox 3.6 does not support element.contains() - // See Node.contains https://developer.mozilla.org/en-US/docs/Web/API/Node.contains - contains = window.jQuery.contains(element[0], e.target); - } else { - contains = element[0].contains(e.target); - } + var contains = false; - if (!contains && !$select.clickTriggeredSelect) { - //Will lose focus only with certain targets - var focusableControls = ['input','button','textarea']; - var targetScope = angular.element(e.target).scope(); //To check if target is other ui-select - var skipFocusser = targetScope && targetScope.$select && targetScope.$select !== $select; //To check if target is other ui-select - if (!skipFocusser) skipFocusser = ~focusableControls.indexOf(e.target.tagName.toLowerCase()); //Check if target is input, button or textarea - $select.close(skipFocusser); - scope.$digest(); - } - $select.clickTriggeredSelect = false; - } - - // See Click everywhere but here event http://stackoverflow.com/questions/12931369 - $document.on('click', onDocumentClick); - - scope.$on('$destroy', function() { - $document.off('click', onDocumentClick); - }); - - // Move transcluded elements to their correct position in main template - transcludeFn(scope, function(clone) { - // See Transclude in AngularJS http://blog.omkarpatil.com/2012/11/transclude-in-angularjs.html - - // One day jqLite will be replaced by jQuery and we will be able to write: - // var transcludedElement = clone.filter('.my-class') - // instead of creating a hackish DOM element: - var transcluded = angular.element('
').append(clone); - - var transcludedMatch = transcluded.querySelectorAll('.ui-select-match'); - transcludedMatch.removeAttr('ui-select-match'); //To avoid loop in case directive as attr - transcludedMatch.removeAttr('data-ui-select-match'); // Properly handle HTML5 data-attributes - if (transcludedMatch.length !== 1) { - throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-match but got '{0}'.", transcludedMatch.length); - } - element.querySelectorAll('.ui-select-match').replaceWith(transcludedMatch); + if (window.jQuery) { + // Firefox 3.6 does not support element.contains() + // See Node.contains https://developer.mozilla.org/en-US/docs/Web/API/Node.contains + contains = window.jQuery.contains(element[0], e.target); + } else { + contains = element[0].contains(e.target); + } - var transcludedChoices = transcluded.querySelectorAll('.ui-select-choices'); - transcludedChoices.removeAttr('ui-select-choices'); //To avoid loop in case directive as attr - transcludedChoices.removeAttr('data-ui-select-choices'); // Properly handle HTML5 data-attributes - if (transcludedChoices.length !== 1) { - throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-choices but got '{0}'.", transcludedChoices.length); + if (!contains && !$select.clickTriggeredSelect) { + //Will lose focus only with certain targets + var focusableControls = ['input','button','textarea']; + var targetScope = angular.element(e.target).scope(); //To check if target is other ui-select + var skipFocusser = targetScope && targetScope.$select && targetScope.$select !== $select; //To check if target is other ui-select + if (!skipFocusser) skipFocusser = ~focusableControls.indexOf(e.target.tagName.toLowerCase()); //Check if target is input, button or textarea + $select.close(skipFocusser); + scope.$digest(); + } + $select.clickTriggeredSelect = false; } - element.querySelectorAll('.ui-select-choices').replaceWith(transcludedChoices); - }); + + // See Click everywhere but here event http://stackoverflow.com/questions/12931369 + $document.on('click', onDocumentClick); + + scope.$on('$destroy', function() { + $document.off('click', onDocumentClick); + }); + + // Move transcluded elements to their correct position in main template + transcludeFn(scope, function(clone) { + // See Transclude in AngularJS http://blog.omkarpatil.com/2012/11/transclude-in-angularjs.html + + // One day jqLite will be replaced by jQuery and we will be able to write: + // var transcludedElement = clone.filter('.my-class') + // instead of creating a hackish DOM element: + var transcluded = angular.element('
').append(clone); + + var transcludedMatch = transcluded.querySelectorAll('.ui-select-match'); + transcludedMatch.removeAttr('ui-select-match'); //To avoid loop in case directive as attr + transcludedMatch.removeAttr('data-ui-select-match'); // Properly handle HTML5 data-attributes + if (transcludedMatch.length !== 1) { + throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-match but got '{0}'.", transcludedMatch.length); + } + element.querySelectorAll('.ui-select-match').replaceWith(transcludedMatch); + + var transcludedChoices = transcluded.querySelectorAll('.ui-select-choices'); + transcludedChoices.removeAttr('ui-select-choices'); //To avoid loop in case directive as attr + transcludedChoices.removeAttr('data-ui-select-choices'); // Properly handle HTML5 data-attributes + if (transcludedChoices.length !== 1) { + throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-choices but got '{0}'.", transcludedChoices.length); + } + element.querySelectorAll('.ui-select-choices').replaceWith(transcludedChoices); + }); + }; } }; }]); diff --git a/src/uisFocusDirectives.js b/src/uisFocusDirectives.js new file mode 100644 index 000000000..793f90a9f --- /dev/null +++ b/src/uisFocusDirectives.js @@ -0,0 +1,25 @@ +uis.directive('uisAutofocusAttr', ['$timeout', function($timeout) { + return { + restrict: 'EA', + require: '^uiSelect', + link: function(scope, element, attrs, $select) { + $timeout(function(){ + $select.setFocus(); + }); + } + }; +}]); + +uis.directive('uisFocusOnAttr', ['$timeout', function($timeout) { + return { + restrict: 'A', + require: '^uiSelect', + link: function(scope, element, attrs, $select) { + scope.$on(attrs.uisFocusOnAttr, function() { + $timeout(function(){ + $select.setFocus(); + }); + }); + } + }; +}]); From 1009cb143eecc0c1e9822e4023041396cc490d5a Mon Sep 17 00:00:00 2001 From: Wladimir Coka Date: Sun, 8 Mar 2015 11:44:41 -0500 Subject: [PATCH 2/4] Focus for multiselect mode --- src/uiSelectController.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/uiSelectController.js b/src/uiSelectController.js index 0aae010a4..40e2f5486 100644 --- a/src/uiSelectController.js +++ b/src/uiSelectController.js @@ -310,7 +310,8 @@ uis.controller('uiSelectCtrl', }; ctrl.setFocus = function(){ - if (!ctrl.focus) ctrl.focusser[0].focus(); + if (!ctrl.focus && !ctrl.multiple) ctrl.focusser[0].focus(); + if (!ctrl.focus && ctrl.multiple) _searchInput[0].focus(); }; ctrl.clear = function($event) { From 7e0dae35164cb9d32cddaac391c9dad9d38d8138 Mon Sep 17 00:00:00 2001 From: Wladimir Coka Date: Sun, 8 Mar 2015 19:31:30 -0500 Subject: [PATCH 3/4] Refactor focus support from directives to uiSelectDirective link fn --- src/uiSelectDirective.js | 595 +++++++++++++++++++------------------- src/uisFocusDirectives.js | 25 -- 2 files changed, 300 insertions(+), 320 deletions(-) delete mode 100644 src/uisFocusDirectives.js diff --git a/src/uiSelectDirective.js b/src/uiSelectDirective.js index e3dd756c9..8bd5369bf 100644 --- a/src/uiSelectDirective.js +++ b/src/uiSelectDirective.js @@ -1,6 +1,6 @@ uis.directive('uiSelect', - ['$document', 'uiSelectConfig', 'uiSelectMinErr', '$compile', '$parse', - function($document, uiSelectConfig, uiSelectMinErr, $compile, $parse) { + ['$document', 'uiSelectConfig', 'uiSelectMinErr', '$compile', '$parse', '$timeout', + function($document, uiSelectConfig, uiSelectMinErr, $compile, $parse, $timeout) { return { restrict: 'EA', @@ -15,352 +15,357 @@ uis.directive('uiSelect', controller: 'uiSelectCtrl', controllerAs: '$select', - compile: function(tElement, tAttrs) { - - //autofocus attribute to uisAutofocusAttr directive - if (angular.isDefined(tElement.attr('autofocus'))) - tElement.append("").removeAttr('autofocus'); - - //focusOn attribute to uisFocusOnAttr directive - if (angular.isDefined(tElement.attr('focus-on'))) - tElement.append("
").removeAttr('focus-on'); - - return function(scope, element, attrs, ctrls, transcludeFn) { - var $select = ctrls[0]; - var ngModel = ctrls[1]; - - var searchInput = element.querySelectorAll('input.ui-select-search'); - - $select.generatedId = uiSelectConfig.generateId(); - $select.baseTitle = attrs.title || 'Select box'; - $select.focusserTitle = $select.baseTitle + ' focus'; - $select.focusserId = 'focusser-' + $select.generatedId; - - $select.multiple = angular.isDefined(attrs.multiple) && ( - attrs.multiple === '' || - attrs.multiple.toLowerCase() === 'multiple' || - attrs.multiple.toLowerCase() === 'true' - ); - - $select.closeOnSelect = function() { - if (angular.isDefined(attrs.closeOnSelect)) { - return $parse(attrs.closeOnSelect)(); - } else { - return uiSelectConfig.closeOnSelect; - } - }(); + link: function(scope, element, attrs, ctrls, transcludeFn) { + var $select = ctrls[0]; + var ngModel = ctrls[1]; + + var searchInput = element.querySelectorAll('input.ui-select-search'); + + $select.generatedId = uiSelectConfig.generateId(); + $select.baseTitle = attrs.title || 'Select box'; + $select.focusserTitle = $select.baseTitle + ' focus'; + $select.focusserId = 'focusser-' + $select.generatedId; + + $select.multiple = angular.isDefined(attrs.multiple) && ( + attrs.multiple === '' || + attrs.multiple.toLowerCase() === 'multiple' || + attrs.multiple.toLowerCase() === 'true' + ); + + $select.closeOnSelect = function() { + if (angular.isDefined(attrs.closeOnSelect)) { + return $parse(attrs.closeOnSelect)(); + } else { + return uiSelectConfig.closeOnSelect; + } + }(); - $select.onSelectCallback = $parse(attrs.onSelect); - $select.onRemoveCallback = $parse(attrs.onRemove); + $select.onSelectCallback = $parse(attrs.onSelect); + $select.onRemoveCallback = $parse(attrs.onRemove); - //From view --> model - ngModel.$parsers.unshift(function (inputValue) { - var locals = {}, - result; - if ($select.multiple){ - var resultMultiple = []; - for (var j = $select.selected.length - 1; j >= 0; j--) { - locals = {}; - locals[$select.parserResult.itemName] = $select.selected[j]; - result = $select.parserResult.modelMapper(scope, locals); - resultMultiple.unshift(result); - } - return resultMultiple; - }else{ + //From view --> model + ngModel.$parsers.unshift(function (inputValue) { + var locals = {}, + result; + if ($select.multiple){ + var resultMultiple = []; + for (var j = $select.selected.length - 1; j >= 0; j--) { locals = {}; - locals[$select.parserResult.itemName] = inputValue; + locals[$select.parserResult.itemName] = $select.selected[j]; result = $select.parserResult.modelMapper(scope, locals); - return result; + resultMultiple.unshift(result); } - }); - - //From model --> view - ngModel.$formatters.unshift(function (inputValue) { - var data = $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search - locals = {}, - result; - if (data){ - if ($select.multiple){ - var resultMultiple = []; - var checkFnMultiple = function(list, value){ - //if the list is empty add the value to the list - if (!list || !list.length){ - resultMultiple.unshift(value); - return true; - } - for (var p = list.length - 1; p >= 0; p--) { - locals[$select.parserResult.itemName] = list[p]; - result = $select.parserResult.modelMapper(scope, locals); - if($select.parserResult.trackByExp){ - var matches = /\.(.+)/.exec($select.parserResult.trackByExp); - if(matches.length>0 && result[matches[1]] == value[matches[1]]){ - resultMultiple.unshift(list[p]); - return true; - } - } - if (result == value){ - resultMultiple.unshift(list[p]); - return true; - } - } - return false; - }; - if (!inputValue) return resultMultiple; //If ngModel was undefined - for (var k = inputValue.length - 1; k >= 0; k--) { - if (!checkFnMultiple($select.selected, inputValue[k])){ - checkFnMultiple(data, inputValue[k]); - } + return resultMultiple; + }else{ + locals = {}; + locals[$select.parserResult.itemName] = inputValue; + result = $select.parserResult.modelMapper(scope, locals); + return result; + } + }); + + //From model --> view + ngModel.$formatters.unshift(function (inputValue) { + var data = $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search + locals = {}, + result; + if (data){ + if ($select.multiple){ + var resultMultiple = []; + var checkFnMultiple = function(list, value){ + //if the list is empty add the value to the list + if (!list || !list.length){ + resultMultiple.unshift(value); + return true; } - return resultMultiple; - }else{ - var checkFnSingle = function(d){ - locals[$select.parserResult.itemName] = d; + for (var p = list.length - 1; p >= 0; p--) { + locals[$select.parserResult.itemName] = list[p]; result = $select.parserResult.modelMapper(scope, locals); - return result == inputValue; - }; - //If possible pass same object stored in $select.selected - if ($select.selected && checkFnSingle($select.selected)) { - return $select.selected; + if($select.parserResult.trackByExp){ + var matches = /\.(.+)/.exec($select.parserResult.trackByExp); + if(matches.length>0 && result[matches[1]] == value[matches[1]]){ + resultMultiple.unshift(list[p]); + return true; + } + } + if (result == value){ + resultMultiple.unshift(list[p]); + return true; + } } - for (var i = data.length - 1; i >= 0; i--) { - if (checkFnSingle(data[i])) return data[i]; + return false; + }; + if (!inputValue) return resultMultiple; //If ngModel was undefined + for (var k = inputValue.length - 1; k >= 0; k--) { + if (!checkFnMultiple($select.selected, inputValue[k])){ + checkFnMultiple(data, inputValue[k]); } } + return resultMultiple; + }else{ + var checkFnSingle = function(d){ + locals[$select.parserResult.itemName] = d; + result = $select.parserResult.modelMapper(scope, locals); + return result == inputValue; + }; + //If possible pass same object stored in $select.selected + if ($select.selected && checkFnSingle($select.selected)) { + return $select.selected; + } + for (var i = data.length - 1; i >= 0; i--) { + if (checkFnSingle(data[i])) return data[i]; + } } - return inputValue; - }); + } + return inputValue; + }); - //Set reference to ngModel from uiSelectCtrl - $select.ngModel = ngModel; + //Set reference to ngModel from uiSelectCtrl + $select.ngModel = ngModel; - $select.choiceGrouped = function(group){ - return $select.isGrouped && group && group.name; - }; + $select.choiceGrouped = function(group){ + return $select.isGrouped && group && group.name; + }; - //Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954 - var focusser = angular.element(""); + //Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954 + var focusser = angular.element(""); - if(attrs.tabindex){ - //tabindex might be an expression, wait until it contains the actual value before we set the focusser tabindex - attrs.$observe('tabindex', function(value) { - //If we are using multiple, add tabindex to the search input - if($select.multiple){ - searchInput.attr("tabindex", value); - } else { - focusser.attr("tabindex", value); - } - //Remove the tabindex on the parent so that it is not focusable - element.removeAttr("tabindex"); - }); - } + if(attrs.tabindex){ + //tabindex might be an expression, wait until it contains the actual value before we set the focusser tabindex + attrs.$observe('tabindex', function(value) { + //If we are using multiple, add tabindex to the search input + if($select.multiple){ + searchInput.attr("tabindex", value); + } else { + focusser.attr("tabindex", value); + } + //Remove the tabindex on the parent so that it is not focusable + element.removeAttr("tabindex"); + }); + } - $compile(focusser)(scope); - $select.focusser = focusser; + $compile(focusser)(scope); + $select.focusser = focusser; - if (!$select.multiple){ + if (!$select.multiple){ - element.append(focusser); - focusser.bind("focus", function(){ - scope.$evalAsync(function(){ - $select.focus = true; - }); + element.append(focusser); + focusser.bind("focus", function(){ + scope.$evalAsync(function(){ + $select.focus = true; }); - focusser.bind("blur", function(){ - scope.$evalAsync(function(){ - $select.focus = false; - }); + }); + focusser.bind("blur", function(){ + scope.$evalAsync(function(){ + $select.focus = false; }); - focusser.bind("keydown", function(e){ - - if (e.which === KEY.BACKSPACE) { - e.preventDefault(); - e.stopPropagation(); - $select.select(undefined); - scope.$apply(); - return; - } + }); + focusser.bind("keydown", function(e){ + + if (e.which === KEY.BACKSPACE) { + e.preventDefault(); + e.stopPropagation(); + $select.select(undefined); + scope.$apply(); + return; + } - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { - return; - } + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { + return; + } - if (e.which == KEY.DOWN || e.which == KEY.UP || e.which == KEY.ENTER || e.which == KEY.SPACE){ - e.preventDefault(); - e.stopPropagation(); - $select.activate(); - } + if (e.which == KEY.DOWN || e.which == KEY.UP || e.which == KEY.ENTER || e.which == KEY.SPACE){ + e.preventDefault(); + e.stopPropagation(); + $select.activate(); + } - scope.$digest(); - }); + scope.$digest(); + }); - focusser.bind("keyup input", function(e){ + focusser.bind("keyup input", function(e){ - if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || e.which == KEY.ENTER || e.which === KEY.BACKSPACE) { - return; - } + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || e.which == KEY.ENTER || e.which === KEY.BACKSPACE) { + return; + } - $select.activate(focusser.val()); //User pressed some regular key, so we pass it to the search input - focusser.val(''); - scope.$digest(); + $select.activate(focusser.val()); //User pressed some regular key, so we pass it to the search input + focusser.val(''); + scope.$digest(); - }); + }); - } + } - scope.$watch('searchEnabled', function() { - var searchEnabled = scope.$eval(attrs.searchEnabled); - $select.searchEnabled = searchEnabled !== undefined ? searchEnabled : uiSelectConfig.searchEnabled; - }); + scope.$watch('searchEnabled', function() { + var searchEnabled = scope.$eval(attrs.searchEnabled); + $select.searchEnabled = searchEnabled !== undefined ? searchEnabled : uiSelectConfig.searchEnabled; + }); - scope.$watch('sortable', function() { - var sortable = scope.$eval(attrs.sortable); - $select.sortable = sortable !== undefined ? sortable : uiSelectConfig.sortable; - }); + scope.$watch('sortable', function() { + var sortable = scope.$eval(attrs.sortable); + $select.sortable = sortable !== undefined ? sortable : uiSelectConfig.sortable; + }); - attrs.$observe('disabled', function() { - // No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string - $select.disabled = attrs.disabled !== undefined ? attrs.disabled : false; - // As the search input field may now become visible, it may be necessary to recompute its size - $select.sizeSearchInput(); - }); + attrs.$observe('disabled', function() { + // No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string + $select.disabled = attrs.disabled !== undefined ? attrs.disabled : false; + // As the search input field may now become visible, it may be necessary to recompute its size + $select.sizeSearchInput(); + }); - attrs.$observe('resetSearchInput', function() { - // $eval() is needed otherwise we get a string instead of a boolean - var resetSearchInput = scope.$eval(attrs.resetSearchInput); - $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; - }); + attrs.$observe('resetSearchInput', function() { + // $eval() is needed otherwise we get a string instead of a boolean + var resetSearchInput = scope.$eval(attrs.resetSearchInput); + $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; + }); - attrs.$observe('tagging', function() { - if(attrs.tagging !== undefined) - { - // $eval() is needed otherwise we get a string instead of a boolean - var taggingEval = scope.$eval(attrs.tagging); - $select.tagging = {isActivated: true, fct: taggingEval !== true ? taggingEval : undefined}; + attrs.$observe('tagging', function() { + if(attrs.tagging !== undefined) + { + // $eval() is needed otherwise we get a string instead of a boolean + var taggingEval = scope.$eval(attrs.tagging); + $select.tagging = {isActivated: true, fct: taggingEval !== true ? taggingEval : undefined}; + } + else + { + $select.tagging = {isActivated: false, fct: undefined}; + } + }); + + attrs.$observe('taggingLabel', function() { + if(attrs.tagging !== undefined ) + { + // check eval for FALSE, in this case, we disable the labels + // associated with tagging + if ( attrs.taggingLabel === 'false' ) { + $select.taggingLabel = false; } else { - $select.tagging = {isActivated: false, fct: undefined}; + $select.taggingLabel = attrs.taggingLabel !== undefined ? attrs.taggingLabel : '(new)'; } + } + }); + + attrs.$observe('taggingTokens', function() { + if (attrs.tagging !== undefined) { + var tokens = attrs.taggingTokens !== undefined ? attrs.taggingTokens.split('|') : [',','ENTER']; + $select.taggingTokens = {isActivated: true, tokens: tokens }; + } + }); + + //Automatically gets focus when loaded + if (angular.isDefined(attrs.autofocus)){ + $timeout(function(){ + $select.setFocus(); }); + } - attrs.$observe('taggingLabel', function() { - if(attrs.tagging !== undefined ) - { - // check eval for FALSE, in this case, we disable the labels - // associated with tagging - if ( attrs.taggingLabel === 'false' ) { - $select.taggingLabel = false; - } - else - { - $select.taggingLabel = attrs.taggingLabel !== undefined ? attrs.taggingLabel : '(new)'; - } - } + //Gets focus based on scope event name (e.g. focus-on='SomeEventName') + if (angular.isDefined(attrs.focusOn)){ + scope.$on(attrs.focusOn, function() { + $timeout(function(){ + $select.setFocus(); + }); }); + } - attrs.$observe('taggingTokens', function() { - if (attrs.tagging !== undefined) { - var tokens = attrs.taggingTokens !== undefined ? attrs.taggingTokens.split('|') : [',','ENTER']; - $select.taggingTokens = {isActivated: true, tokens: tokens }; + if ($select.multiple){ + scope.$watchCollection(function(){ return ngModel.$modelValue; }, function(newValue, oldValue) { + if (oldValue != newValue) + ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters + }); + $select.firstPass = true; // so the form doesn't get dirty as soon as it loads + scope.$watchCollection('$select.selected', function() { + if (!$select.firstPass) { + ngModel.$setViewValue(Date.now()); //Set timestamp as a unique string to force changes + } else { + $select.firstPass = false; } }); - - if ($select.multiple){ - scope.$watchCollection(function(){ return ngModel.$modelValue; }, function(newValue, oldValue) { - if (oldValue != newValue) - ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters - }); - $select.firstPass = true; // so the form doesn't get dirty as soon as it loads - scope.$watchCollection('$select.selected', function() { - if (!$select.firstPass) { - ngModel.$setViewValue(Date.now()); //Set timestamp as a unique string to force changes + focusser.prop('disabled', true); //Focusser isn't needed if multiple + }else{ + scope.$watch('$select.selected', function(newValue) { + if (ngModel.$viewValue !== newValue) { + ngModel.$setViewValue(newValue); + } + }); + } + + ngModel.$render = function() { + if($select.multiple){ + // Make sure that model value is array + if(!angular.isArray(ngModel.$viewValue)){ + // Have tolerance for null or undefined values + if(angular.isUndefined(ngModel.$viewValue) || ngModel.$viewValue === null){ + $select.selected = []; } else { - $select.firstPass = false; - } - }); - focusser.prop('disabled', true); //Focusser isn't needed if multiple - }else{ - scope.$watch('$select.selected', function(newValue) { - if (ngModel.$viewValue !== newValue) { - ngModel.$setViewValue(newValue); - } - }); - } - - ngModel.$render = function() { - if($select.multiple){ - // Make sure that model value is array - if(!angular.isArray(ngModel.$viewValue)){ - // Have tolerance for null or undefined values - if(angular.isUndefined(ngModel.$viewValue) || ngModel.$viewValue === null){ - $select.selected = []; - } else { - throw uiSelectMinErr('multiarr', "Expected model value to be array but got '{0}'", ngModel.$viewValue); - } + throw uiSelectMinErr('multiarr', "Expected model value to be array but got '{0}'", ngModel.$viewValue); } } - $select.selected = ngModel.$viewValue; - }; - - function onDocumentClick(e) { - if (!$select.open) return; //Skip it if dropdown is close - - var contains = false; - - if (window.jQuery) { - // Firefox 3.6 does not support element.contains() - // See Node.contains https://developer.mozilla.org/en-US/docs/Web/API/Node.contains - contains = window.jQuery.contains(element[0], e.target); - } else { - contains = element[0].contains(e.target); - } - - if (!contains && !$select.clickTriggeredSelect) { - //Will lose focus only with certain targets - var focusableControls = ['input','button','textarea']; - var targetScope = angular.element(e.target).scope(); //To check if target is other ui-select - var skipFocusser = targetScope && targetScope.$select && targetScope.$select !== $select; //To check if target is other ui-select - if (!skipFocusser) skipFocusser = ~focusableControls.indexOf(e.target.tagName.toLowerCase()); //Check if target is input, button or textarea - $select.close(skipFocusser); - scope.$digest(); - } - $select.clickTriggeredSelect = false; } + $select.selected = ngModel.$viewValue; + }; - // See Click everywhere but here event http://stackoverflow.com/questions/12931369 - $document.on('click', onDocumentClick); - - scope.$on('$destroy', function() { - $document.off('click', onDocumentClick); - }); + function onDocumentClick(e) { + if (!$select.open) return; //Skip it if dropdown is close - // Move transcluded elements to their correct position in main template - transcludeFn(scope, function(clone) { - // See Transclude in AngularJS http://blog.omkarpatil.com/2012/11/transclude-in-angularjs.html + var contains = false; - // One day jqLite will be replaced by jQuery and we will be able to write: - // var transcludedElement = clone.filter('.my-class') - // instead of creating a hackish DOM element: - var transcluded = angular.element('
').append(clone); + if (window.jQuery) { + // Firefox 3.6 does not support element.contains() + // See Node.contains https://developer.mozilla.org/en-US/docs/Web/API/Node.contains + contains = window.jQuery.contains(element[0], e.target); + } else { + contains = element[0].contains(e.target); + } - var transcludedMatch = transcluded.querySelectorAll('.ui-select-match'); - transcludedMatch.removeAttr('ui-select-match'); //To avoid loop in case directive as attr - transcludedMatch.removeAttr('data-ui-select-match'); // Properly handle HTML5 data-attributes - if (transcludedMatch.length !== 1) { - throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-match but got '{0}'.", transcludedMatch.length); - } - element.querySelectorAll('.ui-select-match').replaceWith(transcludedMatch); + if (!contains && !$select.clickTriggeredSelect) { + //Will lose focus only with certain targets + var focusableControls = ['input','button','textarea']; + var targetScope = angular.element(e.target).scope(); //To check if target is other ui-select + var skipFocusser = targetScope && targetScope.$select && targetScope.$select !== $select; //To check if target is other ui-select + if (!skipFocusser) skipFocusser = ~focusableControls.indexOf(e.target.tagName.toLowerCase()); //Check if target is input, button or textarea + $select.close(skipFocusser); + scope.$digest(); + } + $select.clickTriggeredSelect = false; + } + + // See Click everywhere but here event http://stackoverflow.com/questions/12931369 + $document.on('click', onDocumentClick); + + scope.$on('$destroy', function() { + $document.off('click', onDocumentClick); + }); + + // Move transcluded elements to their correct position in main template + transcludeFn(scope, function(clone) { + // See Transclude in AngularJS http://blog.omkarpatil.com/2012/11/transclude-in-angularjs.html + + // One day jqLite will be replaced by jQuery and we will be able to write: + // var transcludedElement = clone.filter('.my-class') + // instead of creating a hackish DOM element: + var transcluded = angular.element('
').append(clone); + + var transcludedMatch = transcluded.querySelectorAll('.ui-select-match'); + transcludedMatch.removeAttr('ui-select-match'); //To avoid loop in case directive as attr + transcludedMatch.removeAttr('data-ui-select-match'); // Properly handle HTML5 data-attributes + if (transcludedMatch.length !== 1) { + throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-match but got '{0}'.", transcludedMatch.length); + } + element.querySelectorAll('.ui-select-match').replaceWith(transcludedMatch); - var transcludedChoices = transcluded.querySelectorAll('.ui-select-choices'); - transcludedChoices.removeAttr('ui-select-choices'); //To avoid loop in case directive as attr - transcludedChoices.removeAttr('data-ui-select-choices'); // Properly handle HTML5 data-attributes - if (transcludedChoices.length !== 1) { - throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-choices but got '{0}'.", transcludedChoices.length); - } - element.querySelectorAll('.ui-select-choices').replaceWith(transcludedChoices); - }); - }; + var transcludedChoices = transcluded.querySelectorAll('.ui-select-choices'); + transcludedChoices.removeAttr('ui-select-choices'); //To avoid loop in case directive as attr + transcludedChoices.removeAttr('data-ui-select-choices'); // Properly handle HTML5 data-attributes + if (transcludedChoices.length !== 1) { + throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-choices but got '{0}'.", transcludedChoices.length); + } + element.querySelectorAll('.ui-select-choices').replaceWith(transcludedChoices); + }); } }; }]); diff --git a/src/uisFocusDirectives.js b/src/uisFocusDirectives.js deleted file mode 100644 index 793f90a9f..000000000 --- a/src/uisFocusDirectives.js +++ /dev/null @@ -1,25 +0,0 @@ -uis.directive('uisAutofocusAttr', ['$timeout', function($timeout) { - return { - restrict: 'EA', - require: '^uiSelect', - link: function(scope, element, attrs, $select) { - $timeout(function(){ - $select.setFocus(); - }); - } - }; -}]); - -uis.directive('uisFocusOnAttr', ['$timeout', function($timeout) { - return { - restrict: 'A', - require: '^uiSelect', - link: function(scope, element, attrs, $select) { - scope.$on(attrs.uisFocusOnAttr, function() { - $timeout(function(){ - $select.setFocus(); - }); - }); - } - }; -}]); From b8f8306aed8a8fbfe248c8e916f259349dc205b6 Mon Sep 17 00:00:00 2001 From: Wladimir Coka Date: Sun, 8 Mar 2015 19:32:53 -0500 Subject: [PATCH 4/4] Focus examples --- examples/demo-focus.html | 112 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 examples/demo-focus.html diff --git a/examples/demo-focus.html b/examples/demo-focus.html new file mode 100644 index 000000000..b7a52b0db --- /dev/null +++ b/examples/demo-focus.html @@ -0,0 +1,112 @@ + + + + + AngularJS ui-select + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Feature: Focus

+
+ +

Regular uiSelect

+ + {{$select.selected.name}} + +
+ + email: {{person.email}} + age: + +
+
+ +
+
+
+ +

Using uisAutofocus to automatically get focus when loaded

+ + {{$select.selected.name}} + +
+ + email: {{person.email}} + age: + +
+
+ +
+
+
+ +

Using uisFocusOn defining a scope event name to listen

+ + {{$select.selected.name}} + +
+ + email: {{person.email}} + age: + +
+
+ +
+
+ + + +