/**
|
* Created by Jeong on 2016-10-11.
|
*/
|
'use strict';
|
|
var directiveModule = angular.module('js-input-autocomplete', []);
|
|
directiveModule.directive('jsInputAutocomplete', ['$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-input-autocomplete/js-input-autocomplete.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,
|
closeOnSelect: false,
|
buttonClasses: 'btn btn-default btn-sm',
|
closeOnDeselect: false,
|
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);
|
|
$scope.fn = {
|
getSource : getSource,
|
initOptionSelected : initOptionSelected,
|
selectTarget : selectTarget,
|
toggleDropdown : toggleDropdown,
|
findBeforeFocus : findBeforeFocus,
|
findNextFocus : findNextFocus,
|
getFindObj : getFindObj,
|
clearObject : clearObject,
|
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 modalSelectModelHeight = ($scope.selectedModel.length * 20) + 30; // row + th컬럼 높이
|
var offsetTop = $($element).find(".auto-complete-input").offset().top;
|
var multiDiv = newValue.length * 25;
|
var currentHeight = offsetTop + modalSelectModelHeight; // 현재 모달 높이
|
var divHeight = multiDiv - modalSelectModelHeight; // 검색된 정보의 div 높이 - input 밑 테이블 높이
|
var requireHeight = offsetTop + divHeight;
|
var addHeight = requireHeight - currentHeight;
|
|
if (addHeight > 0) {
|
$(".modal-body").height(modalBodyHeight + addHeight);
|
}
|
}
|
}
|
});
|
|
$scope.$watch("selectedModel", function (newValue) {
|
if (angular.isDefined(newValue)) {
|
if (newValue.length < 1) {
|
$scope.texts.totalSelected = null;
|
}
|
}
|
|
if (angular.isDefined($scope.changeEvent)) {
|
$scope.changeEvent();
|
}
|
});
|
|
if($attrs["broadCast"] === "assigneeUpload") {
|
$($element).find(".auto-complete-input").focus();
|
}
|
|
$scope.externalEvents.onInitDone();
|
|
// 데이터가 없을 때 맞는 문구를 출력하기 위해 사용한다.
|
function getTranslateKey(key) {
|
return $scope.texts[key];
|
}
|
|
// 레이어 팝업 닫히는 이벤트 제어
|
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;
|
}
|
|
// 유동적으로 변경되는 값이 li를 그릴 경우 부모를 찾지 못하는 오류 수정.
|
if (target == null) {
|
if ($(e.target.parentElement).hasClass("option-selected-li") || $(e.target.parentElement).hasClass("option-selected-ul")
|
|| $(e.target.parentElement).hasClass("element-item")) {
|
parentFound = true;
|
}
|
}
|
|
if (!parentFound) {
|
$scope.$apply(function () {
|
$scope.open = false;
|
$scope.page = 0;
|
$scope.search = "";
|
$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 getSource() {
|
// 요소 찾기 전 scope 업데이트
|
if ($scope.$root.$$phase !== '$apply' && $scope.$root.$$phase !== '$digest') {
|
$scope.$apply();
|
}
|
|
$scope.source().then(function (response) {
|
$scope.options = response;
|
$scope.networkSuccess = true;
|
|
if($attrs["broadCast"] === "assigneeUpload") {
|
$($element).find(".auto-complete-input").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"])) {
|
if($attrs["broadCast"] === "assigneeUpload"){
|
$rootScope.$broadcast($attrs["broadCast"], {id : id});
|
}else{
|
$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();
|
$scope.fn.getSource();
|
});
|
}
|
|
// 목록 레이어 제어
|
function toggleDropdown() {
|
$scope.open = true;
|
|
$($element).unbind("keyup");
|
$($element).unbind("keydown");
|
|
// 옵션 선택 초기화
|
$scope.fn.initOptionSelected();
|
|
// 최초 열릴 때 통신 시작.
|
if ($scope.options.length < 1) {
|
$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) {
|
$scope.open = true;
|
|
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 $targetA = $($element).find(".option-selected").find("a");
|
var id = $targetA.attr("data-id");
|
|
if (angular.isDefined(id)) {
|
$scope.fn.selectTarget(id);
|
return false;
|
}
|
|
break;
|
case 38 : // 위
|
event.preventDefault();
|
event.stopPropagation();
|
var $targetBefore = $scope.fn.findBeforeFocus();
|
|
if ($targetBefore != null) {
|
$targetBefore.addClass("option-selected");
|
$targetBefore.find("a").focus();
|
}
|
|
break;
|
case 40 : // 아래
|
event.preventDefault();
|
event.stopPropagation();
|
var $targetNext = $scope.fn.findNextFocus();
|
$targetNext.addClass("option-selected");
|
$targetNext.find("a").focus();
|
|
break;
|
case 27 : // 팝업 닫기 esc
|
$scope.open = false;
|
$scope.$apply();
|
event.preventDefault();
|
event.stopPropagation();
|
break;
|
|
default :
|
// 옵션에 active가 들어간 대상이 있을 경우 키보드 입력 제한.
|
var searchTarget = $($element).find(".option-selected");
|
|
if (angular.isDefined(searchTarget[0])) {
|
event.preventDefault();
|
event.stopPropagation();
|
}
|
}
|
});
|
}
|
|
// 이전 요소 찾기
|
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 (index) {
|
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 (index) {
|
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 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.push(finalObj);
|
$scope.externalEvents.onItemSelect(finalObj);
|
}
|
|
if ($scope.$root.$$phase !== '$apply' && $scope.$root.$$phase !== '$digest') {
|
$scope.$apply();
|
}
|
}
|
|
// 체크 여부를 판단한다.
|
function isChecked(id) {
|
return _.findIndex($scope.selectedModel, $scope.fn.getFindObj(id)) !== -1;
|
}
|
|
$scope.fn.closeLayer();
|
}
|
};
|
}]);
|