Created
September 17, 2015 12:54
-
-
Save hmps/692a9e574dd032b0a97e to your computer and use it in GitHub Desktop.
Select
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;(function(angular) { | |
'use strict'; | |
/* globals angular */ | |
/* jshint latedef: false */ | |
/** | |
* @module Apsis.modules.select | |
* @name SelectController | |
* | |
* @description Controller for the select.directive | |
* | |
*/ | |
angular.module('Apsis.modules.select') | |
.controller('SelectController', SelectController); | |
function SelectController($scope) { | |
var vm = this; | |
checkRequiredParams($scope); | |
vm.options = $scope.options; | |
vm.selectKey = $scope.selectKey || 'id'; | |
vm.titleKey = $scope.optionTitle || 'value'; | |
vm.changeModelValue = changeModelValue; | |
setSelectedOption($scope.options, $scope.selectValue); | |
return vm; | |
//////////////////////////////////////////////////////////////// | |
/** | |
* Set the $scope value for selectValue. This is passed back | |
* into the calling controller and gives it a chance to react | |
* to changes to it. | |
* | |
* @return The selected item | |
*/ | |
function changeModelValue(value) { | |
$scope.selectValue = value; | |
} | |
/** | |
* Checks the given parameters to make sure that everything | |
* required has been passed in. | |
* | |
* In this version only "options" is required. | |
*/ | |
function checkRequiredParams($scope) { | |
if ( !angular.isArray($scope.options) ) { | |
throw new Error('Select: You must pass in an options collection'); | |
} | |
} | |
/** | |
* Called on init to set the selected item in the collection. | |
* | |
* @param {array} options array of options | |
* @param selectValue the value that identifies the selected option | |
* | |
* @return {object} the selected item | |
*/ | |
function setSelectedOption(options, selectValue) { | |
if ( !selectValue ) { | |
vm.selected = { | |
index: 0, | |
item: options[0] | |
}; | |
return vm.selected; | |
} | |
angular.forEach(options, function(option, index) { | |
if ( option[vm.selectKey] === selectValue ) { | |
vm.selected = { | |
index: index, | |
item: option | |
}; | |
// Returning false causes the loop to abort early. | |
return false; | |
} | |
}); | |
return vm.selected; | |
} | |
} | |
}(angular)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;(function(angular) { | |
'use strict'; | |
/* global angular, $ */ | |
/* jshint latedef: false */ | |
/** | |
* @module Apsis.modules.select | |
* @name apsis-select | |
* @author HAPE | |
* | |
* @description | |
* # Select | |
* Renders a HTML5 styled select box. To use it, add apsis-select to a <div> | |
* and pass in the parameters required. | |
* | |
* @example | |
* <script> | |
* var options = [ | |
* { id: 0, title: 'Zero' }, | |
* { id: 1, title: 'One' }, | |
* { id: 2, title: 'Two' }, | |
* ]; | |
* </script> | |
* | |
* <div | |
* apsis-select | |
* select-options="options" | |
* select-key="id" | |
* select-value=1 | |
* select-option-title="title" | |
* ></div> | |
* | |
* @params | |
* select-options = a collection of options *required | |
* select-key = the key to select active item by. *optional, default = 'id' | |
* select-value = the value to select active item by. *optional, default = 0 | |
* select-option-title = the key to set as option title. *optional, default = 'value' | |
* | |
*/ | |
angular.module('Apsis.modules.select') | |
.directive('apsisSelect', selectDirective); | |
function selectDirective() { | |
return { | |
scope: { | |
options: '=selectOptions', | |
selectKey: '=', | |
selectValue: '=', | |
optionTitle: '@selectOptionTitle' | |
}, | |
restrict: 'AE', | |
templateUrl: '/modules/select/html/select.template.html', | |
replace: true, | |
controllerAs: 'select', | |
controller: 'SelectController', | |
link: selectLink | |
}; | |
} | |
function selectLink(scope, $element) { | |
var _select = {}, | |
vm = scope.select; | |
setupDOM(); | |
setupEvents(); | |
/** | |
* Add methods to the scope to make them accessible from the template | |
*/ | |
vm.toggleDropdown = toggleDropdown; | |
vm.getSelectedItem = getSelectedItem; | |
vm.selectItem = selectItem; | |
vm.keyPressed = keyPressed; | |
vm.gotFocus = gotFocus; | |
vm.didBlur = didBlur; | |
function keyPressed(e) { | |
/** | |
* The keydowns that we are listening for are: | |
* | |
* 9 = tab | |
* 13 = enter | |
* 27 = Esc | |
* 32 = spacebar | |
* 38 = up key | |
* 40 = down | |
*/ | |
var capture = [9, 13, 32, 38, 40]; | |
/* If the key pressed is anything other than the above, we'll | |
* simply return and let the user get back to business */ | |
if ( capture.indexOf(e.which) === -1 ) { | |
return; | |
} | |
/* If the tab key is pressed we close the dropdown and then move | |
* on to the next tab index. If the dropdown is not open the user | |
* will not notice any difference from normal browser behaviour */ | |
if ( 9 === e.which ) { | |
closeDropdown(); | |
return; | |
} | |
e.preventDefault(); | |
e.stopPropagation(); | |
switch (e.which) { | |
case 13: | |
closeDropdown(); | |
break; | |
case 27: | |
closeDropdown(); | |
break; | |
case 32: | |
toggleDropdown(); | |
break; | |
case 38: | |
selectPrevItem(); | |
break; | |
case 40: | |
selectNextItem(); | |
break; | |
} | |
} | |
/** | |
* When the select element is focused, set a class to it | |
*/ | |
function gotFocus() { | |
_select.$styledSelect.addClass('has-focus'); | |
} | |
/** | |
* Remove the class that is set in gotFocus | |
*/ | |
function didBlur() { | |
_select.$styledSelect.removeClass('active'); | |
_select.$styledSelect.removeClass('has-focus'); | |
} | |
/** | |
* Setup DOM references for use in the directive | |
*/ | |
function setupDOM() { | |
_select.$styledSelect = $element.find('.select-styled'); | |
_select.$el = $element.find('select'); | |
_select.$list = $element.find('.select-options'); | |
} | |
/** | |
* Setup DOM events that are handled "outside" of angulars build in | |
* directives | |
*/ | |
function setupEvents() { | |
/** | |
* Close the select if the user clicks outside of it while it's open | |
*/ | |
$(document).on('click', clickHandler); | |
scope.$on('$destroy', function() { | |
$(document).off('click', clickHandler); | |
}); | |
function clickHandler(e) { | |
if ( ! $(e.target).closest('.select').length ) { | |
_select.$styledSelect.removeClass('active'); | |
_select.$list.hide(); | |
} | |
} | |
} | |
/** | |
* Close the dropdown. | |
*/ | |
function closeDropdown() { | |
_select.$styledSelect.removeClass('active'); | |
_select.$list.hide(); | |
} | |
/** | |
* Select the next item in the collection | |
*/ | |
function selectNextItem() { | |
/* (_items.length - 1) because lenght starts on 1 and _activeItem | |
starts on 0 */ | |
if ( ( vm.options.length - 1 ) > getSelectedItem().index ) { | |
selectItem(getSelectedItem().index + 1); | |
} | |
} | |
/** | |
* Select the previous item in the collection | |
*/ | |
function selectPrevItem() { | |
if ( 0 < getSelectedItem().index ) { | |
selectItem(vm.getSelectedItem().index - 1); | |
} | |
} | |
/** | |
* Select a specific item in the collection | |
*/ | |
function selectItem(index) { | |
vm.selected = { | |
index: index, | |
item: vm.options[index] | |
}; | |
vm.changeModelValue(vm.selected.item.id); | |
setSelectedText(getSelectedItem().item[vm.titleKey]); | |
setSelectedValue(getSelectedItem().item.id); | |
_select.$list.find('.has-focus').removeClass('has-focus'); | |
_select.$list.children().eq(index).addClass('has-focus'); | |
} | |
/** | |
* Get the currently selected item | |
* @return {object} | |
*/ | |
function getSelectedItem() { | |
return vm.selected; | |
} | |
/** | |
* Set the selected value | |
*/ | |
function setSelectedValue(value) { | |
return _select.$el.val(value); | |
} | |
/** | |
* Set the selected text | |
*/ | |
function setSelectedText(text) { | |
_select.$styledSelect.text(text); | |
} | |
/** | |
* Toggle the select dropdowns visibility | |
*/ | |
function toggleDropdown() { | |
_select.$styledSelect.toggleClass('active'); | |
_select.$list.focus(); | |
_select.$list.toggle(); | |
} | |
} | |
}(angular)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="select"> | |
<select | |
class="select-hidden" | |
ng-model="select.selected.item" | |
ng-options="item.title for item in select.options" | |
ng-keydown="select.keyPressed($event)" | |
ng-focus="select.gotFocus()" | |
ng-blur="select.didBlur()" | |
></select> | |
<div | |
class="select-styled" | |
ng-click="select.toggleDropdown()" | |
> | |
{{select.selected.item.title}} | |
</div> | |
<ul class="select-options"> | |
<li | |
ng-repeat="option in select.options" | |
rel="{{option.id}}" | |
ng-class="{'has-focus': select.selected.item.id === option.id}" | |
ng-click="select.selectItem($index); select.toggleDropdown()" | |
> | |
{{option.title}} | |
</li> | |
</ul> | |
</div> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment