Join our webinar on Wednesday, June 26, at 1pm EDTHow Chia Mitigates Risk in the Crypto Industry.Register
Socket
Socket
Sign inDemoInstall

@momsfriendlydevco/angular-ui-query-builder

Package Overview
Dependencies
5
Maintainers
3
Versions
33
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.8 to 1.2.0

dist/angular-ui-query-builder-core.css

275

dist/angular-ui-query-builder.js

@@ -13,3 +13,3 @@ 'use strict';

},
template: '\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\t\t</div>\n\t',
template: '\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\n\t\t\t<!-- Meta field: sort {{{ -->\n\t\t\t<div class="query-container">\n\t\t\t\t<!-- Root branch display {{{ -->\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<!-- }}} -->\n\t\t\t\t<!-- Path component {{{ -->\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tSort by\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!-- }}} -->\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<!-- Query operand component {{{ -->\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.sort" type="text" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!-- }}} -->\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Meta field: limit {{{ -->\n\t\t\t<div class="query-container">\n\t\t\t\t<!-- Root branch display {{{ -->\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<!-- }}} -->\n\t\t\t\t<!-- Path component {{{ -->\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tLimited to\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!-- }}} -->\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<!-- Query operand component {{{ -->\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.limit" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-1">\n\t\t\t\t\t\tSkipping\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.skip" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!-- }}} -->\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\n\t\t</div>\n\t',
controller: ['$scope', function controller($scope) {

@@ -47,3 +47,3 @@ var $ctrl = this;

},
template: '\n\t\t<div ng-repeat="leaf in $ctrl.properties track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t<!-- Root branch display {{{ -->\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Path component {{{ -->\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t<!-- Query type component {{{ -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">After</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Before</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t<!-- Query operand component {{{ -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t</div>\n\t\t<!-- Add button {{{ -->\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t<!-- }}} -->\n\t',
template: '\n\t\t<div ng-repeat="leaf in $ctrl.properties | filter:{isMeta:false} track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<!-- Path component {{{ -->\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t<!-- Query type component {{{ -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Is after</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gte\')">Is at least</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Is before</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lte\')">Is at most</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t<!-- Query operand component {{{ -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="date" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="date" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t</div>\n\t\t<!-- Add button {{{ -->\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t<!-- }}} -->\n\t',
controller: ['$element', '$scope', function controller($element, $scope) {

@@ -92,2 +92,6 @@ var $ctrl = this;

textFalse: 'Disabled'
},
date: {
title: 'Is exactly',
type: 'date'
}

@@ -100,3 +104,3 @@ }, {

export: function _export(leaf) {
return leaf.valueEdit;
return { $ne: leaf.valueEdit };
},

@@ -112,2 +116,6 @@ base: {

textFalse: 'Disabled'
},
date: {
title: 'Is not exactly',
type: 'date'
}

@@ -149,2 +157,6 @@ }, {

type: 'number'
},
date: {
title: 'Is after',
type: 'date'
}

@@ -162,2 +174,6 @@ }, {

type: 'number'
},
date: {
title: 'Is at least',
type: 'date'
}

@@ -175,2 +191,6 @@ }, {

type: 'number'
},
date: {
title: 'Is before',
type: 'date'
}

@@ -188,2 +208,6 @@ }, {

type: 'number'
},
date: {
title: 'Is at most',
type: 'date'
}

@@ -202,3 +226,3 @@ }, {

textTrue: 'Has a value',
textFalse: 'Has no value'
textFalse: 'Has a value' // This isn't technically right but its right next to a disabled checkbox so it makes sense in context
}

