From a300f98c7d0be4b72912fef0a0ece822f78430e4 Mon Sep 17 00:00:00 2001
From: mistic100 Output
+
@@ -208,6 +209,7 @@ Output
*/
{
id: 'name',
+ field: 'username',
label: {
en: 'Name',
fr: 'Nom'
@@ -582,7 +584,7 @@ Output
// set rules from SQL
$('.set-sql').on('click', function() {
- $('#builder').queryBuilder('setRulesFromSQL', 'name NOT LIKE "Mistic%" OR price BETWEEN 100 OR 200 OR NOT (category IN(1, 2) AND rate <= 2)');
+ $('#builder').queryBuilder('setRulesFromSQL', 'username NOT LIKE "Mistic%" OR price BETWEEN 100 OR 200 OR NOT (category IN(1, 2) AND rate <= 2)');
});
// reset builder
diff --git a/package.json b/package.json
index 3d6d917e..af0dbb90 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "jQuery-QueryBuilder",
- "version": "2.4.0",
+ "version": "2.5.0",
"author": {
"name": "Damien \"Mistic\" Sorel",
"email": "contact@git.strangeplanet.fr",
diff --git a/src/plugins/sql-support/plugin.js b/src/plugins/sql-support/plugin.js
index bb190c49..e3676825 100644
--- a/src/plugins/sql-support/plugin.js
+++ b/src/plugins/sql-support/plugin.js
@@ -545,15 +545,7 @@ QueryBuilder.extend(/** @lends module:plugins.SqlSupport.prototype */ {
Utils.error('SQLParse', 'Cannot find field name in {0}', JSON.stringify(data.left));
}
- /**
- * Returns a filter identifier from the SQL field
- * @event changer:getSQLFieldID
- * @memberof module:plugins.SqlSupport
- * @param {string} field
- * @param {*} value
- * @returns {string}
- */
- var id = self.change('getSQLFieldID', field, value);
+ var id = self.getSQLFieldID(field, value);
/**
* Modifies the rule generated from the SQL expression
@@ -583,6 +575,39 @@ QueryBuilder.extend(/** @lends module:plugins.SqlSupport.prototype */ {
*/
setRulesFromSQL: function(query, stmt) {
this.setRules(this.getRulesFromSQL(query, stmt));
+ },
+
+ /**
+ * Returns a filter identifier from the SQL field.
+ * Automatically use the only one filter with a matching field, fires a changer otherwise.
+ * @param {string} field
+ * @param {*} value
+ * @fires module:plugins.SqlSupport:changer:getSQLFieldID
+ * @returns {string}
+ * @private
+ */
+ getSQLFieldID: function(field, value) {
+ var matchingFilters = this.filters.filter(function(filter) {
+ return filter.field === field;
+ });
+
+ var id;
+ if (matchingFilters.length === 1) {
+ id = matchingFilters[0].id;
+ }
+ else {
+ /**
+ * Returns a filter identifier from the SQL field
+ * @event changer:getSQLFieldID
+ * @memberof module:plugins.SqlSupport
+ * @param {string} field
+ * @param {*} value
+ * @returns {string}
+ */
+ id = this.change('getSQLFieldID', field, value);
+ }
+
+ return id;
}
});
diff --git a/tests/index.html b/tests/index.html
index 875f9320..343c9210 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -33,6 +33,7 @@
+
diff --git a/tests/plugins.sql-support.module.js b/tests/plugins.sql-support.module.js
index 6dc82cfe..a64ecf4a 100644
--- a/tests/plugins.sql-support.module.js
+++ b/tests/plugins.sql-support.module.js
@@ -1,13 +1,13 @@
-$(function () {
+$(function() {
var $b = $('#builder');
QUnit.module('plugins.sql-support', {
- afterEach: function () {
+ afterEach: function() {
$b.queryBuilder('destroy');
}
});
- QUnit.test('Raw SQL', function (assert) {
+ QUnit.test('Raw SQL', function(assert) {
$b.queryBuilder({
filters: basic_filters,
rules: basic_rules
@@ -30,7 +30,7 @@ $(function () {
);
});
- QUnit.test('Placeholder SQL', function (assert) {
+ QUnit.test('Placeholder SQL', function(assert) {
$b.queryBuilder({
filters: basic_filters,
rules: basic_rules
@@ -53,7 +53,7 @@ $(function () {
);
});
- QUnit.test('Numbered SQL', function (assert) {
+ QUnit.test('Numbered SQL', function(assert) {
$b.queryBuilder({
filters: basic_filters,
rules: basic_rules
@@ -92,7 +92,7 @@ $(function () {
);
});
- QUnit.test('Named SQL', function (assert) {
+ QUnit.test('Named SQL', function(assert) {
$b.queryBuilder({
filters: basic_filters,
rules: basic_rules
@@ -131,7 +131,7 @@ $(function () {
);
});
- QUnit.test('All operators', function (assert) {
+ QUnit.test('All operators', function(assert) {
$b.queryBuilder({
filters: basic_filters,
rules: all_operators_rules
@@ -154,14 +154,14 @@ $(function () {
);
});
- QUnit.test('Nested rules', function (assert) {
+ QUnit.test('Nested rules', function(assert) {
$b.queryBuilder({
filters: [
- {id: 'a', type: 'integer'},
- {id: 'b', type: 'integer'},
- {id: 'c', type: 'integer'},
- {id: 'd', type: 'integer'}
+ { id: 'a', type: 'integer' },
+ { id: 'b', type: 'integer' },
+ { id: 'c', type: 'integer' },
+ { id: 'd', type: 'integer' }
]
});
@@ -191,7 +191,7 @@ $(function () {
);
});
- QUnit.test('Custom export/parsing', function (assert) {
+ QUnit.test('Custom export/parsing', function(assert) {
var rules = {
condition: 'AND',
rules: [
@@ -225,13 +225,13 @@ $(function () {
]
});
- $b.on('ruleToSQL.queryBuilder.filter', function (e, rule, sqlValue, sqlOperator) {
+ $b.on('ruleToSQL.queryBuilder.filter', function(e, rule, sqlValue, sqlOperator) {
if (rule.id === 'last_days') {
e.value = rule.field + ' ' + sqlOperator('DATE_SUB(NOW(), INTERVAL ' + sqlValue + ' DAY)');
}
});
- $b.on('parseSQLNode.queryBuilder.filter', function (e) {
+ $b.on('parseSQLNode.queryBuilder.filter', function(e) {
var data = e.value;
// left must be the field name and right must be the date_sub function
if (data.left && data.left.value == 'display_date' && data.operation == '>' && data.right && data.right.name == 'DATE_SUB') {
@@ -269,6 +269,44 @@ $(function () {
);
});
+ QUnit.test('Automatically use filter from field', function(assert) {
+ var rules = {
+ condition: 'AND',
+ rules: [
+ {
+ id: 'name',
+ operator: 'equal',
+ value: 'Mistic'
+ }
+ ]
+ };
+
+ var sql = 'username = \'Mistic\'';
+
+ $b.queryBuilder({
+ filters: [
+ {
+ id: 'name',
+ field: 'username',
+ type: 'string'
+ },
+ {
+ id: 'last_days',
+ field: 'display_date',
+ type: 'integer'
+ }
+ ]
+ });
+
+ $b.queryBuilder('setRulesFromSQL', sql);
+
+ assert.rulesMatch(
+ $b.queryBuilder('getRules'),
+ rules,
+ 'Should use "name" filter from "username" field'
+ );
+ });
+
var basic_rules_sql_raw = {
sql: 'price < 10.25 AND name IS NULL AND ( category IN(\'mo\', \'mu\') OR id != \'1234-azer-5678\' ) '
From 7b2b6863826cad430cbe8e5ef3dd485faff36710 Mon Sep 17 00:00:00 2001
From: David Beer-Gabel Output
$('.set-mongo').on('click', function() {
$('#builder').queryBuilder('setRulesFromMongo', {
"$or": [{
- "name": {
+ "username": {
"$regex": "^(?!Mistic)"
}
}, {
diff --git a/src/plugins/mongodb-support/plugin.js b/src/plugins/mongodb-support/plugin.js
index eca501bc..93a17348 100644
--- a/src/plugins/mongodb-support/plugin.js
+++ b/src/plugins/mongodb-support/plugin.js
@@ -270,15 +270,7 @@ QueryBuilder.extend(/** @lends module:plugins.MongoDbSupport.prototype */ {
var opVal = mdbrl.call(self, value);
- /**
- * Returns a filter identifier from the MongoDB field
- * @event changer:getMongoDBFieldID
- * @memberof module:plugins.MongoDbSupport
- * @param {string} field
- * @param {*} value
- * @returns {string}
- */
- var id = self.change('getMongoDBFieldID', field, value);
+ var id = self.getMongoDBFieldID(field, value);
/**
* Modifies the rule generated from the MongoDB expression
@@ -320,6 +312,39 @@ QueryBuilder.extend(/** @lends module:plugins.MongoDbSupport.prototype */ {
*/
setRulesFromMongo: function(query) {
this.setRules(this.getRulesFromMongo(query));
+ },
+
+ /**
+ * Returns a filter identifier from the MongoDB field.
+ * Automatically use the only one filter with a matching field, fires a changer otherwise.
+ * @param {string} field
+ * @param {*} value
+ * @fires module:plugins.MongoDbSupport:changer:getMongoDBFieldID
+ * @returns {string}
+ * @private
+ */
+ getMongoDBFieldID: function(field, value) {
+ var matchingFilters = this.filters.filter(function(filter) {
+ return filter.field === field;
+ });
+
+ var id;
+ if (matchingFilters.length === 1) {
+ id = matchingFilters[0].id;
+ }
+ else {
+ /**
+ * Returns a filter identifier from the MongoDB field
+ * @event changer:getMongoDBFieldID
+ * @memberof module:plugins.MongoDbSupport
+ * @param {string} field
+ * @param {*} value
+ * @returns {string}
+ */
+ id = this.change('getMongoDBFieldID', field, value);
+ }
+
+ return id;
}
});
diff --git a/tests/plugins.mongo-support.module.js b/tests/plugins.mongo-support.module.js
index 68e4a329..73ff20f9 100644
--- a/tests/plugins.mongo-support.module.js
+++ b/tests/plugins.mongo-support.module.js
@@ -62,6 +62,48 @@ $(function(){
);
});
+ QUnit.test('Automatically use filter from field', function(assert) {
+ var rules = {
+ condition: 'AND',
+ rules: [
+ {
+ id: 'name',
+ operator: 'equal',
+ value: 'Mistic'
+ }
+ ]
+ };
+
+ var mongo = {
+ $and: [{
+ username: 'Mistic'
+ }]
+ };
+
+ $b.queryBuilder({
+ filters: [
+ {
+ id: 'name',
+ field: 'username',
+ type: 'string'
+ },
+ {
+ id: 'last_days',
+ field: 'display_date',
+ type: 'integer'
+ }
+ ]
+ });
+
+ $b.queryBuilder('setRulesFromMongo', mongo);
+
+ assert.rulesMatch(
+ $b.queryBuilder('getRules'),
+ rules,
+ 'Should use "name" filter from "username" field'
+ );
+ });
+
var all_operators_rules = {
condition: 'AND',
From 845017c85bf73125a1dea5f657099fdb139cb63e Mon Sep 17 00:00:00 2001
From: mistic100 Output
-
+
diff --git a/src/model.js b/src/model.js
index af13aac7..a397c98f 100644
--- a/src/model.js
+++ b/src/model.js
@@ -64,44 +64,6 @@ $.extend(Model.prototype, /** @lends Model.prototype */ {
}
});
-/**
- * Defines properties on an Node prototype with getter and setter.
- * Update events are emitted in the setter through root Model (if any).
- * The object must have a `__` object, non enumerable property to store values.
- * @param {function} obj
- * @param {string[]} fields
- */
-Model.defineModelProperties = function(obj, fields) {
- fields.forEach(function(field) {
- Object.defineProperty(obj.prototype, field, {
- enumerable: true,
- get: function() {
- return this.__[field];
- },
- set: function(value) {
- var previousValue = (this.__[field] !== null && typeof this.__[field] == 'object') ?
- $.extend({}, this.__[field]) :
- this.__[field];
-
- this.__[field] = value;
-
- if (this.model !== null) {
- /**
- * After a value of the model changed
- * @event model:update
- * @memberof Model
- * @param {Node} node
- * @param {string} field
- * @param {*} value
- * @param {*} previousValue
- */
- this.model.trigger('update', this, field, value, previousValue);
- }
- }
- });
- });
-};
-
/**
* Root abstract object
@@ -177,7 +139,7 @@ var Node = function(parent, $el) {
this.parent = parent;
};
-Model.defineModelProperties(Node, ['level', 'error', 'data', 'flags']);
+Utils.defineModelProperties(Node, ['level', 'error', 'data', 'flags']);
Object.defineProperty(Node.prototype, 'parent', {
enumerable: true,
@@ -340,7 +302,7 @@ var Group = function(parent, $el) {
Group.prototype = Object.create(Node.prototype);
Group.prototype.constructor = Group;
-Model.defineModelProperties(Group, ['condition']);
+Utils.defineModelProperties(Group, ['condition']);
/**
* Removes group's content
@@ -561,7 +523,7 @@ var Rule = function(parent, $el) {
Rule.prototype = Object.create(Node.prototype);
Rule.prototype.constructor = Rule;
-Model.defineModelProperties(Rule, ['filter', 'operator', 'value']);
+Utils.defineModelProperties(Rule, ['filter', 'operator', 'value']);
/**
* Checks if this Node is the root
diff --git a/src/plugins/not-group/plugin.js b/src/plugins/not-group/plugin.js
index 6596a817..da6f98b0 100644
--- a/src/plugins/not-group/plugin.js
+++ b/src/plugins/not-group/plugin.js
@@ -104,7 +104,7 @@ QueryBuilder.define('not-group', function(options) {
* @memberof Group
* @instance
*/
-Model.defineModelProperties(Group, ['not']);
+Utils.defineModelProperties(Group, ['not']);
QueryBuilder.selectors.group_not = QueryBuilder.selectors.group_header + ' [data-not=group]';
diff --git a/src/utils.js b/src/utils.js
index 8f6150f2..94af0956 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -202,3 +202,41 @@ Utils.groupSort = function(items, key) {
return newItems;
};
+
+/**
+ * Defines properties on an Node prototype with getter and setter.
+ * Update events are emitted in the setter through root Model (if any).
+ * The object must have a `__` object, non enumerable property to store values.
+ * @param {function} obj
+ * @param {string[]} fields
+ */
+Utils.defineModelProperties = function(obj, fields) {
+ fields.forEach(function(field) {
+ Object.defineProperty(obj.prototype, field, {
+ enumerable: true,
+ get: function() {
+ return this.__[field];
+ },
+ set: function(value) {
+ var previousValue = (this.__[field] !== null && typeof this.__[field] == 'object') ?
+ $.extend({}, this.__[field]) :
+ this.__[field];
+
+ this.__[field] = value;
+
+ if (this.model !== null) {
+ /**
+ * After a value of the model changed
+ * @event model:update
+ * @memberof Model
+ * @param {Node} node
+ * @param {string} field
+ * @param {*} value
+ * @param {*} previousValue
+ */
+ this.model.trigger('update', this, field, value, previousValue);
+ }
+ }
+ });
+ });
+};
diff --git a/tests/index.html b/tests/index.html
index 343c9210..3af96d4d 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -38,8 +38,8 @@
-
+
From d552fde68147ec6a8f654be409a51155af2e4171 Mon Sep 17 00:00:00 2001
From: Damien SOREL Output
2: 'warning',
5: 'success'
},
- operators: ['in', 'not_in', 'equal', 'not_equal', 'is_null', 'is_not_null']
+ operators: ['equal', 'not_equal', 'in', 'not_in', 'is_null', 'is_not_null'],
+ default_operator: 'in'
},
/*
* select
diff --git a/src/core.js b/src/core.js
index 764c8c6b..31964cc6 100644
--- a/src/core.js
+++ b/src/core.js
@@ -617,7 +617,20 @@ QueryBuilder.prototype.createRuleOperators = function(rule) {
$operatorContainer.html($operatorSelect);
// set the operator without triggering update event
- rule.__.operator = operators[0];
+ if (rule.filter.default_operator) {
+ rule.__.operator = operators.filter(function(operator) {
+ return operator.type === rule.filter.default_operator;
+ })[0];
+
+ if (!rule.__.operator) {
+ Utils.error('Config', 'Invalid operator {0}', rule.filter.default_operator);
+ }
+ }
+ else {
+ rule.__.operator = operators[0];
+ }
+
+ rule.$el.find(QueryBuilder.selectors.rule_operator).val(rule.operator.type);
/**
* After creating the dropdown for operators
@@ -752,6 +765,7 @@ QueryBuilder.prototype.updateRuleOperator = function(rule, previousOperator) {
*/
this.trigger('afterUpdateRuleOperator', rule);
+ // FIXME is it necessary ?
this.updateRuleValue(rule);
};
diff --git a/tests/common.js b/tests/common.js
index 91a3ffb5..89550f42 100644
--- a/tests/common.js
+++ b/tests/common.js
@@ -301,7 +301,8 @@ var basic_filters = [{
label: 'Age',
type: 'integer',
input: 'text',
- value_separator: '|'
+ value_separator: '|',
+ default_operator: 'in'
}];
var basic_rules = {
diff --git a/tests/data.module.js b/tests/data.module.js
index 358df511..ea381717 100644
--- a/tests/data.module.js
+++ b/tests/data.module.js
@@ -411,6 +411,23 @@ $(function() {
);
});
+ /**
+ * Test default operator
+ */
+ QUnit.test('default operator', function(assert) {
+ $b.queryBuilder({
+ filters: basic_filters
+ });
+
+ $('[name=builder_rule_0_filter]').val('age').trigger('change');
+
+ assert.equal(
+ $('[name=builder_rule_0_operator]').val(),
+ 'in',
+ 'Should set "age" operator to "in" by default'
+ );
+ });
+
/**
* Test allow_invalid option
*/
From 6331d3ea61c5eb7c740be8f26f633cd12813c85b Mon Sep 17 00:00:00 2001
From: mistic100 Output
}, {
id: 'coord',
operator: 'equal',
- value: 'B.3'
+ value: undefined // will use filter's default value
}]
}, {
id: 'name',
diff --git a/src/core.js b/src/core.js
index 31964cc6..5036062f 100644
--- a/src/core.js
+++ b/src/core.js
@@ -618,13 +618,7 @@ QueryBuilder.prototype.createRuleOperators = function(rule) {
// set the operator without triggering update event
if (rule.filter.default_operator) {
- rule.__.operator = operators.filter(function(operator) {
- return operator.type === rule.filter.default_operator;
- })[0];
-
- if (!rule.__.operator) {
- Utils.error('Config', 'Invalid operator {0}', rule.filter.default_operator);
- }
+ rule.__.operator = this.getOperatorByType(rule.filter.default_operator);
}
else {
rule.__.operator = operators[0];
diff --git a/src/public.js b/src/public.js
index 610a4adf..14a16c60 100644
--- a/src/public.js
+++ b/src/public.js
@@ -410,17 +410,22 @@ QueryBuilder.prototype.setRules = function(data, options) {
if (!item.empty) {
model.filter = self.getFilterById(item.id, !options.allow_invalid);
+ }
- if (model.filter) {
- model.operator = self.getOperatorByType(item.operator, !options.allow_invalid);
+ if (model.filter) {
+ model.operator = self.getOperatorByType(item.operator, !options.allow_invalid);
- if (!model.operator) {
- model.operator = self.getOperators(model.filter)[0];
- }
+ if (!model.operator) {
+ model.operator = self.getOperators(model.filter)[0];
+ }
+ }
- if (model.operator && model.operator.nb_inputs !== 0 && item.value !== undefined) {
- model.value = item.value;
- }
+ if (model.operator && model.operator.nb_inputs !== 0) {
+ if (item.value !== undefined) {
+ model.value = item.value;
+ }
+ else if (model.filter.default_value !== undefined) {
+ model.value = model.filter.default_value;
}
}
diff --git a/tests/data.module.js b/tests/data.module.js
index 72de4539..d7f11838 100644
--- a/tests/data.module.js
+++ b/tests/data.module.js
@@ -517,6 +517,35 @@ $(function() {
);
});
+ QUnit.test('apply default value', function(assert) {
+ $b.queryBuilder({
+ filters: [
+ {
+ id: 'name',
+ default_value: 'Mistic'
+ }
+ ],
+ rules: [
+ {
+ id: 'name'
+ }
+ ]
+ });
+
+ assert.rulesMatch(
+ $b.queryBuilder('getRules'),
+ {
+ condition: 'AND',
+ rules: [{
+ id: 'name',
+ operator: 'equal',
+ value: 'Mistic'
+ }]
+ },
+ 'Should have used the filter default value'
+ );
+ });
+
/**
* Test allow_empty_value option
*/
From e1887ad2650531ff3c3a0263442a8d7259251582 Mon Sep 17 00:00:00 2001
From: mistic100 \
-
Output
-
+
From f66174a0980cf13734849a4efb95978b54438289 Mon Sep 17 00:00:00 2001
From: mistic100 jQuery QueryBuilder
Output