mirror of
https://github.com/ansible/awx.git
synced 2026-05-16 05:47:38 -02:30
Smart Search - support array of values per key (#4187)
* Smart Search - support array of values per key, #4162 * lint
This commit is contained in:
@@ -249,7 +249,7 @@ var tower = angular.module('Tower', [
|
|||||||
},
|
},
|
||||||
// decoding
|
// decoding
|
||||||
// from "_search=operator:key:compator=value& ... "
|
// from "_search=operator:key:compator=value& ... "
|
||||||
// to "_search=operator:key:compator=value& ... "
|
// to {operator__key1__comparator=value, ... }
|
||||||
decode: function(item) {
|
decode: function(item) {
|
||||||
return QuerySet.$get().decodeArr(item);
|
return QuerySet.$get().decodeArr(item);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -51,10 +51,19 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
|||||||
encodeQueryset(params) {
|
encodeQueryset(params) {
|
||||||
let queryset;
|
let queryset;
|
||||||
queryset = _.reduce(params, (result, value, key) => {
|
queryset = _.reduce(params, (result, value, key) => {
|
||||||
return result + `${key}=${value}&`;
|
return result + encodeTerm(value, key);
|
||||||
}, '');
|
}, '');
|
||||||
queryset = queryset.substring(0, queryset.length - 1);
|
queryset = queryset.substring(0, queryset.length - 1);
|
||||||
return angular.isObject(params) ? `?${queryset}` : '';
|
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
|
// encodes a ui smart-search param to a django-friendly param
|
||||||
// operand:key:comparator:value => {operand__key__comparator: value}
|
// operand:key:comparator:value => {operand__key__comparator: value}
|
||||||
@@ -62,19 +71,42 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
|||||||
let split = param.split(':');
|
let split = param.split(':');
|
||||||
return {[split.slice(0,split.length -1).join('__')] : split[split.length-1]};
|
return {[split.slice(0,split.length -1).join('__')] : split[split.length-1]};
|
||||||
},
|
},
|
||||||
// decodes a django queryset param into ui smart-search param
|
// decodes a django queryset param into a ui smart-search tag or set of tags
|
||||||
decodeParam(key, value){
|
decodeParam(value, key){
|
||||||
return `${key.split('__').join(':')}:${value}`;
|
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
|
// 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) {
|
encodeArr(params) {
|
||||||
let url;
|
let url;
|
||||||
url = _.reduce(params, (result, value, key) => {
|
url = _.reduce(params, (result, value, key) => {
|
||||||
return result.concat(`${key}:${value}`);
|
return result.concat(encodeUrlString(value, key));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return url.join(';');
|
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
|
// 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) => {
|
_.forEach(arr.split(';'), (item) => {
|
||||||
let key = item.split(':')[0],
|
let key = item.split(':')[0],
|
||||||
value = item.split(':')[1];
|
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;
|
return params;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
|
|||||||
|
|
||||||
// Removes state definition defaults and pagination terms
|
// Removes state definition defaults and pagination terms
|
||||||
function stripDefaultParams(params) {
|
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
|
// 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 defaults[key] !== value && key !== 'page' && key !== 'page_size' && defaults[key] !== null;
|
||||||
});
|
});
|
||||||
|
return _(stripped).map(qs.decodeParam).flatten().value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// searchable relationships
|
// searchable relationships
|
||||||
@@ -53,8 +54,16 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
|
|||||||
};
|
};
|
||||||
|
|
||||||
// remove tag, merge new queryset, $state.go
|
// remove tag, merge new queryset, $state.go
|
||||||
$scope.remove = function(key) {
|
$scope.remove = function(index) {
|
||||||
delete queryset[key];
|
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('.', {
|
$state.go('.', {
|
||||||
[$scope.iterator + '_search']: queryset });
|
[$scope.iterator + '_search']: queryset });
|
||||||
qs.search(path, queryset).then((res) => {
|
qs.search(path, queryset).then((res) => {
|
||||||
@@ -91,7 +100,16 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
|
|||||||
}
|
}
|
||||||
|
|
||||||
params.page = '1';
|
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
|
// https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic
|
||||||
// This transition will not reload controllers/resolves/views
|
// This transition will not reload controllers/resolves/views
|
||||||
// but will register new $stateParams[$scope.iterator + '_search'] terms
|
// but will register new $stateParams[$scope.iterator + '_search'] terms
|
||||||
@@ -105,9 +123,5 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', '
|
|||||||
$scope.searchTerm = null;
|
$scope.searchTerm = null;
|
||||||
$scope.searchTags = stripDefaultParams(queryset);
|
$scope.searchTags = stripDefaultParams(queryset);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.decodeParam = function(key, value) {
|
|
||||||
return qs.decodeParam(key, value);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -19,12 +19,12 @@
|
|||||||
<div class="SmartSearch-tags">
|
<div class="SmartSearch-tags">
|
||||||
<div class="SmartSearch-tagSection">
|
<div class="SmartSearch-tagSection">
|
||||||
<div class="SmartSearch-flexContainer">
|
<div class="SmartSearch-flexContainer">
|
||||||
<div class="SmartSearch-tagContainer" ng-repeat="(key, value) in searchTags track by $index">
|
<div class="SmartSearch-tagContainer" ng-repeat="tag in searchTags track by $index">
|
||||||
<div class="SmartSearch-deleteContainer" ng-click="remove(key)">
|
<div class="SmartSearch-deleteContainer" ng-click="remove($index)">
|
||||||
<i class="fa fa-times SmartSearch-tagDelete"></i>
|
<i class="fa fa-times SmartSearch-tagDelete"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="SmartSearch-tag SmartSearch-tag--deletable">
|
<div class="SmartSearch-tag SmartSearch-tag--deletable">
|
||||||
<span class="SmartSearch-name">{{decodeParam(key, value)}}</span>
|
<span class="SmartSearch-name">{{tag}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href class="SmartSearch-clearAll" ng-click="clearAll()" ng-show="!(searchTags | isEmpty)">CLEAR ALL</a>
|
<a href class="SmartSearch-clearAll" ng-click="clearAll()" ng-show="!(searchTags | isEmpty)">CLEAR ALL</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user