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

Cannot swap out input for custom input without breaking the "getRules" method.

NickCTLVR opened this issue · comments

This isn't so much of an issue as it is a question.

I'm trying to add a custom select to rules that allows the user to toggle between two types of value controls within the same rule.

State one:

The user clicks add rule. With the customization I've made, there is a select to the right of the value input that allows the user to toggle between whatever the default input is provided by the rule/filter, or a list of values provided a select.

State two:

The user chooses the select that I've added to the queryBuilder. Under the hood, I am calling .empty() on the .rule-value-container and replacing with my select. I realize I've ventured into customized territory and run the risk of breaking the builder.

The problem:

When the user toggles the custom select I've added to the query builder, the query builder throws a validation error. That's all well and good. The real issue is that the "getRules" method now returns null. My question is, is there a binding that I can add manually so that the control I'm adding is still able to provide the correct values? I inspected the elements that are added and basically replicated them and their associated attributes, e.g. for input type text, it has a class="form-control" and a name="builder-basic_rule_value_0. I realize this is kind of a long shot question, but we're behind the eight ball a bit, so I thought I'd ask.

Here is the relevant code for illustration:

`
//get the rule value container

let valueControlContainer = $(`#builder-basic_rule_${ruleCount} .rule-value-container`);

if (type === "fieldSystem") {
    // Get Child of container, which is the child generated by the query builder
    let originalControlType = valueControlContainer;

    let artifactTypeID = $("#artifactType").val();

    fieldService.fetchFieldsAsync(artifactTypeID).then((fields) => {
        let fieldControl = $(`<select data-previous-input-type='${originalControlType.html()}' class='form-control' name='basic-builder_rule_${ruleCount}_value_0'></select>`);

        fields.forEach((f) => {
            // Attach fields as options to select
            const optionVal = {
                artifactID: f.FilterID,
                fieldTypeID: f.FieldTypeID,
                choices: f.Choices,
                label: f.FilterDisplay
            };
            
            const option = new Option(f.FilterDisplay,  JSON.stringify(optionVal));
            fieldControl.append(option, undefined);
        });

        
        // Remove the default input control (my hunch is I'm severing some binding done under the hood, I've yet to look more //closely at the source code
        valueControlContainer.empty();

        // Add my own selector
        valueControlContainer.append(fieldControl);    
    });`

The problem is not the name of the input, but the fact that you break the event listener created here https://github.com/mistic100/jQuery-QueryBuilder/blob/dev/src/core.js#L711

Why do you destroy the whole select ? you could simply change it's options, that way the form control element stays the same.

Thank you for the response and the line number in the source code. You may save a project from hitting an iceberg.

To your question as to why I've destroy or called empty() on the select, this is because I've added another select to the right of the rule value input that changes the control, e.g., if you choose one option in the select to the right of the rule value, it removes the input and replaces it with an option select. Visa-versa, if you select the other option, it re-adds the original input. If you could provide a recommended way of re-binding the inputs when they are swapped out so that the "getRules" method still returns the object, you'd save me a ton of heartache.

Thank you again, your control is awesome and it's working beautifully for our use case.

I've created a codepen to illustrate the problem I'm trying to solve. In order to see the scenario, you must add a new rule as I haven't yet worked out how to add the additional input to existing rules.

Code Pen

  1. Add a new rule
  2. Select the filter
  3. Switch to System/Field
  4. Switch back to Constant

You will see (as you correctly stated) that on step 3, the binding is broken. The same problem exists on step 4. I tried getting the rule and passing it to the "createRule" method, but the input is not yet created and formatted properly.

I realize we are kind of hacking the control a bit and that there is the potential for downstream bugs, but that's ok for now.

Any help would be appreciated.

General remarks :

  • rule.model.group.id => rule.parent.id because the parent is not necessarly the root
  • use rule.id instead of reconstructing it
  • use rule.$el to get the rule container
  • your thing with the data-previous-input-type='${originalControlType.html()}' is honestly disgusting....

https://querybuilder.js.org/#inside_the_box


here is a working demo by what I understood https://jsfiddle.net/mistic100/nwzboax8/

Well, I appreciate your honesty and, admittedly, it was already in ugly territory once we started hacking the original rule.

I want to thank you again for your help and the control itself. Unfortunately, are requirements are stretching the control itself to the breaking point, so we've had to pull the control out of the application and design something that more fits our needs.

Again, thank you again.