1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 08:21:15 +03:00

Smart Search - support array of values per key (#4187)

* Smart Search - support array of values per key, #4162

* lint
This commit is contained in:
Leigh Johnson 2016-12-01 17:55:53 -05:00 committed by GitHub
parent 6022668706
commit 1dab4f903c
4 changed files with 73 additions and 19 deletions

View File

@ -249,7 +249,7 @@ var tower = angular.module('Tower', [
},
// decoding
// from "_search=operator:key:compator=value& ... "
// to "_search=operator:key:compator=value& ... "
// to {operator__key1__comparator=value, ... }
decode: function(item) {
return QuerySet.$get().decodeArr(item);
},

View File

@ -51,10 +51,19 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
encodeQueryset(params) {
let queryset;
queryset = _.reduce(params, (result, value, key) => {
return result + `${key}=${value}&`;
return result + encodeTerm(value, key);
}, '');
queryset = queryset.substring(0, queryset.length - 1);
return angular.isObject(params) ? `?${queryset}` : '';
function encodeTerm(value, key){
if (Array.isArray(value)){
return _.map(value, (item) => `${key}=${item}`).join('&') + '&';
}
else {
return `${key}=${value}&`;
}
}
},
// encodes a ui smart-search param to a django-friendly param
// operand:key:comparator:value => {operand__key__comparator: value}
@ -62,19 +71,42 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
let split = param.split(':');
return {[split.slice(0,split.length -1).join('__')] : split[split.length-1]};
},
// decodes a django queryset param into ui smart-search param
decodeParam(key, value){
return `${key.split('__').join(':')}:${value}`;
// decodes a django queryset param into a ui smart-search tag or set of tags
decodeParam(value, key){
if (Array.isArray(value)){
return _.map(value, (item) => {
return `${key.split('__').join(':')}:${item}`;
});
}
else {
return `${key.split('__').join(':')}:${value}`;
}
},
// encodes a django queryset for ui-router's URLMatcherFactory
// {operand__key__comparator: value, } => 'operand:key:comparator:value,...'
// {operand__key__comparator: value, } => 'operand:key:comparator:value;...'
// value.isArray expands to:
// {operand__key__comparator: [value1, value2], } => 'operand:key:comparator:value1;operand:key:comparator:value1...'
encodeArr(params) {
let url;
url = _.reduce(params, (result, value, key) => {
return result.concat(`${key}:${value}`);
return result.concat(encodeUrlString(value, key));
}, []);
return url.join(';');
// {key:'value'} => 'key:value'
// {key: [value1, value2, ...]} => ['key:value1', 'key:value2']
function encodeUrlString(value, key){
if (Array.isArray(value)){
return _.map(value, (item) => {
return `${key}:${item}`;
});
}
else {
return `${key}:${value}`;
}
}
},
// decodes a django queryset for ui-router's URLMatcherFactory
@ -84,7 +116,15 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
_.forEach(arr.split(';'), (item) => {
let key = item.split(':')[0],
value = item.split(':')[1];
params[key] = value;
if(!params[key]){
params[key] = value;
}
else if (Array.isArray(params[key])){
params[key].push(value);
}
else {
params[key] = [params[key], value];
}
});
return params;
},

View File

@ -22,10 +22,11 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
// Removes state definition defaults and pagination terms
function stripDefaultParams(params) {
return _.pick(params, (value, key) => {
let stripped =_.pick(params, (value, key) => {
// setting the default value of a term to null in a state definition is a very explicit way to ensure it will NEVER generate a search tag, even with a non-default value
return defaults[key] !== value && key !== 'page' && key !== 'page_size' && defaults[key] !== null;
});
return _(stripped).map(qs.decodeParam).flatten().value();
}
// searchable relationships
@ -53,8 +54,16 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
};
// remove tag, merge new queryset, $state.go
$scope.remove = function(key) {
delete queryset[key];
$scope.remove = function(index) {
let removed = qs.encodeParam($scope.searchTags.splice(index, 1)[0]);
_.each(removed, (value, key) => {
if (Array.isArray(queryset[key])){
_.remove(queryset[key], (item) => item === value);
}
else {
delete queryset[key];
}
});
$state.go('.', {
[$scope.iterator + '_search']: queryset });
qs.search(path, queryset).then((res) => {
@ -91,7 +100,16 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
}
params.page = '1';
queryset = _.merge(queryset, params);
queryset = _.merge(queryset, params, (objectValue, sourceValue, key, object) => {
if (object[key] && object[key] !== sourceValue){
return [object[key], sourceValue];
}
else {
// // https://lodash.com/docs/3.10.1#merge
// If customizer fn returns undefined merging is handled by default _.merge algorithm
return undefined;
}
});
// https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic
// This transition will not reload controllers/resolves/views
// but will register new $stateParams[$scope.iterator + '_search'] terms
@ -105,9 +123,5 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
$scope.searchTerm = null;
$scope.searchTags = stripDefaultParams(queryset);
};
$scope.decodeParam = function(key, value) {
return qs.decodeParam(key, value);
};
}
];

View File

@ -19,12 +19,12 @@
<div class="SmartSearch-tags">
<div class="SmartSearch-tagSection">
<div class="SmartSearch-flexContainer">
<div class="SmartSearch-tagContainer" ng-repeat="(key, value) in searchTags track by $index">
<div class="SmartSearch-deleteContainer" ng-click="remove(key)">
<div class="SmartSearch-tagContainer" ng-repeat="tag in searchTags track by $index">
<div class="SmartSearch-deleteContainer" ng-click="remove($index)">
<i class="fa fa-times SmartSearch-tagDelete"></i>
</div>
<div class="SmartSearch-tag SmartSearch-tag--deletable">
<span class="SmartSearch-name">{{decodeParam(key, value)}}</span>
<span class="SmartSearch-name">{{tag}}</span>
</div>
</div>
<a href class="SmartSearch-clearAll" ng-click="clearAll()" ng-show="!(searchTags | isEmpty)">CLEAR ALL</a>