gumga-query-filter-ng
Version:
Gumga Query Filter
340 lines (281 loc) • 14.4 kB
JavaScript
// (function(){
//Description
Search.$inject = ['$q','$timeout','$compile','$interpolate', '$rootScope' ]
function Search($q, $timeout, $compile, $interpolate, $rootScope){
let template = `
<style>
gumga-query .gumga-date {
left:0 !important;
top:35px;
}
</style>
<div class="input-group">
<input type="text" placeholder="Busque seus filtros salvos" class="form-control" ng-model="ctrl.searchField" ng-keyup="ctrl.doSearch(ctrl.searchField, $event, 'TYPEAHEAD')" uib-typeahead="item.description for item in ctrl.proxyFn($viewValue)" typeahead-on-select="ctrl.filterSelect($item, $model, $label, $event)" ng-show="ctrl.hasQuerySaved && openFilter"/>
<input type="number" ng-if="ctrl.getInputType() == 'number'" ng-disabled="ctrl.getActivesFields().length == 0 || openFilter" class="form-control" ng-model="ctrl.searchField" ng-keyup="ctrl.doSearch(ctrl.searchField, $event)" ng-show="!ctrl.hasQuerySaved || !openFilter" />
<input type="text" ng-if="ctrl.getInputType() == 'text'" ng-disabled="ctrl.getActivesFields().length == 0" class="form-control" ng-model="ctrl.searchField" ng-keyup="ctrl.doSearch(ctrl.searchField, $event)" ng-show="!ctrl.hasQuerySaved || !openFilter" />
<input type="date" ng-if="ctrl.getInputType() == 'date' && !ctrl.useGumgaDate()" ng-disabled="ctrl.getActivesFields().length == 0" class="form-control" ng-model="ctrl.searchField" ng-keyup="ctrl.doSearch(ctrl.searchField, $event)" ng-show="!ctrl.hasQuerySaved || !openFilter" />
<gumga-date ng-if="ctrl.getInputType() == 'date' && ctrl.useGumgaDate()" ng-model="ctrl.searchField" ng-disabled="ctrl.getActivesFields().length == 0" ng-keyup="ctrl.doSearch(ctrl.searchField, $event)" ng-show="!ctrl.hasQuerySaved || !openFilter"></gumga-date>
<span class="input-group-btn" uib-dropdown uib-keyboard-nav auto-close="outsideClick">
<button class="btn btn-default" type="button" uib-dropdown-toggle>
<span class="glyphicon glyphicon-chevron-down"><span>
</button>
<ul uib-dropdown-menu role="menu" aria-labelledby="single-button" class="dropdown-menu-search">
<li role="menuitem" ng-repeat="(key, $value) in ctrl.mapFields" style="{{ctrl.isDisabled($value.field) ? 'opacity: 0.3;' : ''}}">
<a class="no-padding-search-fields">
<label ng-click="ctrl.checkFields($event, $value.field)">
<input type="checkbox" ng-model="$value.checkbox" style="pointer-events: none;"/>
{{::$value.label}}
</label>
</a>
</li>
</ul>
<button class="btn btn-default" ng-click="openFilter = !openFilter" type="button">
<span class="glyphicon glyphicon-filter"></span>
</button>
<button class="btn btn-primary" type="button" ng-click="ctrl.doSearch(ctrl.searchField)" ng-disabled="openFilter">
<span> {{::ctrl.searchText}} </span>
<span class="glyphicon glyphicon-search rotate-search-glyph"></span>
</button>
</span>
</div>
<div class="row replace-filter">
<div class="col-md-12">
<div id="replaceFilter"></div>
</div>
</div>`
controller.$inject = ['$scope', '$element', '$attrs', '$transclude']
function controller($scope, $element, $attrs, $transclude){
let ctrl = this
const hasAttr = string => (!!$attrs[string]),
FIELD_ERR = 'É necessário um parâmetro field na tag search-field.[<search-field field="foo"></search-field>]',
SEARCH_ERR = 'É necessário passar uma função para o atributo "search". [search="foo(field, param)"]'
ctrl.mapFields = {}
ctrl.possibleAdvancedFields = []
const SOURCE_CHARS = "'âàãáÁÂÀÃéêÉÊíÍóôõÓÔÕüúÜÚÇç'";
const TARGET_CHARS = "'AAAAAAAAEEEEIIOOOOOOUUUUCC'";
ctrl.uid = guid();
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
if(!hasAttr('search')) console.error(SEARCH_ERR)
$transclude((transcludeElement) => {
let alreadySelected = false,
parentContext = $scope.$parent;
[].slice.call(transcludeElement).forEach(value => {
if(value && value.nodeName === 'ADVANCED-SEARCH-FIELD') ctrl.possibleAdvancedFields.push(value.outerHTML)
if(!value || value.nodeName !== 'SEARCH-FIELD') return
let element = angular.element(value),
field = element.attr('field') ? element.attr('field') : '',
type = element.attr('type') ? element.attr('type') : 'string',
checkbox = !!$scope.$eval(element.attr('select')),
label = element.attr('label') ? $interpolate(element.attr('label'))(parentContext) : field.charAt(0).toUpperCase().concat(field.slice(1)),
innerJoin = element.attr('inner-join') ? element.attr('inner-join').split(',') : [],
leftJoin = element.attr('left-join') ? element.attr('left-join').split(',') : [],
ignoreCase = element.attr('ignore-case') == "false" ? false : true,
translate = element.attr('translate') == "false" ? false : true;
if(!field) console.error(FIELD_ERR)
if(checkbox) alreadySelected = true
ctrl.mapFields[field] = { checkbox, label, field, type, innerJoin, leftJoin, ignoreCase, translate }
})
if(!alreadySelected){
for(var first in ctrl.mapFields) break
if(first) ctrl.mapFields[first].checkbox = true
}
})
ctrl.$onInit = () => {
ctrl.compileFilter = compileFilter
ctrl.doSearch = doSearch
ctrl.proxyFn = proxyFn
ctrl.filterSelect = filterSelect
ctrl.advancedSearch = hasAttr('advancedSearch') ? ctrl.advancedSearch : null
ctrl.containerAdvanced = hasAttr('containerAdvanced') ? ctrl.containerAdvanced : "replaceFilter"
ctrl.savedFilters = hasAttr('savedFilters') ? ctrl.savedFilters : angular.noop
ctrl.searchText = hasAttr('searchText') ? $attrs['searchText'] : ' '
ctrl.proxySearch = (param) => ctrl.advancedSearch({ param })
ctrl.hasQuerySaved = !!$attrs.savedFilters
$scope.proxySave = (query, name) => ctrl.saveQuery({ query, name })
if(ctrl.advancedSearch) ctrl.compileFilter()
}
function compileFilter(){
let template = `<gumga-filter-core uid="${ctrl.uid}" ng-show="openFilter" use-gquery="${$attrs.useGquery}" is-open="true" search="ctrl.proxySearch(param)" ${$attrs.saveQuery ? 'save-query="saveQuery(query, name)"' : ''}is-query="true">${ctrl.possibleAdvancedFields.reduce(((prev, next) => prev += next), '')}</gumga-filter-core>`,
element = angular.element($element.find('#'+ctrl.containerAdvanced));
element.replaceWith($compile(template)($scope));
}
const getTypeInputByTypeField = (type) => {
switch(type){
case 'string':
return 'text';
case 'number':
return 'number';
case 'date':
return 'date';
default:
return 'text';
}
}
ctrl.getInputType = () => {
let selecteds = Object
.keys(ctrl.mapFields)
.filter(value => !!ctrl.mapFields[value].checkbox);
if(selecteds.length == 0) ctrl.searchSimpleType = 'text';
let type = ctrl.mapFields[selecteds[0]] && ctrl.mapFields[selecteds[0]].type ? ctrl.mapFields[selecteds[0]].type : 'string';
ctrl.searchSimpleType = getTypeInputByTypeField(type);
return ctrl.searchSimpleType;
}
function doSearch(param, event = { keyCode: 13 }, inputType){
if(event.keyCode !== 13 || inputType == 'TYPEAHEAD') return;
let result = Object
.keys(ctrl.mapFields)
.filter(value => !!ctrl.mapFields[value].checkbox)
.reduce((prev, next) => (prev += next.concat(',')), '')
.slice(0, -1);
if(result.length === 0) return;
if(ctrl.useGquery){
let query = new GQuery();
result = result.split(',');
let innerJoins = [];
let leftJoins = [];
let selecteds = Object
.keys(ctrl.mapFields)
.filter(value => !!ctrl.mapFields[value].checkbox);
selecteds.map(key => {
return ctrl.mapFields[key];
}).forEach(field => {
if (field.innerJoin) {
field.innerJoin.forEach(innerJoin => {
if(innerJoins.indexOf(innerJoin) == -1){
innerJoins.push(innerJoin);
}
})
}
if (field.leftJoin) {
field.leftJoin.forEach(leftJoin => {
if(leftJoins.indexOf(leftJoin) == -1){
leftJoins.push(leftJoin);
}
})
}
})
result.forEach((field, index) => {
let criteria = null;
if(param != undefined && param != null){
criteria = new Criteria(field, getComparisonOperatorByType(field), param == undefined || param == null ? '' : param);
}
if(ctrl.mapFields[field].type == 'number' && param != undefined && param != null) {
criteria = new Criteria(field, getComparisonOperatorByType(field), param == undefined || param == null ? 0 : Number(param));
} else if(ctrl.mapFields[field].type == 'date' && param) {
criteria = new Criteria(field, getComparisonOperatorByType(field), param == undefined || param == null ? new Date() : new Date(param));
} else if(ctrl.mapFields[field].type == 'string' && criteria) {
criteria.setFieldFunction('%s');
criteria.setValueFunction('%s');
if(ctrl.mapFields[field].ignoreCase) {
criteria.setFieldFunction(addIgnoreCase(criteria.getFieldFunction()));
criteria.setValueFunction(addIgnoreCase(criteria.getValueFunction()));
}
if(ctrl.mapFields[field].translate) {
criteria.setFieldFunction(addTranslate(criteria.getFieldFunction()));
criteria.setValueFunction(addTranslate(criteria.getValueFunction()));
}
}
if(index == 0){
query = new GQuery(criteria);
}else{
query = query.or(criteria);
}
});
innerJoins.forEach(function(innerJoin) {
query.join(new Join(innerJoin, JoinType.INNER))
})
leftJoins.forEach(function(leftJoin) {
query.join(new Join(leftJoin, JoinType.LEFT))
})
if($attrs.lastGquery){
ctrl.lastGquery = angular.copy(query);
}
ctrl.search({ param: query });
}else{
ctrl.search({ field: result, param: param })
}
}
function addIgnoreCase(value) {
return value.replace(/%s/g, "lower(%s)");
}
function addTranslate(value) {
return value.replace(/%s/g, "translate(%s, "+SOURCE_CHARS+","+TARGET_CHARS+")");
}
function getComparisonOperatorByType(field){
if(ctrl.mapFields[field].type == 'string'){
return ComparisonOperator.CONTAINS;
}
return ComparisonOperator.EQUAL;
}
$scope.$watch('openFilter', (open) => {
if(open != undefined) $rootScope.$broadcast('openOrCloseFilter'+ctrl.uid, open);
})
ctrl.getActivesFields = () => {
return Object.keys(ctrl.mapFields).filter(value => !!ctrl.mapFields[value].checkbox);
}
ctrl.isDisabled = (field) => {
let someChecked = ctrl.getActivesFields();
if(ctrl.mapFields[field] && ctrl.mapFields[field].type){
let differentKinds = someChecked.filter(some => ctrl.mapFields[some].type != ctrl.mapFields[field].type);
return differentKinds.length > 0;
}
}
ctrl.checkFields = (event, field) => {
let someChecked = ctrl.getActivesFields();
// if ((someChecked.length == 1 && someChecked[0] == field) || Object.keys(ctrl.mapFields).length == 1) {
// event.preventDefault();
// }
if(ctrl.mapFields[field] && ctrl.mapFields[field].type && getTypeInputByTypeField(ctrl.mapFields[field].type) != ctrl.searchSimpleType){
delete ctrl.searchField;
}
if(ctrl.mapFields[field] && ctrl.mapFields[field].type){
let differentKinds = someChecked.filter(some => ctrl.mapFields[some].type != ctrl.mapFields[field].type);
if(differentKinds.length > 0){
ctrl.mapFields[field].checkbox = false;
}
}
event.stopPropagation();
}
ctrl.useGumgaDate = function() {
try {
return false// !!angular.module('gumga.date');
} catch (error) {
return false;
}
}
function proxyFn($value){
return $q.when(ctrl.savedFilters({ page: location.hash }))
}
function filterSelect($item, $model, $label, $event){
$timeout(() => (ctrl.searchField= '', $scope.$broadcast('filter-items', $item)))
}
}
return {
restrict: 'E',
scope: {
search: '&',
advancedSearch: '&?',
containerAdvanced: '@?',
savedFilters: '&?',
saveQuery:'&?',
useGquery:'=?',
lastGquery:'=?'
},
bindToController: true,
transclude: true,
controllerAs: 'ctrl',
controller,
template
}
}
angular.module('gumga.queryfilter.query', [])
.directive('gumgaQuery', Search)
// })()