(function () { 'use strict'; angular.module('api.module.Components') /** * @class api.module.Components.directives.EloInputAttr * @ngDirective * @xtype elo-input-attr * * @private */ .directive('eloInputAttr', ['$compile', function ($compile) { return { priority: 1001, // compiles first terminal: true, // prevent lower priority directives to compile after it require: '^eloInput', scope: false, compile: function (el) { el.removeAttr('elo-input-attr'); // necessary to avoid infinite compile loop return function (scope, element, attrs, parentCtrl) { if (scope.config.disabled) { element.attr('disabled', 'true'); } if (scope.config.kwl) { element.attr('uib-typeahead', scope.typeaheadString); element.attr('typeahead-template-url', scope.config.kwl.templateUrl); element.attr('typeahead-editable', !scope.config.kwl.onlyFromKwl); element.attr('typeahead-min-length', scope.config.kwl.typeaheadMinLength); element.attr('typeahead-loading', 'typeaheadLoading'); element.attr('typeahead-no-results', 'typeaheadNoResults'); element.attr('typeahead-wait-ms', scope.typeaheadWaitMs); if (scope.config.kwl.onSelect) { element.attr('typeahead-on-select', "config.kwl.onSelect($item, $model, $label, model)"); } if (scope.config.kwl.popupUrl) { element.attr('typeahead-popup-template-url', scope.config.kwl.popupUrl); } } else if (!scope.config.kwl && scope.config.datePickerPopup) { element.attr('uib-datepicker-popup', scope.config.datePickerPopup); element.attr('is-open', 'store.datePickerIsOpen'); element.attr('current-text', api.helpers.Text.getText('ELOCMP.INPUT.CURRENTTEXT')); element.attr('clear-text', api.helpers.Text.getText('ELOCMP.INPUT.CLEARTEXT')); element.attr('close-text', api.helpers.Text.getText('ELOCMP.INPUT.CLOSETEXT')); } parentCtrl.compile(element); }; } }; }]) /** * @class api.module.Components.directives.EloInput * @ngDirective * @xtype elo-input * * This directive renders an input field and provides functionality * for label, validation, keywordlists etc. * * # Example: * * <elo-input config="store.config" model="store.model"></elo-input> * * @cfg {Object} config * The configuration object. It defines the complete behavior of the input field. * The config model is final, after setting it changes to that object * will result in undefined behaviour. The object contains: * * @cfg {String} config.name * [Required] The name this input writes its value to in the model object. * @cfg {String} config.label * The label of the input field. * @cfg {String} config.placeholder * A placeholder text if the input field is empty. * @cfg {Boolean} config.hidden * true to hide this input field (invisible inputs). * @cfg {Boolean} config.disabled * true to diable the input field. * @cfg {Object} config.validation * The validation object, holding: * @cfg {Number} config.validation.maxLength * The maximal number of characters the field may contain. * @cfg {String} config.validation.maxLengthMsg * The message to show if maxLength is violated. * @cfg {Number} config.validation.minLength * The minimal number of characters the field needs. * @cfg {String} config.validation.minLengthMsg * The message to show if minLength is violated. * @cfg {Boolean} config.validation.required * true if this field may not be empty. * @cfg {String} config.validation.requiredMsg * The message to show if required is violated. * @cfg {String} config.validation.pattern * A regular expression string the input value must match. * @cfg {String} config.validation.patternMsg * The message to show if pattern is violated. * @cfg {Object} config.kwl * The configuration for a keyword list (typeahead). It contains: * @cfg {Function} config.kwl.getEntries * The function to obtain the keywords. This function gets called with * $scope and $q objects and should return a promise or an array of objects. * @cfg {String} config.kwl.valueKey * The key where to get the value from objects returned by getEntries * @cfg {String} config.kwl.viewKey * The key what should be displayed in the list from objects returned by getEntries * @cfg {Number} config.kwl.limit * The limit of entries shown in the keyword list. * @cfg {String} config.datePickerPopup * The date format used to render the selected date. (See angular ui datepicker) * If this is set no keywordlist is possible. The model will get the date object as value. * * @cfg {Object} model * The model the value of the input field will be written to. * The value is set to model[config.name] so the same model object * can be used for several input fields. * */ .directive('eloInput', ['$compile', function ($compile) { return { restrict: 'E', replace: true, controller: ['$scope', '$filter', '$q', '$templateCache', function ($scope, $filter, $q, $templateCache) { this.compile = function (element) { $compile(element)($scope); }; $scope.store = $scope.store || {}; if ($scope.config.kwl) { if ($scope.config.kwl.typeaheadMinLength === null || $scope.config.kwl.typeaheadMinLength === undefined) { $scope.config.kwl.typeaheadMinLength = 1; } $scope.typeaheadWaitMs = 200; $scope.typeaheadString = ['entry', ($scope.config.kwl.valueKey ? '.' : ''), $scope.config.kwl.valueKey, ' as entry', ($scope.config.kwl.viewKey ? '.' : ''), $scope.config.kwl.viewKey, ' for entry in internalGetEntries()'].join(''); } $scope.internalGetEntries = function () { var promise1 = $q.when($scope.config.kwl.getEntries($scope, $q, $templateCache)), promise2 = $q(function (resolve, reject) { promise1.then(function (entries) { var search = {}; // do not filter for both since name and id cannot be the same viewValue // if ($scope.config.kwl.valueKey) { // search[$scope.config.kwl.valueKey] = $scope.form[$scope.config.name].$viewValue; // } if ($scope.config.kwl.viewKey) { search[$scope.config.kwl.viewKey] = $scope.form[$scope.config.name].$viewValue; } entries = $filter('filter')(entries, search); entries = $filter('limitTo')(entries, $scope.config.kwl.limit); resolve(entries); return entries; }); }); return promise2; }; $scope.internalTriggerFct = function () { if (!$scope.config.triggerFct) { elo.helpers.Console.error("api.module.Components.eloInput: No trigger function given."); } else { $scope.config.triggerFct($scope); } }; $scope.getInvalidMsg = function(config, error) { if (!config || !config.validation || !error) { return undefined; } if (config.validation.maxLength && config.validation.maxLengthMsg && error.maxlength) { return config.validation.maxLengthMsg; } else if (config.validation.minLength && config.validation.minLengthMsg && error.minlength) { return config.validation.minLengthMsg; } else if (config.validation.required && config.validation.requiredMsg && error.required) { return config.validation.requiredMsg; } else if (config.validation.pattern && config.validation.patternMsg && error.pattern) { return config.validation.patternMsg; } else if (config.datePickerPopup && config.validation.dateMsg && error.date) { return config.validation.dateMsg; } else if (config.kwl && config.kwl.onlyFromKwl && error.editable) { return config.validation.editableMsg; } }; if ($scope.store.hasTrigger === undefined) { $scope.store.hasTrigger = $scope.config.triggerFct && !$scope.config.disabled; } // templateUrl if ($scope.config.kwl && $scope.config.kwl.popupUrl && $scope.config.kwl.popupUrl.indexOf('POPUP-DYNAMIC-KEYWORDLIST') === 0 && !$templateCache.get($scope.config.kwl.popupUrl)) { var sb = new api.helpers.StringBuilder(); sb.append("<ul class=\"container dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">"); sb.append("<div class=\"elo-input-dynkwl-header row\" id=\"").append($scope.config.kwl.popupUrl).append("\" elo-template></div>"); sb.append("<li role=\"presentation\" class=\"divider\"></li>"); sb.append("<li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">"); sb.append("<div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>"); sb.append("</li>"); sb.append("</ul>"); $templateCache.put($scope.config.kwl.popupUrl, sb.toString()); } }], link: function ($scope, elem, attr, formCtrl) { $scope.form = $scope[$scope.config.name + 'Form']; $scope.$watch('form.$error', function(error) { if ($scope.config.focused && !angular.equals({}, $scope.form.$error)) { $scope.store.popover = $scope.getInvalidMsg($scope.config, $scope.form.$error); $scope.store.popoverIsOpen = true; } else { $scope.store.popoverIsOpen = false; } }); $scope.$watch('config.focused', function(focused) { if ($scope.config.focused && !angular.equals({}, $scope.form.$error)) { $scope.store.popover = $scope.getInvalidMsg($scope.config, $scope.form.$error); $scope.store.popoverIsOpen = true; } else { $scope.store.popoverIsOpen = false; } }); setTimeout(function () { $scope.inputEl = elem.find('input')[0]; $scope.$apply(); }, 100); }, scope: { config: '=', model: '=' }, templateUrl: api.module.Components.relativeUrl + 'directives/input/input.html' }; }]); }());