mistic100 / jQuery-QueryBuilder

jQuery plugin offering an interface to create complex queries

Home Page:https://querybuilder.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Custom Plugin requires an edit of the Rule's structure in order to work

VanHiro opened this issue · comments

The Problem

I created a custom plugin but to make it work I need to edit the Rule's structure from the file querybuilder-standalone itself.
The plugin adds a button like the one from the plugin not-group inside each rule. The function of this plugin is to transform an input like this 'field = "value"' into this 'UPPER( field ) = "VALUE"'. The problem with this solution is that, in order to reuse this query (get/set SQL), I need to modify the structure of the Rule itself in the query-builder.
You can find this question also in stackoverflow:
https://stackoverflow.com/questions/69646450/jquery-querybuilder-editing-the-rules-structure-in-the-file-query-builder-stan

The Code

The edits I would need to make are the following:
You can find the original code at line 2242 of the file query-builder.standalone.js

if (model.filter) {
    model.filter.to_upper = item.to_upper; /* <---This is the line I added myself */
    model.operator = self.getOperatorByType(item.operator, !options.allow_invalid);
    if (!model.operator) {
       model.operator = self.getOperators(model.filter)[0];
    }
}

And this:
Original code between line 6215 and 6232

var id = self.getSQLFieldID(field, value);
//The following line is the code I need
var to_upper = data.to_upper;
/**
* Modifies the rule generated from the SQL expression
* @event changer:sqlToRule
* @memberof module:plugins.SqlSupport
* @param {object} rule
* @param {object} AST
* @returns {object}
*/
var rule = self.change('sqlToRule', {
   id: id,
   field: field,
   operator: opVal.op,
   value: opVal.val,
   //The following line us the code I need
   to_upper: to_upper
}, data);

'to_upper' is a property I set to false by default and to true when its related button it's clicked.

The Question

How could I do this without adding these code to query-builder itself?

The plugin upper-rule

/**
 * @class ToUpperRule
 * @memberof 
 * @description Adds a "toUpper" checkbox in front of group conditions.
 * @param {object} [options]
 * @param {string} [options.icon_checked='glyphicon glyphicon-checked']
 * @param {string} [options.icon_unchecked='glyphicon glyphicon-unchecked']
 */

QueryBuilder.define('upper-rule', function(options) {
	var self = this;
	
	this.on('afterInit', function() {
        self.$el.on('click.queryBuilder', '[data-upper=rule]', function() {
            var $rule = $(this).closest(QueryBuilder.selectors.rule_container);
            var rule = self.getModel($rule);
            rule.to_upper = !rule.to_upper;
        });
        self.model.on('update', function(e, node, field) {
            if (node instanceof Rule && field === 'to_upper') {
                self.updateRuleToUpper(node);
            }
        });
    });
    // Init "toUpper" property
    this.on('afterAddRule', function(e, rule) {
        rule.__.to_upper = false;
    });

    if (!options.disable_template) {
        this.on('getRuleTemplate.filter', function(h) {
            var $h = $($.parseHTML(h.value));
            $h.find(QueryBuilder.selectors.rule_header).prepend(
                '<button type="button" class="upper btn btn-xs btn-default" data-upper="rule">' +
                '<i class="' + options.icon_unchecked + '"></i> ' + self.translate('UPPER') +
                '</button>'
            );
            h.value = $h.prop('outerHTML');
        });
	}
	// Export "toUpper" to JSON
    this.on('ruleToJson.filter', function(e, rule) {
        e.value.to_upper = rule.to_upper;
    });
    
     // Read "toUpper" and hide or show based on the rule's type
    this.on('jsonToRule.filter', function(e,rule) {
		if (rule.to_upper)
			e.value.to_upper = rule.to_upper;
		if (e.value.filter.type != "string" || e.value.filter.plugin != null) {
			e.value.$el.find('.rule-header').find('.upper').hide();
			rule.to_upper = false;	
		} else if (e.value.filter.type == 'string') {
			e.value.$el.find('.rule-header').find('.upper').show();
		}
	});

    // Export "toUpper" to SQL
    this.on('ruleToSQL.filter', function(e, rule) {
		if(rule != undefined && rule.to_upper && (rule.type === "string" && rule.value[0] != undefined)) {
			if (rule.value[0] instanceof Array) {
				rule.value[0][0] = rule.value[0][0].toUpperCase();
				var b = rule.value[0][0];
			} else {
				rule.value[0] = rule.value[0].toUpperCase();
				var b = rule.value[0];
			}
			var operatorAndVal = e.value.replace(rule.field, "").toUpperCase().replace(rule.value,b);
			return e.value = " UPPER(" + rule.field + ") " + operatorAndVal;
		}
    });
	 // Parse "UPPER" function from sqlparser
    this.on('parseSQLNode.filter', function(e) {
		if (e.value.right != null && e.value.left.name === 'UPPER') {
			e.value.to_upper = true;
			e.value.left = e.value.left.arguments.value[0];
			delete e.value.left.name;
			
		}
    });

	// Request to create sub-rule if the "UPPER" flag is set
    this.on('sqlGroupsDistinct.filter', function(e, data, i) {
        if (data.to_upper && i > 0) {
			debugger;
            e.value = true;
        }
    });
}, {
    icon_unchecked: 'glyphicon glyphicon-unchecked',
    icon_checked: 'glyphicon glyphicon-check',
    disable_template: false
});

/**
 * @name toUpper
 * @instance
 * **/

Utils.defineModelProperties(Rule, ['to_upper']);
QueryBuilder.selectors.rule_upper = QueryBuilder.selectors.rule_header + ' [data-upper=rule]';

QueryBuilder.extend({
	updateRuleToUpper: function(rule) {
		var options = this.plugins['upper-rule'];
		rule.$el.find(QueryBuilder.selectors.rule_upper)
		.toggleClass('active', rule.to_upper)
		.find('i').attr('class', rule.to_upper ? options.icon_checked : options.icon_unchecked);

	/**
         * After the rule's upper flag has been modified
         * @event afterUpdateGroupToUpper
         * @memberof module:plugins.UpperGroup
         * @param {Rule} rule
         */

        this.trigger('afterUpdateRuleUpper', rule);
        
        this.trigger('rulesChanged');
	}
});

First of all I don't understand what you are trying to achieve with model.filter.to_upper = item.to_upper;
The filter is the object from the configuration, it is shared accross all rules using this filter, surely you don't want to alter it with the data from one rule.

ruleToJson is what is used to parse additional data from JSON, and you already implemented it.


Now for the modification in the SQL plugin, it is very simple, you are exactly modifying the params of the sqlToRule event, so just use this event.

this.on('sqlToRule.filter', function(e, data) {
  e.value.to_upper = data.to_upper;
});

With only this addition in your plugin it seems to work as intended

Indeed, it works like a charm. I did not consider the potential of the event right there. Thank you for the helpful prompt.