Skip to content

Instantly share code, notes, and snippets.

@hmps
Created September 17, 2015 12:54
Show Gist options
  • Save hmps/692a9e574dd032b0a97e to your computer and use it in GitHub Desktop.
Save hmps/692a9e574dd032b0a97e to your computer and use it in GitHub Desktop.
Select
;(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));
;(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));
<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