Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding event listeners before or after init? #536

Closed
oller opened this issue Jul 25, 2017 · 8 comments
Closed

Adding event listeners before or after init? #536

oller opened this issue Jul 25, 2017 · 8 comments
Labels
bug Identified bug which needs a fix
Milestone

Comments

@oller
Copy link

oller commented Jul 25, 2017

Hello all, I'm having a bit of a conundrum binding event listeners to querybuilder.

As per my code below, I'm adding the.on() event listeners chained before the init of querybuilder(). This works in as much as all the events are detected correctly, including afterInit.

        const builder = view.$el.find("#builder");

        builder.on("afterAddGroup.queryBuilder", (event, group) => {
            console.log("on after add group");
            // Each time a group is added, it rewrites the dom, always ensure we
            // apply the applicable template options back to the group dropdown
            view.updateTemplateList(group);
            view.applyPermittedGroupNegation(group);
            view.updateSQLPane();
        })
        .on("afterDeleteGroup.queryBuilder", () => {
            console.log("on after delete group");
            view.applyPermittedGroupNegation();
            view.updateSQLPane();
        })
        .on("afterUpdateGroupCondition.queryBuilder afterUpdateRuleFilter.queryBuilder afterUpdateRuleOperator.queryBuilder afterUpdateRuleValue.queryBuilder", () => {
            console.log("values updated");
            view.updateSQLPane();
        })
        .on("afterMove.queryBuilder", (event, node) => {
            console.log("after drag move");
            if (node.constructor.name === "Group") {
                // User has dragged a group
                view.applyPermittedGroupNegation(node);
            }
            view.updateSQLPane();
        })
        .on("afterInit.queryBuilder", () => {
            console.log("after init");
            view.updateTemplateList(view);
        })
        .queryBuilder({
            rules: view.getDefaultRoot(),
            allow_empty: true,
            plugins: {
                "not-group": null,
                sortable: { icon: "icon-button icon-menu" }
            },
            display_empty_filter: false,
            filters: view.filters,
            icons: {
                error: "icon-button bg-seagreen c-white icon-infoFilled"
            },
            templates: {
                rule: templateRule(),
                filterSelect: templateFilter(),
                operatorSelect: templateOperator(),
                group: templateGroup()
            }
        });

The issue I'm having is that in some of the event listeners I'm trying to validate the query (on the fly validation) and if valid updating a SQL query preview. These all error with the following error. It's as if the querybuilder was initialised with no filters, when I know this is not the case, as that view.filters property is populated before the init, there's no async issues here, and before adding this on the fly validation, the builder was working perfectly.

From going through the stack trace, data is returning undefined, so it seems a new instance of the builder is being initialised, a new instance which won't have filters etc. defined. I'm unsure why this is the case though, as I'm following the same pattern in the demo of $('selector').queryBuilder('method'); to run a method on an already initialised builder.

if (!data) {
this.data('queryBuilder', new QueryBuilder(this, options));
}

Uncaught Error: Missing filters list

Utils.error | @ | query-builder.js?49d5:3520
QueryBuilder.checkFilters | @ | query-builder.js?49d5:558
QueryBuilder | @ | query-builder.js?49d5:162
$.fn.queryBuilder | @ | query-builder.js?49d5:3667
getSQL | @ | builderaddedit.view.js?706c:382

I've tried chaining the .on() event listeners after the queryBuilder() but then half of them don't fire, for example binding afterInit after you've initialised the builder is pretty pointless.

It's a pretty standard use-case, so I'm sure i'm not trying the impossible. Is there a suggested way I should be listening to these events?

Apologies if i'm doing something silly, I'm following the docs as closely as possible, I'm a bit stuck! A fantastic library and set of docs otherwise, I've got a lot done before hitting this issue!

Thanks in advance

@oller oller changed the title Adding event listeners before or after init? Events triggered Adding event listeners before or after init? Events triggered either don't find instance, or can't be triggered because they're added after init Jul 25, 2017
@mistic100
Copy link
Owner

You don't show your full code, what is your selector ? why don't you just do builder.queryBuilder('method'); ?

@oller
Copy link
Author

oller commented Jul 25, 2017

