'use strict';
|
|
var directiveModule = angular.module('angularjs-dropdown-multiselect', []);
|
|
directiveModule.directive('ngDropdownMultiselect', ['$filter', '$document', '$compile', '$parse', '$log', '$timeout',
|
function ($filter, $document, $compile, $parse, $log, $timeout) {
|
|
return {
|
restrict : 'AE',
|
scope : {
|
selectedModel : '=',
|
options : '=',
|
extraSettings : '=',
|
events : '=',
|
searchFilter : '=?',
|
translationTexts : '=',
|
customText : "=",
|
changeEvent : "=changeEvent",
|
},
|
templateUrl : "custom_components/angular-multi-select/angular-multi-select.html",
|
link : function ($scope, $element, $attrs) {
|
var $dropdownTrigger = $element.children()[0];
|
|
$scope.externalEvents = {
|
onItemSelect : angular.noop,
|
onItemDeselect : angular.noop,
|
onSelectAll : angular.noop,
|
onDeselectAll : angular.noop,
|
onInitDone : angular.noop,
|
onMaxSelectionReached : angular.noop
|
};
|
|
$scope.settings = {
|
dynamicTitle : true,
|
scrollable : false,
|
scrollableHeight : '300px',
|
displayProp : 'fieldValue',
|
idProp : 'fieldKey',
|
externalIdProp : 'fieldKey',
|
enableSearch : false,
|
selectionLimit : 0,
|
showCheckAll : true,
|
showUncheckAll : true,
|
closeOnSelect : false,
|
buttonClasses : 'btn btn-default multi-select-form-control',
|
closeOnDeselect : false,
|
smartButtonMaxItems : 0,
|
smartButtonTextConverter : angular.noop,
|
width : '',
|
widthable : false,
|
stringTypeOption : false,
|
maxlength : 50
|
};
|
|
$scope.texts = {
|
checkAll : "common.checkAll",
|
unCheckAll : "common.unCheckAll",
|
selectionCount : "common.selected",
|
selectionOf : '/',
|
searchPlaceholder : 'Search...',
|
buttonDefaultText : "common.select",
|
dynamicButtonTextSuffix : "common.select",
|
totalSelected : null,
|
selectYn : false
|
};
|
|
$scope.searchFilter = $scope.searchFilter || '';
|
|
angular.extend($scope.settings, $scope.extraSettings || []);
|
angular.extend($scope.externalEvents, $scope.events || []);
|
angular.extend($scope.texts, $scope.translationTexts);
|
|
// 함수 모음
|
$scope.fn = {
|
closeLayer : closeLayer,
|
initOptionSelected : initOptionSelected,
|
clickActionAddFocus : clickActionAddFocus,
|
toggleDropdown : toggleDropdown,
|
findBeforeFocus : findBeforeFocus,
|
findNextFocus : findNextFocus,
|
getFindObj : getFindObj,
|
getButtonText : getButtonText,
|
getPropertyForObject : getPropertyForObject,
|
selectAll : selectAll,
|
deselectAll : deselectAll,
|
setSelectedItem : setSelectedItem,
|
isChecked : isChecked,
|
};
|
|
// 팝업 창이 올라왔을 때 body 이벤트 사라지는 현상을 수정하기 재실행.
|
$scope.$on("closeLayer", function () {
|
$scope.fn.closeLayer();
|
});
|
|
// options 내용 변경시 텍스트 초기화
|
$scope.$watch("options", function (newValue) {
|
$scope.texts.totalSelected = null;
|
|
if (angular.isDefined(newValue)) {
|
angular.forEach(newValue, function (option) {
|
option[$scope.settings.displayProp] = option[$scope.settings.displayProp].replace(/</g,"<");
|
option[$scope.settings.displayProp] = option[$scope.settings.displayProp].replace(/>/g,">");
|
});
|
}
|
});
|
|
$scope.$watch("selectedModel", function (newValue) {
|
if (angular.isDefined(newValue)) {
|
if (newValue.length < 1) {
|
$scope.texts.totalSelected = null;
|
}
|
}
|
|
if (angular.isDefined($scope.changeEvent)) {
|
$scope.changeEvent();
|
}
|
});
|
|
|
// 선택한 옵션에 있는 클래스를 초기화한다.
|
function initOptionSelected() {
|
$($element).find(".option-target").each(function () {
|
$(this).removeClass("option-selected");
|
});
|
}
|
|
// 클릭시 포커스를 준다.
|
function clickActionAddFocus(id) {
|
$scope.fn.initOptionSelected();
|
|
$($element).find(".option-target").each(function () {
|
if ($(this).find("a").attr("data-id") == id) {
|
$(this).addClass("option-selected");
|
return false;
|
}
|
});
|
|
$scope.fn.setSelectedItem(id);
|
}
|
|
function toggleDropdown($event) {
|
$($element).unbind("keydown");
|
|
if (!$scope.open && (($event.type == "keydown" && $event.keyCode == 40) || ($event.type == "click"))) {
|
$scope.open = !$scope.open;
|
|
if ($scope.open) {
|
// 옵션 선택 초기화
|
$scope.fn.initOptionSelected();
|
|
$timeout(function () {
|
$($element).find(".select-search").focus();
|
});
|
|
if ($event.type != "click") {
|
event.preventDefault();
|
event.stopPropagation();
|
}
|
|
$($element).keydown(function (event) {
|
switch (event.keyCode) {
|
case 13 :
|
event.preventDefault();
|
event.stopPropagation();
|
|
$scope.open = false;
|
$scope.fn.initOptionSelected();
|
|
$timeout(function () {
|
$($element).find(".select-display-btn").focus();
|
});
|
|
break;
|
case 9 : // 탭키 닫기
|
$scope.open = false;
|
$scope.fn.initOptionSelected();
|
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
|
break;
|
case 32 : // 대상 선택
|
var target = $($element).find(".option-selected").find("a");
|
var id = target.attr("data-id");
|
|
if (angular.isDefined(id)) {
|
if ($scope.settings.stringTypeOption) {
|
$scope.fn.setSelectedItem(String(id));
|
}
|
else {
|
$scope.fn.setSelectedItem(Number(id));
|
}
|
|
event.preventDefault();
|
event.stopPropagation();
|
}
|
break;
|
case 38 : // 위
|
event.preventDefault();
|
event.stopPropagation();
|
var target = $scope.fn.findBeforeFocus();
|
|
if (target != null) {
|
target.addClass("option-selected");
|
target.find("a").focus();
|
}
|
|
break;
|
case 40 : // 아래
|
event.preventDefault();
|
event.stopPropagation();
|
|
var target = $scope.fn.findNextFocus();
|
target.addClass("option-selected");
|
target.find("a").focus();
|
break;
|
}
|
});
|
}
|
}
|
else {
|
if ($event.type == "click" && $scope.open) {
|
$scope.open = false;
|
}
|
}
|
}
|
|
function findBeforeFocus() {
|
var searchTarget = $($element).find(".option-selected");
|
|
var target = null;
|
|
if (angular.isDefined(searchTarget[0])) {
|
var currentTargetId = $(searchTarget).find("a").attr("data-id");
|
|
$($element).find(".option-target").each(function (index) {
|
$(this).removeClass("option-selected");
|
|
if ($(this).find("a").attr("data-id") == currentTargetId) {
|
if (index > 0) {
|
target = $(this).prev();
|
}
|
else {
|
$timeout(function () {
|
$($element).find(".select-search").focus();
|
});
|
}
|
}
|
});
|
}
|
|
return target;
|
}
|
|
|
function findNextFocus() {
|
var searchTarget = $($element).find(".option-selected");
|
|
var target = null;
|
|
if (angular.isDefined(searchTarget[0])) {
|
|
var currentTargetId = $(searchTarget).find("a").attr("data-id");
|
var nextCheck = false;
|
|
$($element).find(".option-target").each(function () {
|
if (nextCheck) {
|
target = $(this);
|
nextCheck = false;
|
}
|
|
$(this).removeClass("option-selected");
|
if ($(this).find("a").attr("data-id") == currentTargetId) {
|
nextCheck = true;
|
}
|
});
|
|
if (target == null) {
|
$($element).find(".option-target").each(function () {
|
if ($(this).find("a").attr("data-id") == currentTargetId) {
|
target = $(this);
|
}
|
});
|
}
|
}
|
else {
|
target = $($element).find(".option-target").eq(0);
|
$(".multi-select-option-ul").scrollTop();
|
}
|
|
return target;
|
}
|
|
|
function getFindObj(id) {
|
var findObj = {};
|
|
if ($scope.settings.externalIdProp === '') {
|
findObj[$scope.settings.idProp] = id;
|
}
|
else {
|
findObj[$scope.settings.externalIdProp] = id;
|
}
|
|
return findObj;
|
}
|
|
// 레이어 팝업 닫히는 이벤트 제어
|
function closeLayer() {
|
$(document).ready(function () {
|
$("body").click(function (e) {
|
var target = e.target.parentElement;
|
var parentFound = false;
|
|
while (angular.isDefined(target) && target !== null && !parentFound) {
|
if (_.contains(target.className.split(' '), 'multiselect-parent') && !parentFound) {
|
if (target === $dropdownTrigger) {
|
parentFound = true;
|
}
|
}
|
target = target.parentElement;
|
}
|
|
if (!parentFound) {
|
$scope.$apply(function () {
|
$scope.open = false;
|
$scope.searchFilter = "";
|
});
|
}
|
});
|
});
|
}
|
|
function getButtonText() {
|
if (!angular.isDefined($scope.selectedModel)) {
|
$scope.selectedModel = [];
|
}
|
|
if ($scope.settings.dynamicTitle && ($scope.selectedModel.length > 0 || (angular.isObject($scope.selectedModel) && _.keys($scope.selectedModel).length > 0))) {
|
if ($scope.settings.smartButtonMaxItems > 0) {
|
var itemsText = [];
|
|
angular.forEach($scope.options, function (optionItem) {
|
if ($scope.fn.isChecked($scope.fn.getPropertyForObject(optionItem, $scope.settings.idProp))) {
|
var displayText = $scope.fn.getPropertyForObject(optionItem, $scope.settings.displayProp);
|
var converterResponse = $scope.settings.smartButtonTextConverter(displayText, optionItem);
|
|
itemsText.push(converterResponse ? converterResponse : displayText);
|
}
|
});
|
|
if ($scope.selectedModel.length > $scope.settings.smartButtonMaxItems) {
|
itemsText = itemsText.slice(0, $scope.settings.smartButtonMaxItems);
|
itemsText.push('...');
|
}
|
|
return itemsText.join(', ');
|
}
|
else {
|
var totalSelected = angular.isDefined($scope.selectedModel) ? $scope.selectedModel.length : 0;
|
|
if (totalSelected === 0) {
|
return $scope.customText == undefined ? $scope.texts.buttonDefaultText : $scope.customText;
|
}
|
else {
|
$scope.texts.totalSelected = totalSelected;
|
return $scope.texts.dynamicButtonTextSuffix;
|
}
|
}
|
}
|
else {
|
return $scope.customText == undefined ? $scope.texts.buttonDefaultText : $scope.customText;
|
}
|
}
|
|
function getPropertyForObject(object, property) {
|
if (angular.isDefined(object) && object.hasOwnProperty(property)) {
|
return object[property];
|
}
|
|
return '';
|
}
|
|
function selectAll() {
|
$scope.fn.deselectAll(false);
|
$scope.externalEvents.onSelectAll();
|
|
angular.forEach($filter("filter")($scope.options, $scope.searchFilter), function (value) {
|
$scope.fn.setSelectedItem(value[$scope.settings.idProp], true);
|
});
|
// 옵션 포커스 초기화
|
$scope.fn.initOptionSelected();
|
}
|
|
function deselectAll(sendEvent) {
|
// 옵션 포커스 초기화
|
$scope.fn.initOptionSelected();
|
$scope.texts.totalSelected = null;
|
sendEvent = sendEvent || true;
|
|
if (sendEvent) {
|
$scope.externalEvents.onDeselectAll();
|
}
|
|
var filterConvertOptions = $filter("filter")($scope.options, $scope.searchFilter);
|
var adds = [];
|
|
angular.forEach($scope.selectedModel, function (target) {
|
var removeCheck = false;
|
for (var count in filterConvertOptions) {
|
if (target.fieldKey == filterConvertOptions[count].fieldKey) {
|
removeCheck = true;
|
break;
|
}
|
}
|
|
if (!removeCheck) {
|
adds.push(target);
|
}
|
});
|
|
$scope.selectedModel = adds;
|
}
|
|
|
function setSelectedItem(id, dontRemove) {
|
var findObj = $scope.fn.getFindObj(id);
|
var finalObj = null;
|
|
if ($scope.settings.externalIdProp === '') {
|
finalObj = _.find($scope.options, findObj);
|
}
|
else {
|
finalObj = findObj;
|
}
|
|
dontRemove = dontRemove || false;
|
|
var exists = _.findIndex($scope.selectedModel, findObj) !== -1;
|
|
if (!dontRemove && exists) {
|
$scope.selectedModel.splice(_.findIndex($scope.selectedModel, findObj), 1);
|
$scope.externalEvents.onItemDeselect(findObj);
|
$scope.texts.totalSelected = null;
|
}
|
else if (!exists && ($scope.settings.selectionLimit === 0 || $scope.selectedModel.length < $scope.settings.selectionLimit)) {
|
$scope.selectedModel.push(finalObj);
|
$scope.externalEvents.onItemSelect(finalObj);
|
}
|
if ($scope.settings.closeOnSelect) $scope.open = false;
|
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
}
|
|
function isChecked(id) {
|
return _.findIndex($scope.selectedModel, $scope.fn.getFindObj(id)) !== -1;
|
}
|
|
$scope.externalEvents.onInitDone();
|
|
$scope.fn.closeLayer();
|
}
|
};
|
}]);
|
|
directiveModule.filter('multiSelectFilter', ["$log", "$filter",
|
function ($log, $filter) {
|
return function (items, searchObj) {
|
|
var results = [];
|
|
angular.forEach(items, function (item) {
|
var translateWord = $filter("translate")(item[searchObj.searchKey]);
|
|
if (translateWord.indexOf(searchObj.searchWord) != -1) {
|
results.push(item);
|
}
|
});
|
|
return results;
|
}
|
}]);
|