/**
|
* Created by Jeong on 2016-10-11.
|
*/
|
'use strict';
|
|
var directiveModule = angular.module('js-autocomplete-single', []);
|
|
directiveModule.directive('jsAutocompleteSingle', ['$filter', '$document', '$compile', '$parse', '$log', '$timeout', '$rootScope',
|
function ($filter, $document, $compile, $parse, $log, $timeout, $rootScope) {
|
|
return {
|
restrict : 'AE',
|
scope : {
|
selectedModel : '=',
|
extraSettings : '=',
|
events : '=',
|
searchFilter : '=?',
|
translationTexts : '=',
|
customText : "=",
|
changeEvent : "=changeEvent",
|
search : "=",
|
source : '&',
|
page : "=",
|
totalPage : "=",
|
inputDisabled : "=",
|
},
|
templateUrl : "custom_components/js-autocomplete-single/js-autocomplete-single.html",
|
link : function ($scope, $element, $attrs) {
|
var $dropdownTrigger = $element.children()[0];
|
var blank_pattern = /^\s+|\s+$/g;
|
|
// 입력 필드 비활성화
|
if (!angular.isDefined($scope.inputDisabled)) {
|
$scope.inputDisabled = false;
|
}
|
|
$scope.options = []; // 전체 목록
|
$scope.networkSuccess = false; // 통신 완료 후 화면 표시.
|
$scope.externalEvents = {
|
onItemSelect : angular.noop,
|
onItemDeselect : angular.noop,
|
onInitDone : angular.noop,
|
};
|
|
$scope.settings = {
|
scrollable : false,
|
scrollableHeight : '300px',
|
displayProp : '',
|
idProp : '',
|
externalIdProp : '',
|
selectionLimit : 0,
|
buttonClasses : 'btn btn-default btn-sm',
|
width : '',
|
widthable : false,
|
imageable : false,
|
imagePathProp : '',
|
type : '',
|
maxlength : 50,
|
autoResize : false
|
};
|
|
$scope.texts = {
|
checkAll : "common.checkAll",
|
unCheckAll : "common.unCheckAll",
|
selectionCount : "common.selecte",
|
selectionOf : '/',
|
searchPlaceholder : 'Search...',
|
buttonDefaultText : "common.select",
|
dynamicButtonTextSuffix : "common.select",
|
empty : "common.noData",
|
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);
|
|
$log.debug("확인 : " , $scope.texts);
|
|
$scope.fn = {
|
searchInputField : searchInputField,
|
getSource : getSource,
|
deleteTag : deleteTag,
|
initOptionSelected : initOptionSelected,
|
selectTarget : selectTarget,
|
toggleDropdown : toggleDropdown,
|
findBeforeFocus : findBeforeFocus,
|
findNextFocus : findNextFocus,
|
getFindObj : getFindObj,
|
clearObject : clearObject,
|
getButtonText : getButtonText,
|
getPropertyForObject : getPropertyForObject,
|
setSelectedItem : setSelectedItem,
|
isChecked : isChecked,
|
closeLayer : closeLayer, // 레이어 팝업 닫히는 이벤트 제어
|
getTranslateKey : getTranslateKey // 데이터가 없을 때 맞는 문구를 출력하기 위해 사용한다.
|
};
|
|
|
// options 내용 변경시 텍스트 초기화
|
$scope.$watch("options", function (newValue, oldValue) {
|
$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,">");
|
});
|
}
|
|
// 팝업창에서 검색대상이 표시되면 팝업 사이즈를 동적으로 변경한다.
|
if ($scope.settings.autoResize) {
|
$(".modal-body").height("auto");
|
|
// 팝업 창에서 동적으로 팝업창ㄷ 높이를 변경해준다.
|
if (newValue.length > 0) {
|
var modalBodyHeight = $(".modal-body").height();
|
var multiDiv = $($element).find(".multi-select-option-ul").height();
|
$(".modal-body").height(modalBodyHeight + multiDiv);
|
}
|
}
|
});
|
|
$scope.$watch("selectedModel", function (newValue, oldValue) {
|
if (angular.isDefined(newValue)) {
|
if (newValue.length < 1) {
|
$scope.texts.totalSelected = null;
|
$(".modal-body").height("auto");
|
}
|
}
|
|
if (angular.isDefined($scope.changeEvent)) {
|
$scope.changeEvent();
|
}
|
});
|
|
// owl-auto-focus 어트리뷰트 존재하면 인풋에 포커스
|
if ("owlAutoFocus" in $attrs) {
|
$($element).find('input').focus();
|
}
|
|
$scope.externalEvents.onInitDone();
|
|
// 데이터가 없을 때 맞는 문구를 출력하기 위해 사용한다.
|
function getTranslateKey(key) {
|
return $scope.texts[key];
|
}
|
|
// 검색된 아이템 클릭시 입력 필드 포커스 주기
|
function searchInputField() {
|
if ($scope.selectedModel.length > 0) {
|
$($element).find(".input-tag-search-field-readonly").focus();
|
}
|
}
|
|
// 서버에서 데이터 조회
|
function getSource() {
|
$scope.source().then(function (response) {
|
$scope.options = response;
|
$scope.networkSuccess = true;
|
});
|
}
|
|
// 선택한 대상 초기화
|
function deleteTag() {
|
$scope.selectedModel = [];
|
|
// 브로드 캐스트가 옵션일 경우 실행.
|
if (angular.isDefined($attrs["broadCast"])) {
|
$rootScope.$broadcast($attrs["broadCast"], $scope.selectedModel);
|
}
|
|
// 요소 찾기 전 scope 업데이트
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
|
$timeout(function () {
|
$($element).find(".input-search").focus();
|
});
|
}
|
|
// 현재 선택한 옵션 초기화
|
function initOptionSelected() {
|
$($element).find(".option-target").each(function () {
|
$(this).removeClass("option-selected");
|
});
|
}
|
|
// 대상을 선택한 경우 실행
|
function selectTarget(id) {
|
$scope.search = "";
|
$scope.page = 0;
|
$scope.totalPage = 0;
|
$scope.fn.initOptionSelected(); // 선택 대상 class 초기화
|
$scope.fn.setSelectedItem(Number(id));
|
|
// 브로드 캐스트가 옵션일 경우 실행.
|
if (angular.isDefined($attrs["broadCast"])) {
|
$rootScope.$broadcast($attrs["broadCast"], $scope.selectedModel);
|
}
|
|
var displaySearchText = $scope.selectedModel[0][$scope.settings.displayProp].split("(");
|
$scope.search = displaySearchText[0].trim();
|
|
// 요소 찾기 전 scope 업데이트
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
|
$timeout(function () {
|
$($element).find(".input-search").focus();
|
});
|
}
|
|
// 목록 레이어 제어
|
function toggleDropdown() {
|
// 아이템이 선택되어 있을 경우 목록 레이어는 표시될 수 없다.
|
$scope.open = true;
|
// if ($scope.selectedModel.length > 0) {
|
// $scope.open = false;
|
// $scope.options = [];
|
// }
|
// else {
|
// $scope.open = true;
|
// }
|
|
// 팝업 창에서 입력 필드에 포커스가 가면 자동 스크롤.
|
if ($scope.settings.autoResize) {
|
$(".modal-body").animate({
|
scrollTop : $(".modal-body").scrollTop() + 420
|
}, 500);
|
}
|
|
$($element).unbind("keyup");
|
$($element).unbind("keydown");
|
|
if ($scope.open) {
|
// 옵션 선택 초기화
|
$scope.fn.initOptionSelected();
|
|
// 최초 열릴 때 통신 시작.
|
if ($scope.options.length == 0) {
|
$scope.fn.getSource();
|
}
|
|
$element.bind("keyup", function (e) {
|
if (e.keyCode == 40 || e.keyCode == 38 || e.keyCode == 13 || e.keyCode == 9) {
|
return false;
|
}
|
|
// 옵션에 active가 들어간 대상이 있을 경우 키보드 입력 제한.
|
var searchTarget = $($element).find(".option-selected");
|
|
if (angular.isDefined(searchTarget[0])) {
|
event.preventDefault();
|
event.stopPropagation();
|
return false;
|
}
|
|
// 검색어가 입력되면 페이징 초기화
|
$scope.page = 0;
|
$scope.totalPage = 0;
|
|
if ($scope.search.length > 0) {
|
if ($scope.search.replace(blank_pattern, '') == "") {
|
$scope.search = "";
|
return false;
|
}
|
}
|
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
|
$scope.fn.getSource();
|
});
|
|
$($element).keydown(function (event) {
|
switch (event.keyCode) {
|
case 9 : // 탭키 닫기
|
$scope.open = false;
|
$scope.search = "";
|
$scope.page = 0;
|
$scope.totalPage = 0;
|
$scope.options = [];
|
$scope.fn.initOptionSelected();
|
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
|
break;
|
case 32 : // 대상 선택
|
event.preventDefault();
|
event.stopPropagation();
|
|
var target = $(".option-selected").find("a");
|
var id = target.attr("data-id");
|
|
if (angular.isDefined(id)) {
|
$scope.fn.selectTarget(id);
|
return false;
|
}
|
|
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;
|
case 27 : // 팝업 닫기 esc
|
$scope.open = false;
|
$scope.$apply();
|
event.preventDefault();
|
event.stopPropagation();
|
break;
|
|
default :
|
// 옵션에 active가 들어간 대상이 있을 경우 키보드 입력 제한.
|
var searchTarget = $(".option-selected");
|
|
if (angular.isDefined(searchTarget[0])) {
|
event.preventDefault();
|
event.stopPropagation();
|
}
|
}
|
});
|
}
|
else {
|
// toggle이 닫혔을 때
|
$($element).keydown(function (event) {
|
|
// 상단 숫자 키패드
|
if (47 < event.keyCode && 58 > event.keyCode) {
|
event.preventDefault();
|
}
|
// 영문 입력 키패드
|
if (64 < event.keyCode && 91 > event.keyCode) {
|
event.preventDefault();
|
}
|
// 우측 키패드
|
if (95 < event.keyCode && 112 > event.keyCode) {
|
event.preventDefault();
|
}
|
// 특수 기호 & 한글 입력
|
if (event.keyCode == 186 || event.keyCode == 187 ||
|
event.keyCode == 188 || event.keyCode == 189 ||
|
event.keyCode == 190 || event.keyCode == 191 ||
|
event.keyCode == 192 || event.keyCode == 219 ||
|
event.keyCode == 220 || event.keyCode == 221 ||
|
event.keyCode == 222 || event.keyCode == 229 ||
|
event.keyCode == 32) {
|
event.preventDefault();
|
}
|
|
// 삭제 대기 아이템 css 제거
|
if (event.keyCode == 8 || event.keyCode == 46) {
|
$scope.fn.deleteTag();
|
// 백스페이스 & delete키
|
}
|
else if (event.keyCode == 9) {
|
$scope.open = false;
|
|
if ($scope.selectedModel.length == 0) {
|
$scope.search = "";
|
}
|
|
$scope.page = 0;
|
$scope.totalPage = 0;
|
$scope.fn.initOptionSelected();
|
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
}
|
});
|
}
|
}
|
|
// 이전 대상 선택
|
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(".input-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);
|
}
|
});
|
|
if ($scope.page + 1 <= $scope.totalPage) {
|
$scope.page++;
|
|
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
|
$scope.$apply();
|
}
|
|
$scope.source().then(function (response) {
|
angular.forEach(response, function (target) {
|
$scope.options.push(target);
|
});
|
});
|
}
|
}
|
}
|
else {
|
target = $($element).find(".option-target").eq(0);
|
$($element).find(".multi-select-option-ul").scrollTop();
|
}
|
|
return target;
|
}
|
|
// id로 객체를 찾는다.
|
function getFindObj(id) {
|
var findObj = {};
|
|
if ($scope.settings.externalIdProp === '') {
|
findObj[$scope.settings.idProp] = id;
|
}
|
else {
|
findObj[$scope.settings.externalIdProp] = id;
|
}
|
|
return findObj;
|
}
|
|
// 전체 객체를 삭제한다.
|
function clearObject(object) {
|
for (var prop in object) {
|
delete object[prop];
|
}
|
}
|
|
// 레이어 팝업 닫히는 이벤트 제어
|
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 (typeof target.className.split != 'function') {
|
break;
|
}
|
|
if (_.contains(target.className.split(' '), 'multiselect-parent') && !parentFound) {
|
if (target === $dropdownTrigger) {
|
parentFound = true;
|
}
|
}
|
target = target.parentElement;
|
}
|
|
if (!parentFound) {
|
$scope.$apply(function () {
|
$scope.open = false;
|
if ($scope.selectedModel == null || $scope.selectedModel.length == 0) {
|
$scope.search = "";
|
}
|
$scope.page = 0;
|
$scope.totalPage = 0;
|
$scope.options = [];
|
$scope.networkSuccess = false;
|
// 삭제 대기 아이템 css 제거
|
$($element).find(".input-tag").removeClass("remove-ready-item");
|
});
|
}
|
});
|
});
|
}
|
|
// 팝업 창이 올라왔을 때 body 이벤트 사라지는 현상을 수정하기 재실행.
|
$scope.$on("closeLayer", function (args) {
|
$scope.fn.closeLayer();
|
});
|
|
// 버튼 텍스트를 동적으로 변경한다.
|
function getButtonText() {
|
if (!angular.isDefined($scope.selectedModel)) {
|
$scope.selectedModel = [];
|
}
|
|
if (($scope.selectedModel.length > 0 || (angular.isObject($scope.selectedModel) && _.keys($scope.selectedModel).length > 0))) {
|
var totalSelected;
|
|
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 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 = [];
|
$scope.selectedModel.push(finalObj);
|
$scope.externalEvents.onItemSelect(finalObj);
|
}
|
}
|
|
// 체크 여부를 판단한다.
|
function isChecked(id) {
|
return _.findIndex($scope.selectedModel, $scope.fn.getFindObj(id)) !== -1;
|
}
|
|
// 레이어 팝업 닫히는 이벤트 제어
|
$scope.fn.closeLayer();
|
|
}
|
};
|
}]);
|