Thanks for the response, allow me to give a more reduced example:

        const builder = view.$el.find("#builder");

        builder.on("afterAddGroup.queryBuilder", (event, group) => {
            console.log("on after add group");
            builder.queryBuilder("getRules");
        })
        .on("afterDeleteGroup.queryBuilder", () => {
            console.log("on after delete group");
            builder.queryBuilder("getRules");
        })
        .on("afterUpdateGroupCondition.queryBuilder afterUpdateRuleFilter.queryBuilder afterUpdateRuleOperator.queryBuilder afterUpdateRuleValue.queryBuilder", () => {
            console.log("values updated");
            builder.queryBuilder("getRules");
        })
        .on("afterMove.queryBuilder", (event, node) => {
            console.log("after drag move");
            builder.queryBuilder("getRules");
        })
        .on("afterInit.queryBuilder", () => {
            console.log("after init");
            builder.queryBuilder("getRules");  //I throw the error, but only because I run first, any of the getRules methods will
        })
        .queryBuilder({
            rules: view.getDefaultRoot(),
            allow_empty: true,
            plugins: {
                "not-group": null,
                sortable: { icon: "icon-button icon-menu" }
            },
            display_empty_filter: false,
            filters: view.filters,
            icons: {
                error: "icon-button bg-seagreen c-white icon-infoFilled"
            },
            templates: {
                rule: templateRule(),
                filterSelect: templateFilter(),
                operatorSelect: templateOperator(),
                group: templateGroup()
            }
        });

In this case, the first error comes from the afterInit event listener:

Utils.error | @ | query-builder.js?49d5:3520
QueryBuilder.checkFilters | @ | query-builder.js?49d5:558
QueryBuilder | @ | query-builder.js?49d5:162
$.fn.queryBuilder | @ | query-builder.js?49d5:3667
(anonymous) | @ | builderaddedit.view.js?706c:119  // This is the getRules() in the afterInit listener

A step through of the stack trace error: https://giphy.com/gifs/3ohzgWmRhhtvRgoko0/fullscreen

You can see querybuilder thinking there's no data and instantiating a new builder, which I suppose would have no filters, I suspect the issue is how/why that is occuring?

If I were to comment out all the getRules() methods this builder would load perfectly fine.

Just to re-iterate, a great library with great docs, I've got so far already! Thanks again for your help.

@mistic100
Copy link
Owner

I understand the behaviour but I really don't understand what you are trying to do, in afterInit the rules will always be empty, the root group hasn't even been created (as said in the description of the event).

@mistic100
Copy link
Owner

@oller
Copy link
Author

oller commented Jul 25, 2017

Thanks again for the quick response, the issue isn't with afterInit specifically, any of those eventListeners hit the same issue when running a number of methods.

Say if I comment out the getRules line in the afterInit event listener. I'll get the same error, but this time, the afterAddGroup event will trigger the same error. In that case there must be a group, therefore there must be some rules to get?

I can even change all the getRules to validate and the same error will occur.

As I mentioned before, from the stack trace it looks like it doesn't detect the data when trying to run these methods, so instantiates another instance, which doesn't have any filters applied as there's no options object and then errors.

if (!data) {
this.data('queryBuilder', new QueryBuilder(this, options));
}

I'm not doing anything complicated with the selector, so I don't understand why this is the case?

--

The use case is to have a SQL preview pane updating as frequently as possible when users change values in the builder, so on an event which I deem as a change to the builder (as there's no universal change event), I run validate and if valid, getSQL and populate a preview pane. This is seemingly on the fly.

@mistic100
Copy link
Owner

Explanation : afterInit and the first batch of other events is called at the end of the constructor (which hasn't returned yet), which mean the QueryBuilder object does not actually exists yet, thus is not assigned to the DOM element.

I will but the last few lines in an external method called avec complete creation.

@mistic100 mistic100 added the bug Identified bug which needs a fix label Jul 25, 2017
@mistic100 mistic100 added this to the 2.4.5 milestone Jul 25, 2017
@oller
Copy link
Author

oller commented Jul 25, 2017

This makes sense, thanks again for the prompt replies, shall keep an eye out for 2.4.5

Then in theory you can chain all the events you want .on(..).on(..).queryBuilder(...) and they ought to fire and have access to QueryBuilder object with all its options?

👍

@mistic100 mistic100 changed the title Adding event listeners before or after init? Events triggered either don't find instance, or can't be triggered because they're added after init Adding event listeners before or after init? Jul 25, 2017
@oller
Copy link
Author

oller commented Jul 25, 2017

Thanks for this. To confirm, the issue I was facing wasn't specifically to do with the afterInit method, it's any event listener chained before the queryBuilder, i.e. builder.on(...).on(...).queryBuilder(...);

Does not seem to have access to the queryBuilder object via its selector and therefore does not have access to the options and filters when any of those event listeners fire, the selector returns empty so it creates another with no options which causes these errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Identified bug which needs a fix
Projects
None yet
Development

No branches or pull requests

2 participants