@@ -249,16 +273,11 @@ }, {

return _($ctrl.branch).map(function (v, k) {
var wrappingKey = _.isObject(v) ? _(v).keys().first() : '$eq';
var firstKeyVal = _.isObject(v) && _.size(v) > 0 ? _(v).map().first() : undefined;
var newBranch = {
return {
id: k,
value: v,
valueEdit: firstKeyVal || v,
valueOperand: wrappingKey,
isMeta: k.startsWith('$'),
valueEdit: $ctrl.getFlatValue(v),
valueOperand: _.isObject(v) ? _(v).keys().first() : '$eq',
isMeta: k.startsWith('$') || ['sort', 'skip', 'limit'].includes(k),
spec: $ctrl.getSpec(k, v, k),
path: pathSegments.concat([k])
};
return newBranch;
}).sortBy(function (p) {

@@ -305,15 +324,37 @@ return p.isMeta ? 'Z' + p.id : 'A' + p.id;

$ctrl.setWrapper = function (leaf, type) {
var newValue = {};
if (_.isObject(leaf.value) && _.size(leaf.value) == 1) {
// Unwrap object value
newValue[type] = _(leaf.value).values().first();
if (leaf.valueOperand == '$eq' && type == '$ne') {
// Negate
leaf.valueOperand = '$ne';
leaf.valueEdit = $ctrl.getFlatValue(leaf.value);
leaf.value = { $ne: leaf.valueEdit };
} else if (leaf.valueOperand == '$ne' && type == '$eq') {
leaf.valueOperand = '$eq';
leaf.valueEdit = $ctrl.getFlatValue(leaf.value);
leaf.value = { $eq: leaf.valueEdit };
} else if (leaf.valueOperand == '$in' && type == '$eq') {
// Flatten array into scalar
leaf.valueOperand = '$eq';
leaf.value = leaf.valueEdit = $ctrl.getFlatValue(leaf.value);
} else if ((leaf.valueOperand == '$eq' || leaf.valueOperand === undefined) && type == '$in') {
// Roll scalar into array
leaf.valueOperand = '$in';
leaf.valueEdit = $ctrl.getFlatValue(leaf.value);
leaf.value = { $in: [leaf.valueEdit] };
} else if (type == '$exists') {
// Convert anything to exists - force it to be a boolean
leaf.valueOperand = '$exists';
leaf.valueEdit = true;
leaf.value = { $exists: leaf.valueEdit };
} else {
// Preseve value
newValue[type] = leaf.valueEdit;
// Unknown swapping - convert to an object with one key
console.log('UNHANDLED TYPE CONVERT:', leaf.type, '=>', type);
var newValue = $ctrl.getFlatValue(leaf.value);
leaf.valueOperand = type;
leaf.valueEdit = newValue;
leaf.value = _defineProperty({}, leaf.valueOperand, leaf.valueEdit);
}
leaf.valueOperand = type;
leaf.value = newValue;
leaf.valueEdit = _.isObject(newValue[type]) && _.size(newValue[type]) ? newValue[type] : newValue;
$ctrl.setValue(leaf);
// Set the upstream model value
$ctrl.exportBranch();
};

@@ -331,3 +372,3 @@

leaf.value = $ctrl.operandsByID[leaf.valueOperand].setter(newValue);
leaf.valueEdit = _.isObject(leaf.value) && _.size(leaf.value) ? _(leaf.value).map().first() : leaf.value;
leaf.valueEdit = $ctrl.getFlatValue(leaf.value);

@@ -363,2 +404,29 @@ // Set the upstream model value

};
/**
* Return the 'flat' value of a Mongo expression
* This will always return the closest thing we have to a scalar primative
* @param {Object|string} input The input expression to flatten
* @returns {string|number} The nearest thing we can evaluate to a primative (or an empty string)
*
* @example
* $ctrl.getFlatValue('foo') //= 'foo'
* @example
* $ctrl.getFlatValue({$eq: 'bar'}) //= 'bar'
* @example
* $ctrl.getFlatValue({$in: ['quz', 'qux']}) //= 'quz'
*/
$ctrl.getFlatValue = function (input) {
if (_.isString(input) || _.isNumber(input) || _.isBoolean(input) || _.isDate(input)) {
// Already a primative
return input;
} else if (_.isObject(input) && _.size(input) == 1) {
// Unwrap object value from object
return _(input).values().first();
} else {
// No idea how to convert - just return an empty string
console.warn('Given up trying to flatten input value', input);
return input;
}
};
// }}}

@@ -425,2 +493,161 @@

}]);
// }}}
angular.module('angular-ui-query-builder')
// Table decorator {{{
.service('qbTableSettings', function () {
return {
icons: {
sortNone: 'fa fa-fw fa-sort text-muted',
sortAsc: 'fa fa-fw fa-sort-alpha-asc text-primary',
sortDesc: 'fa fa-fw fa-sort-alpha-desc text-primary'
}
};
})
/**
* Directive applied to a table element to indicate that we should manage that table via angular-ui-query
* @param {Object} qbTable The query object to modify
* @emits qbTableQueryChange Emitted to child elements as (e, query) when the query object changes
*/
.directive('qbTable', function () {
return {
scope: {
qbTable: '='
},
restrict: 'AC',
controller: ['$scope', 'qbTableSettings', function controller($scope, qbTableSettings) {
var $ctrl = this;
$ctrl.query = $scope.qbTable; // Copy into $ctrl so children can access it / $watch it
$ctrl.setField = function (field, value) {
if (value == undefined) {
// Remove from query
delete $ctrl.query[field];
return;
}
switch (field) {
case 'sort':
if ($ctrl.query.sort === value) {
// If already sorting by field switch the sort direction
$ctrl.query.sort = '-' + value;
} else if ($ctrl.query.sort === '-' + value) {
// If reverse sorting switch the right way up again
$ctrl.query.sort = value;
} else {
// Just set the sorting
$ctrl.query.sort = value;
}
break;
default:
$scope.qbTable[field] = value;
}
};
}]
};
})
/**
* Directive for header elements to add angular-ui-query functionality
* @param {Object} ^qbTable.qbTable The query Object to mutate
* @param {string} qbCol The field to operate on
* @param {string} [sortable=q] Indicates that the column should switch to being sorted if the user clicks on it, if a value is specified that is used instead of `q` as the sort field
*/
.directive('qbCol', function () {
return {
scope: {
qbCol: '@', // The field to operate on
sortable: '@'
},
require: '^qbTable',
restrict: 'A',
transclude: true,
controller: ['$attrs', '$scope', 'qbTableSettings', function controller($attrs, $scope, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
// Sort functionality {{{
$scope.canSort = false; // True if either sortable has a specific value or is at least present
$scope.isSorted = false; // False, 'asc', 'desc'
$ctrl.$onInit = function () {
$scope.canSort = $scope.sortable || $attrs.sortable === '';
};
$scope.$watch('qbTable.query.sort', function (sorter) {
var sortField = $scope.sortable || $scope.q;
if (!sorter) {
$scope.isSorted = false;
} else if (angular.isArray(sorter) && sorter.some(function (i) {
return i == sortField;
}) || sorter == sortField) {
$scope.isSorted = 'asc';
} else if (angular.isArray(sorter) && sorter.some(function (i) {
return i == '-' + sortField;
}) || sorter == '-' + sortField) {
$scope.isSorted = 'desc';
} else {
$scope.isSorted = false;
}
});
$scope.toggleSort = function () {
if ($scope.sortable) {
$scope.qbTable.setField('sort', $scope.sortable);
} else if ($scope.q && $attrs.sortable === '') {
// Has attribute but no value - assume main key if we have one
$scope.qbTable.setField('sort', $scope.q);
}
};
// }}}
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: '\n\t\t<ng-transclude></ng-transclude>\n\t\t<a ng-if="canSort" ng-click="toggleSort()" class="pull-right">\n\t\t\t<i class="{{\n\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t}}"></i>\n\t\t</a>\n\t'
};
})
/**
* Directive to add table pagination
* @param {Object} ^qbTable.qbTable The query Object to mutate
*/
.directive('qbPagination', function () {
return {
scope: {},
require: '^qbTable',
restrict: 'EA',
controller: ['$attrs', '$scope', 'qbTableSettings', function controller($attrs, $scope, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
$scope.canPrev = true;
$scope.canNext = true;
$scope.$watchGroup(['qbTable.query.limit', 'qbTable.query.skip'], function (sorter) {
$scope.canPrev = $scope.qbTable.query.skip > 0;
$scope.canNext = !$scope.total || $scope.qbTable.query.skip + $scope.qbTable.query.limit < $scope.total;
});
$scope.navPageRelative = function (pageRelative) {
if (pageRelative == -1) {
$scope.qbTable.setField('skip', Math.min(($scope.qbTable.query.skip || 0) - ($scope.qbTable.query.limit || 10), 0));
} else if (pageRelative == 1) {
$scope.qbTable.setField('skip', ($scope.qbTable.query.skip || 0) + ($scope.qbTable.query.limit || 10), 0);
} else {
throw new Error('Unsupported page move: ' + pageRelative);
}
};
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: '\n\t\t<nav>\n\t\t\t<ul class="pager">\n\t\t\t\t<li ng-class="canPrev ? \'\' : \'disabled\'" class="previous"><a ng-click="navPageRelative(-1)"><i class="fa fa-arrow-left"></i></a></li>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i class="fa fa-arrow-right"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t'
};
});
// }}}

2

dist/angular-ui-query-builder.min.js

@@ -1,1 +0,1 @@

"use strict";function _defineProperty(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}angular.module("angular-ui-query-builder",[]).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\t\t</div>\n\t',controller:["$scope",function(t){var e=this;t.$watch("$ctrl.spec",function(){_.forEach(e.spec,function(t,e){t.title||(t.title=_.startCase(e)),t.enum&&_.isArray(t.enum)&&(t.enum=_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value())})})}]}).component("uiQueryBuilderBranch",{bindings:{branch:"=",spec:"<"},template:'\n\t\t<div ng-repeat="leaf in $ctrl.properties track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query type component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">After</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Before</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t\t\x3c!-- Add button {{{ --\x3e\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t\x3c!-- }}} --\x3e\n\t',controller:["$element","$scope",function(t,e){var n=this;n.operands=[{id:"$eq",setter:function(t){return{$eq:t}},export:function(t){return t.valueEdit},base:{title:"Is",type:"string"},boolean:{title:"Is",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"}},{id:"$ne",setter:function(t){return{$ne:t}},export:function(t){return t.valueEdit},base:{title:"Is not",type:"string"},boolean:{title:"Is not",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"}},{id:"$in",setter:function(t){return{$in:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$in:t.value.$in}},base:{title:"One of",type:"array"}},{id:"$nin",setter:function(t){return{$nin:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$nin:t.value.$nin}},base:{title:"Not one of",type:"array"}},{id:"$gt",setter:function(t){return{$gt:t}},export:function(t){return{$gt:t.value.$gt}},base:{title:"Above",type:"number"}},{id:"$gte",setter:function(t){return{$gte:t}},export:function(t){return{$gte:t.value.$gte}},base:{title:"Above or equals",type:"number"}},{id:"$lt",setter:function(t){return{$lt:t}},export:function(t){return{$lt:t.value.$lt}},base:{title:"Below",type:"number"}},{id:"$lte",setter:function(t){return{$lt:t}},export:function(t){return{$lte:t.value.$lte}},base:{title:"Below or equals",type:"number"}},{id:"$exists",setter:function(t){return{$exists:!!t}},export:function(t){return{$exists:t.value.$exists}},base:{title:"Has a value",type:"boolean",textTrue:"Has a value",textFalse:"Has no value"}},{id:"$regexp",setter:function(t){return{$regexp:t}},export:function(t){return{$regexp:t.value.$regexp}},base:{title:"Matches",type:"string"}}],n.operandsByID=_.mapKeys(n.operands,"id"),n.getSpec=function(t,e,a){return n.spec[a]?n.spec[a]:"$and"==t||"$or"==t?_defineProperty({type:"group"},"type",t):_.isString(e)?{type:"string"}:_.isNumber(e)?{type:"number"}:{type:"string"}},n.translateBranch=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];return _(n.branch).map(function(t,a){var r=_.isObject(t)?_(t).keys().first():"$eq";return{id:a,value:t,valueEdit:(_.isObject(t)&&_.size(t)>0?_(t).map().first():void 0)||t,valueOperand:r,isMeta:a.startsWith("$"),spec:n.getSpec(a,t,a),path:e.concat([a])}}).sortBy(function(t){return t.isMeta?"Z"+t.id:"A"+t.id}).value()},n.exportBranch=function(){n.branch=_(n.properties).mapKeys(function(t){return t.id}).mapValues(function(t){return n.operandsByID[t.valueOperand].export(t)}).value()},n.properties,e.$watchGroup(["$ctrl.branch","$ctrl.spec"],function(){n.branch&&n.spec&&(n.properties=n.translateBranch(n.branch))}),n.setField=function(t,e){t.id=e,t.path=[e],t.value=void 0,t.valueEdit=void 0,t.valueOperand="$eq",t.spec=n.spec[e],n.setValue(t)},n.setWrapper=function(t,e){var a={};_.isObject(t.value)&&1==_.size(t.value)?a[e]=_(t.value).values().first():a[e]=t.valueEdit,t.valueOperand=e,t.value=a,t.valueEdit=_.isObject(a[e])&&_.size(a[e])?a[e]:a,n.setValue(t)},n.setValue=function(t,e){var a=_.isUndefined(e)?t.valueEdit:e;t.value=n.operandsByID[t.valueOperand].setter(a),t.valueEdit=_.isObject(t.value)&&_.size(t.value)?_(t.value).map().first():t.value,n.exportBranch()},n.setValueIncluded=function(t,e,n){var a=_(t.value).keys().first();if(!a)throw new Error("Tried to set array inclusion on non wrapped key: "+t.value);var r=t.value[a].includes(e);n&&!r?t.value[a].push(e):!n&&r&&(t.value[a]=t.value[a].filter(function(t){return t!=e})),t.value[a].sort(),t.valueEdit=_.isObject(t.value)&&_.size(t.value)?_(t.value).map().first():t.value},n.add=function(){if(!n.properties.some(function(t){return!t.id})){n.properties.push({});e.$on("uiQueryQueryRepaint",function(){t.find(".query-block > .new").addClass("open")})}},n.remove=function(t){n.properties=n.properties.filter(function(e){return e.id!=t}),n.exportBranch()}}]}).filter("uiQueryBuilderFilterSelected",function(){return function(t,e,n){if(t)return t.filter(function(t){var a=e.valueEdit.includes(t.id);return n?!a:a})}}).directive("ngRepeatEmit",["$rootScope","$timeout",function(t,e){return{restrict:"A",link:function(t,n,a){!0===t.$last&&e(function(){return t.$emit(a.ngRepeatEmit)})}}}]);
"use strict";function _defineProperty(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}angular.module("angular-ui-query-builder",[]).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\n\t\t\t\x3c!-- Meta field: sort {{{ --\x3e\n\t\t\t<div class="query-container">\n\t\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tSort by\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.sort" type="text" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Meta field: limit {{{ --\x3e\n\t\t\t<div class="query-container">\n\t\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tLimited to\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.limit" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-1">\n\t\t\t\t\t\tSkipping\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.skip" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\n\t\t</div>\n\t',controller:["$scope",function(t){var e=this;t.$watch("$ctrl.spec",function(){_.forEach(e.spec,function(t,e){t.title||(t.title=_.startCase(e)),t.enum&&_.isArray(t.enum)&&(t.enum=_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value())})})}]}).component("uiQueryBuilderBranch",{bindings:{branch:"=",spec:"<"},template:'\n\t\t<div ng-repeat="leaf in $ctrl.properties | filter:{isMeta:false} track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query type component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Is after</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gte\')">Is at least</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Is before</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lte\')">Is at most</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="date" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="date" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t\t\x3c!-- Add button {{{ --\x3e\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t\x3c!-- }}} --\x3e\n\t',controller:["$element","$scope",function(t,e){var n=this;n.operands=[{id:"$eq",setter:function(t){return{$eq:t}},export:function(t){return t.valueEdit},base:{title:"Is",type:"string"},boolean:{title:"Is",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"},date:{title:"Is exactly",type:"date"}},{id:"$ne",setter:function(t){return{$ne:t}},export:function(t){return{$ne:t.valueEdit}},base:{title:"Is not",type:"string"},boolean:{title:"Is not",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"},date:{title:"Is not exactly",type:"date"}},{id:"$in",setter:function(t){return{$in:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$in:t.value.$in}},base:{title:"One of",type:"array"}},{id:"$nin",setter:function(t){return{$nin:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$nin:t.value.$nin}},base:{title:"Not one of",type:"array"}},{id:"$gt",setter:function(t){return{$gt:t}},export:function(t){return{$gt:t.value.$gt}},base:{title:"Above",type:"number"},date:{title:"Is after",type:"date"}},{id:"$gte",setter:function(t){return{$gte:t}},export:function(t){return{$gte:t.value.$gte}},base:{title:"Above or equals",type:"number"},date:{title:"Is at least",type:"date"}},{id:"$lt",setter:function(t){return{$lt:t}},export:function(t){return{$lt:t.value.$lt}},base:{title:"Below",type:"number"},date:{title:"Is before",type:"date"}},{id:"$lte",setter:function(t){return{$lt:t}},export:function(t){return{$lte:t.value.$lte}},base:{title:"Below or equals",type:"number"},date:{title:"Is at most",type:"date"}},{id:"$exists",setter:function(t){return{$exists:!!t}},export:function(t){return{$exists:t.value.$exists}},base:{title:"Has a value",type:"boolean",textTrue:"Has a value",textFalse:"Has a value"}},{id:"$regexp",setter:function(t){return{$regexp:t}},export:function(t){return{$regexp:t.value.$regexp}},base:{title:"Matches",type:"string"}}],n.operandsByID=_.mapKeys(n.operands,"id"),n.getSpec=function(t,e,a){return n.spec[a]?n.spec[a]:"$and"==t||"$or"==t?_defineProperty({type:"group"},"type",t):_.isString(e)?{type:"string"}:_.isNumber(e)?{type:"number"}:{type:"string"}},n.translateBranch=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];return _(n.branch).map(function(t,a){return{id:a,value:t,valueEdit:n.getFlatValue(t),valueOperand:_.isObject(t)?_(t).keys().first():"$eq",isMeta:a.startsWith("$")||["sort","skip","limit"].includes(a),spec:n.getSpec(a,t,a),path:e.concat([a])}}).sortBy(function(t){return t.isMeta?"Z"+t.id:"A"+t.id}).value()},n.exportBranch=function(){n.branch=_(n.properties).mapKeys(function(t){return t.id}).mapValues(function(t){return n.operandsByID[t.valueOperand].export(t)}).value()},n.properties,e.$watchGroup(["$ctrl.branch","$ctrl.spec"],function(){n.branch&&n.spec&&(n.properties=n.translateBranch(n.branch))}),n.setField=function(t,e){t.id=e,t.path=[e],t.value=void 0,t.valueEdit=void 0,t.valueOperand="$eq",t.spec=n.spec[e],n.setValue(t)},n.setWrapper=function(t,e){if("$eq"==t.valueOperand&&"$ne"==e)t.valueOperand="$ne",t.valueEdit=n.getFlatValue(t.value),t.value={$ne:t.valueEdit};else if("$ne"==t.valueOperand&&"$eq"==e)t.valueOperand="$eq",t.valueEdit=n.getFlatValue(t.value),t.value={$eq:t.valueEdit};else if("$in"==t.valueOperand&&"$eq"==e)t.valueOperand="$eq",t.value=t.valueEdit=n.getFlatValue(t.value);else if("$eq"!=t.valueOperand&&void 0!==t.valueOperand||"$in"!=e)if("$exists"==e)t.valueOperand="$exists",t.valueEdit=!0,t.value={$exists:t.valueEdit};else{console.log("UNHANDLED TYPE CONVERT:",t.type,"=>",e);var a=n.getFlatValue(t.value);t.valueOperand=e,t.valueEdit=a,t.value=_defineProperty({},t.valueOperand,t.valueEdit)}else t.valueOperand="$in",t.valueEdit=n.getFlatValue(t.value),t.value={$in:[t.valueEdit]};n.exportBranch()},n.setValue=function(t,e){var a=_.isUndefined(e)?t.valueEdit:e;t.value=n.operandsByID[t.valueOperand].setter(a),t.valueEdit=n.getFlatValue(t.value),n.exportBranch()},n.setValueIncluded=function(t,e,n){var a=_(t.value).keys().first();if(!a)throw new Error("Tried to set array inclusion on non wrapped key: "+t.value);var l=t.value[a].includes(e);n&&!l?t.value[a].push(e):!n&&l&&(t.value[a]=t.value[a].filter(function(t){return t!=e})),t.value[a].sort(),t.valueEdit=_.isObject(t.value)&&_.size(t.value)?_(t.value).map().first():t.value},n.getFlatValue=function(t){return _.isString(t)||_.isNumber(t)||_.isBoolean(t)||_.isDate(t)?t:_.isObject(t)&&1==_.size(t)?_(t).values().first():(console.warn("Given up trying to flatten input value",t),t)},n.add=function(){if(!n.properties.some(function(t){return!t.id})){n.properties.push({});e.$on("uiQueryQueryRepaint",function(){t.find(".query-block > .new").addClass("open")})}},n.remove=function(t){n.properties=n.properties.filter(function(e){return e.id!=t}),n.exportBranch()}}]}).filter("uiQueryBuilderFilterSelected",function(){return function(t,e,n){if(t)return t.filter(function(t){var a=e.valueEdit.includes(t.id);return n?!a:a})}}).directive("ngRepeatEmit",["$rootScope","$timeout",function(t,e){return{restrict:"A",link:function(t,n,a){!0===t.$last&&e(function(){return t.$emit(a.ngRepeatEmit)})}}}]),angular.module("angular-ui-query-builder").service("qbTableSettings",function(){return{icons:{sortNone:"fa fa-fw fa-sort text-muted",sortAsc:"fa fa-fw fa-sort-alpha-asc text-primary",sortDesc:"fa fa-fw fa-sort-alpha-desc text-primary"}}}).directive("qbTable",function(){return{scope:{qbTable:"="},restrict:"AC",controller:["$scope","qbTableSettings",function(t,e){var n=this;n.query=t.qbTable,n.setField=function(e,a){if(void 0!=a)switch(e){case"sort":n.query.sort===a?n.query.sort="-"+a:(n.query.sort,n.query.sort=a);break;default:t.qbTable[e]=a}else delete n.query[e]}}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,n){var a=this;e.qbTableSettings=n,e.canSort=!1,e.isSorted=!1,a.$onInit=function(){e.canSort=e.sortable||""===t.sortable},e.$watch("qbTable.query.sort",function(t){var n=e.sortable||e.q;t?angular.isArray(t)&&t.some(function(t){return t==n})||t==n?e.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+n})||t=="-"+n?e.isSorted="desc":e.isSorted=!1:e.isSorted=!1}),e.toggleSort=function(){e.sortable?e.qbTable.setField("sort",e.sortable):e.q&&""===t.sortable&&e.qbTable.setField("sort",e.q)}}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<a ng-if="canSort" ng-click="toggleSort()" class="pull-right">\n\t\t\t<i class="{{\n\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t}}"></i>\n\t\t</a>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",controller:["$attrs","$scope","qbTableSettings",function(t,e,n){e.qbTableSettings=n,e.canPrev=!0,e.canNext=!0,e.$watchGroup(["qbTable.query.limit","qbTable.query.skip"],function(t){e.canPrev=e.qbTable.query.skip>0,e.canNext=!e.total||e.qbTable.query.skip+e.qbTable.query.limit<e.total}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.min((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0));else{if(1!=t)throw new Error("Unsupported page move: "+t);e.qbTable.setField("skip",(e.qbTable.query.skip||0)+(e.qbTable.query.limit||10),0)}}}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<nav>\n\t\t\t<ul class="pager">\n\t\t\t\t<li ng-class="canPrev ? \'\' : \'disabled\'" class="previous"><a ng-click="navPageRelative(-1)"><i class="fa fa-arrow-left"></i></a></li>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i class="fa fa-arrow-right"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t'}});
var _ = require('lodash');
var babel = require('gulp-babel');
var concat = require('gulp-concat');
var cleanCSS = require('gulp-clean-css');

@@ -9,3 +10,2 @@ var ghPages = require('gulp-gh-pages');

var nodemon = require('gulp-nodemon');
var rename = require('gulp-rename');
var rimraf = require('rimraf');

@@ -47,4 +47,9 @@ var uglify = require('gulp-uglify');

gulp.task('js', ()=>
gulp.src('./src/angular-ui-query-builder.js')
gulp.task('js', ['js:all', 'js:core', 'js:tables']);
gulp.task('js:all', ()=>
gulp.src([
'./src/angular-ui-query-builder-core.js',
'./src/angular-ui-query-builder-tables.js',
])
.pipe(plumber({

@@ -57,3 +62,3 @@ errorHandler: function(err) {

}))
.pipe(rename('angular-ui-query-builder.js'))
.pipe(concat('angular-ui-query-builder.js'))
.pipe(babel({

@@ -64,3 +69,3 @@ presets: ['es2015'],

.pipe(gulp.dest('./dist'))
.pipe(rename('angular-ui-query-builder.min.js'))
.pipe(concat('angular-ui-query-builder.min.js'))
.pipe(uglify())

@@ -70,7 +75,52 @@ .pipe(gulp.dest('./dist'))

gulp.task('css', ()=>
gulp.src('./src/angular-ui-query-builder.css')
.pipe(rename('angular-ui-query-builder.css'))
gulp.task('js:core', ()=>
gulp.src('./src/angular-ui-query-builder-core.js')
.pipe(plumber({
errorHandler: function(err) {
gutil.log(gutil.colors.red('ERROR DURING JS BUILD'));
process.stdout.write(err.stack);
this.emit('end');
},
}))
.pipe(concat('angular-ui-query-builder-core.js'))
.pipe(babel({
presets: ['es2015'],
plugins: ['angularjs-annotate'],
}))
.pipe(gulp.dest('./dist'))
.pipe(rename('angular-ui-query-builder.min.css'))
.pipe(concat('angular-ui-query-builder-core.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist'))
);
gulp.task('js:tables', ()=>
gulp.src('./src/angular-ui-query-builder-tables.js')
.pipe(plumber({
errorHandler: function(err) {
gutil.log(gutil.colors.red('ERROR DURING JS BUILD'));
process.stdout.write(err.stack);
this.emit('end');
},
}))
.pipe(concat('angular-ui-query-builder-tables.js'))
.pipe(babel({
presets: ['es2015'],
plugins: ['angularjs-annotate'],
}))
.pipe(gulp.dest('./dist'))
.pipe(concat('angular-ui-query-builder-tables.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist'))
);
gulp.task('css', ['css:all', 'css:core', 'css:tables']);
gulp.task('css:all', ()=>
gulp.src([
'./src/angular-ui-query-builder-core.css',
'./src/angular-ui-query-builder-tables.css',
])
.pipe(concat('angular-ui-query-builder.css'))
.pipe(gulp.dest('./dist'))
.pipe(concat('angular-ui-query-builder.min.css'))
.pipe(cleanCSS())

@@ -80,2 +130,20 @@ .pipe(gulp.dest('./dist'))

gulp.task('css:core', ()=>
gulp.src('./src/angular-ui-query-builder-core.css')
.pipe(concat('angular-ui-query-builder-core.css'))
.pipe(gulp.dest('./dist'))
.pipe(concat('angular-ui-query-builder-core.min.css'))
.pipe(cleanCSS())
.pipe(gulp.dest('./dist'))
);
gulp.task('css:tables', ()=>
gulp.src('./src/angular-ui-query-builder-tables.css')
.pipe(concat('angular-ui-query-builder-tables.css'))
.pipe(gulp.dest('./dist'))
.pipe(concat('angular-ui-query-builder-tables.min.css'))
.pipe(cleanCSS())
.pipe(gulp.dest('./dist'))
);
gulp.task('gh-pages', ['build'], function() {

@@ -101,3 +169,3 @@ rimraf.sync('./gh-pages');

], {base: __dirname})
.pipe(rename(function(path) {
.pipe(concat(function(path) {
if (path.dirname == 'demo') { // Move all demo files into root

@@ -104,0 +172,0 @@ path.dirname = '.';

{
"name": "@momsfriendlydevco/angular-ui-query-builder",
"version": "1.1.8",
"version": "1.2.0",
"description": "MongoDB format query-builder UI component for Angular",

@@ -24,2 +24,3 @@ "main": "dist/angular-ui-query-builder.js",

"express": "^4.16.1",
"faker": "^4.1.0",
"font-awesome": "^4.7.0",

@@ -29,6 +30,6 @@ "gulp": "^3.9.1",

"gulp-clean-css": "^3.9.0",
"gulp-concat": "^2.6.1",
"gulp-gh-pages": "^0.5.4",
"gulp-nodemon": "^2.2.1",
"gulp-plumber": "^1.1.0",
"gulp-rename": "^1.2.2",
"gulp-uglify": "^3.0.0",

@@ -43,4 +44,5 @@ "gulp-util": "^3.0.8",

"jquery": "^3.2.1",
"lodash": "^4.17.4"
"lodash": "^4.17.4",
"moment": "^2.20.1"
}
}

@@ -39,2 +39,85 @@ angular-ui-query-builder

API
====
ui-query-builder (directive)
----------------------------
Simply create a query object and link it up to the directive.
In a controller:
```javascript
$scope.mySpec = {
_id: {type: 'objectId'},
name: {type: 'string'},
email: {type: 'string'},
status: {type: 'string', enum: ['pending', 'active', 'deleted']},
};
$scope.myQuery = {
status: 'active', // Assumes you have a status field
limit: 10,
skip: 0,
};
```
In a HTML template:
```html
<ui-query-builder query="$ctrl.myQuery" spec="$ctrl.mySpec"></ui-query-builder>
```
... or see the [Demo](https://momsfriendlydevco.github.io/angular-ui-query-builder).
The ui-query-builder directive takes the following parameters:
| Parameter | Type | Description |
|-----------|--------|---------------------------------------------------------------------------------------|
| `query` | Object | The current query, this object will be mutated into / from a MongoDB compatible query |
| `spec` | Object | A base specification of field types to use when providing the UI |
qb-table, qb-col, qb-pagination (directives)
--------------------------------------------
If using either the full JS release (`angular-ui-query-builder.js`) or the table add-on (`angular-ui-query-builder-tables.js`) additional functionality is provided for Tables including column setup, pagination and other functionality.
To use:
1. Add the `qb-table` directive to the table header with a pointer to the query object to mutate
2. (Optional) Add the `qb-col` directive to any table column to extend, include attributes like `sortable` to add that functionality
3. (Optional) Add the `qb-pagination` directive into the table footer to add pagination functionality
For example:
```html
<table class="table table-bordered table-striped table-hover" qb-table="query">
<thead>
<tr>
<th qb-col="name" sortable>Name</th>
<th qb-col="username" sortable>Username</th>
<th qb-col="email" sortable>Email</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in data track by row.id">
<td>{{row.name}}</td>
<td>{{row.username}}</td>
<td>{{row.email}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">
<qb-pagination></qb-pagination>
</td>
</tr>
</tfoot>
</table>
```
For a more complex example see the [demo](https://momsfriendlydevco.github.io/angular-ui-query-builder).
TODO

@@ -46,8 +129,8 @@ ====

* [ ] Compound queries - `$or` / `$and`
* [ ] Automatically moving from a static string (`$eq` condition) to a multiple choice enum (`$in`) when a comma is used in a string
* [x] Automatically moving from a static string (`$eq` condition) to a multiple choice enum (`$in`) when a comma is used in a string
* [ ] Convert string ENUMs to a `$in` type automatically
* [ ] Number filtering - above, below, between
* [ ] Date support - date selector, before, after
* [x] Date support - date selector, before, after
* [ ] Nicer syntax support for `$regexp`
* [ ] Support for `$length`
* [ ] Nicer multi level path support

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc