• Hello Fabrik Community

    Fabrik is now in the hands of the development team that brought you Fabrik for Joomla 4. We have recently transitioned the Fabrik site over to a new server and are busy trying to clean it up. We have upgraded the site to Joomla 4 and are running the latest version of Fabrik 4. We have also upgraded the Xenforo forum software to the latest version. Many of the widgets you might have been used to on the forum are no longer operational, many abandoned by the developers. We hope to bring back some of the important ones as we have time.

    Exciting times to be sure.

    The Fabrik 4.0 Official release is now available. In addition, the Fabrik codebase is now available in a public repository. See the notices about these in the announcements section

    We wish to shout out a very big Thank You to all of you who have made donations. They have really helped. But we can always use more...wink..wink..

    Also a big Thank You to those of you who have been assisting others in the forum. This takes a very big burden off of us as we work on bugs, the website and the future of Fabrik.

Calc Ajax validation not triggered on Cascading Dropdown

Bauer

Well-Known Member
I'm not sure if this is a known bug (and I sure wish it worked) - but if you use a cascading dropdown as an Ajax watch element for a calc element, it doesn't work.

In a calc element that is set to watch both a cascadingdropdown and the database join watch element for it, the calc element Ajax is only triggered by changes made to the cascadingdropdown watch element - but not the cascadingdropdown element itself. (At least that's what I get when the controlling dbjoin element and the cascading dropdown element are both set to use checkboxes.)

Update: See new thread 'Issues with labels' - http://fabrikar.com/forums/index.php?threads/issues-with-labels.40951/ (just wondering if this is related)
 
Just trying to get some attention to this problem - as no one has responded.:(

To clarify this a bit. 3 elements are involved.
  1. A calc element
  2. A databasejoin element (multiselect checkboxes)
  3. A cascadingdropdown element (multiselect checkboxes - with the datbasejoin element as the 'watch' element)
The calc element references both the databasejoin and cascading dropdown elements.

When the form first loads, the calc element will get updated from any changes to the cascading dropdown element just fine.
And, when the form first loads, the calc element will get updated from changes made to the databasejoin (the CCD watch element) just fine.

BUT once a selection is changed in the DBJ element, then the CCD element no longer triggers the Ajax for the calc element.

In other words, it appears that the cascading dropdown is removed from the 'watch' options of the calc element once the watch element of the cascading dropdown has been changed.

It seems like, in one or both of those Ajax calls (from the calc and/or CCD), there needs to be some code added - to refresh all 3 of those elements so that they all work like they do/did when the form was initially displayed.

This is an important form to my project, as the calc element is used to display a 'Shopping Cart' for the PayPal form plugin. I have been fighting with this for a week and am anxious to get the issue resolved. (Or know that I'll need to come up with another method if this issue cannot be easilly fixed.)

And of note to Hugh (so you don't think I've just been sitting on my hands waiting for someone to fix this)..
I have tried for days to find/fix the problem. But I am not all that versed in javascript (especially these 'mini' files used).

A few things I would like to note from my debugging thus far...
The calc-min.js seems to have a typo - and greatly differs from the calc.js (non-minfied) code.
e.g.
Code:
attachedToForm:function(){if(this.options.ajax){{var a;this.form}
Shouldn't that be
Code:
attachedToForm:function(){if(this.options.ajax){{var a=this.form}
???

Also, in that calc-min.js I added some code so I could see the value of 'this.options.observe' in the console.log - and discovered that there were duplicates in the 'watch' options.

I could also see from that console.log that any OR ('||') condition that was used in the placeholders of the calc element was not being removed either. And there would sometimes be empty an string in the 'this.options.observe' array.

Assuming that this would cause some code to get run multiple times (and/or suspecting it was part of the bug), I set out to fix that.

SO.... Upon examining the code in the function elementJavascript() in calc.php, I saw what was causing those things to happen and made these changes to fix it -
  1. The array_unique() function was being called BEFORE changes to remove the '_raw' part from the element name - that needed to be done after.
  2. The function to remove '_raw' was not working/correct - so I just added that to the function that removed the '{' and '}'
  3. I added a function to remove any OR condition that was part of the element placeholders ('||xxxx')
  4. I moved the array_unique function to the bottom - after 1, 2, & 3
  5. I then removed any empty strings from that array - as the final $obs to pass in the Ajax call.
So, I have included below an updated version of the elementJavascript function (with old lines left intact, and with my comments) in calc.php...
PHP:
    public function elementJavascript($repeatCounter)
    {
        $id = $this->getHTMLId($repeatCounter);
        $opts = $this->getElementJSOptions($repeatCounter);
        $params = $this->getParams();
        $calc = $params->get('calc_calculation');
        $obs = preg_replace('#\s#', '', $params->get('calc_ajax_observe'));
        $obs = explode(',', $obs);
 
        if (preg_match_all("/{[^}\s]+}/i", $calc, $matches) !== 0)
        {
            $matches = $matches[0];
            $obs = array_merge($obs, $matches);
        }
 
        // Need to move this to after removing '_raw' and '||xxx'
        // $obs = array_unique($obs);
 
        foreach ($obs as &$m)
        {
            // $m = str_replace(array('{', '}'), '', $m);
            // just include '_raw' here
            $m = str_replace(array('{', '}', '_raw'), '', $m);
 
            // $$$ hugh - we need to knock any _raw off, so JS can match actual element ID
            // this wasn't working - see above
            // $m = preg_replace('#_raw$#', '', $m);
 
            // what about removing any OR ('||') in placeholder?
            $m = (strpos($m,'||')) ? str_replace(substr($m,strpos($m,'||')), '', $m):$m;
        }
 
        // apply array_unique function last
        $obs = array_unique($obs);
        // then remove any blank strings
        $obs = array_filter($obs);
 
        $opts->ajax = $params->get('calc_ajax', 0) == 0 ? false : true;
        $opts->observe = array_values($obs);
        $opts->calcOnLoad = (bool) $params->get('calc_on_load', false);
        $opts->id = $this->id;
 
        return array('FbCalc', $id, $opts);
    }

Please don't scold me for not doing this myself, but I really have little time in my day for anything 'extra' anymore. And I guess I'm just a prime example of the clich? - "You can't teach an old dog new tricks." :rolleyes:
 
Apologies, but as you know I have to prioritize paid support. Doesn't mean I don't value your work, it just means I have no choice but to deal with the paid forums before I even start looking in Community, and sometimes that means I'll go days without time to even scan for issues. I do look out for your posts, but if they appear when I'm in the middle of a particularly tough few days (like this one did), I won't have time. Yes, I know they are bugs which need fixing, but so are the other issues I'm dealing with, and that's pretty much the whole point of of the subscription system - it is part of the way we prioritize our time for fixing things. It's not the be all and end all, we factor in bug severity, how hard it is to fix, how many people it affects, etc. But it is an important factor.

I'm pretty sure I know where the issue will be with the calc not firing on the join / CDD change, I'll go take a look.

I'm not going to scold, just point out that it's been 3 years or so I've been trying to get you to use github so we can actually properly review and merge your fixes. Add up the time you have lost having to re-apply fixes and fixes that have gone un-noticed, and hence your time fixing them wasted ... investing a few hours in learning github would have paid for itself many times over. Not to mention my time in having to merge your changes by hand, without being able to do side by side diffs, which makes in 10x quicker and easier to work out what they are doing and spot any potential problems .. and like you, I don't have much time. So when I have several proposed changes, some as mergeable PR's in github, and some not ... guess which ones are going to get done first.

You are a smart guy, you could learn the basics in an hour or so if you put your mind to it, and I have even offered several times to give you one on one tuition / assistance to get you set up. Bottom line, if you want your changes reliably merged into Fabrik, you'll have to use github, otherwise it's going to continue to be a crapshoot as to whether I a) ever even see them and b) have time to do them by steam.

That said, I'll try and find time this weekend to look at those changes. But I can't guarrantee anything, as I'm working on a couple of other tough issues atm.

-- hugh
 
Do you have the "work in progress" Chosen (advanced) behavior enabled for your CDD?

If so, that will break any event handling, and the calc won't see the change. I just tested, and the only way I can get the calc to fail on a CDD change is to enable that feature.

-- hugh
 
Do you have the "work in progress" Chosen (advanced) behavior enabled for your CDD?

If so, that will break any event handling, and the calc won't see the change. I just tested, and the only way I can get the calc to fail on a CDD change is to enable that feature.

-- hugh
Thanks for your time, Hugh.
To answer your question - No I don't.

See attached images for a real mind-boggle.

I removed everything from the calc element except to return the placeholder for the databasejoin element. (And I even removed the cascading dropdown element)

Then I edited the layout file (fabrik-element-calc-form.php) and put some code to echo the value of $displayData['value'] before it is assigned to $d and then the value of $d['value'] after the array is copied. (Upon seeing that $d['value'] is the value displayed in the <span>tag)

The attachmnets are the results of that calc elment when the form loads (onload.png) - and then the results after I unchecked one of the checkboxes in the databasejoin elment (dbj_changed.png).

You'll notice that not only was the new value not reflected in the $displayData array - but the calc element somehow was returning 'undefined' as the result - despite the fact that the $d['value'] used inside the <span> by the layout file was not returning 'undefined' when echoed just before in the fabrik-element-calc-form.php file. My head-scratching avatar fits on this one!:confused:

Looking at that elementJavascript function code again this morning and I'm wondering why all of that manipulation of the calc code [ $calc = $params->get('calc_calculation'); ] - to pull out placeholders - is being done or needed? Why would you want to pull out every instance of a placeholder in the calc code and use it as part of the $ops sent to Ajax when there is already a configuration option for entering the elements that you want to watch to trigger Ajax?

IMO, if the user has enough knowledge to use placeholders in the calc they should have enough sense to specify which of those elements (or other elements in the form) should trigger the Ajax.

Just to test, I removed all the stuff being done with $calc by that elementJavascript() function (in calc.php) and it still seems to work (in a different calc element). I.e. it gets the correct values for any placeholders used in the calc as well as triggers the ajax when any watch elements are changed.

I suppose this is one where I don't understand the big picture or historical content - but it seems like unecessary code to me.
 

Attachments

  • onload.png
    onload.png
    4.6 KB · Views: 227
  • dbj_changed.png
    dbj_changed.png
    4.2 KB · Views: 229
There are of course reasons for the additional AJAX trigger placeholders, we didn't just wake up one day and decide to make life complicated for ourselves. It's for special case usage, when there are changes happening on the form with elements you aren't using in the calc itself, but you want to trigger it when they change. Rarely needed, but we had enough need for it we had to add it, as people started to use calc elements for more and more complex things. We originally intended it for fairly simple minded stuff, like concatenating first and last names into full names, etc., so simply extracting the elements they used in the calc and observing those was good enough. But it turned out to be such a useful "catch all" for doing things Fabrik doesn't do out of box, that people started to really go hog wild doing all kinds of stuff we never anticipated. And of course by the time it became evident we may sometimes need to watch stuff they don't use in the calc, and had to add that "additional" option , we couldn't just rip out the automatic extraction of the observed placeholders from the calc code, as that would have broken backward compat with every single existing usage of the calc element, requiring everybody to build the list of elements to watch.

I'll continue trying to replicate the problem, but so far everything is working for me as expected.

-- hugh
 
BTW, my advice when people start running in to intractable issues with calc's which I can't replicate (which quite often turns out to be down to some other kind of "custom" thing happening on the page) is to stop and rethink, and use form JS files and (usually) form PHP submission scripts and/or user AJAX instead.

While it seems like it should be fairly simple on the surface, the calc element (and the resulting dependencies on the rest of the code) is actually some of the most fiendishly mind bending element code we have. It's one of the top 3 elements by time spent working on it, ironing out issues. And any issues left with it will, by definition, be the nasty, corner case ones. Which isn't to say the solution isn't often really obvious and simple ... once I can replicate it ... but that's the big problem. if I can't replicate the problem, I have no way of debugging it.

So ... anything you can do with a calc, you can do by rolling your own JS and PHP code. And in the long run, if what you are doing is "complex" its often easier to maintain, and more efficient to do yourself.

That's not to say I won't continue to try and track this one down, but I can't guarantee a fix or a timescale, so if this is a roadblock, you may need to work round it.

-- hugh
 
Yeah - don't kill yourself over this, Hugh. I undertsand the complexities - and thanks for taking the time top explain the history.

I'm having some arguments with the client at the moment of just how this form will work - and I probably won't be using the cascading dropdown after all. I'm so frustrated with this project, and her never-ending changes, that I turned my computer off all day. That doesn't happen too often.

Believe me, if I can make it more simple, and still accomplish what I need - I will.:)
 
No problem, I know the feeling.

You have my email, and you know if you can't get my attention in the Community forum, and have really hit a brick wall on something you need to get a job finished, you can always get hold of me that way.

-- hugh
 
Ran into this issue today. I created a form js that reset another field in order to force the user to change another element. It's an okay solution. I tried using jQuery .trigger(), but that didn't work.

Workarounds work for me.
 
If you need to get a 'watch' to fire (like a calc watching another element), you typically need to do this ...

Code:
// get the Fabrik element object for the "watched" element
var el = Fabrik.getBlock('form_X').formElements.get('yourtable___yourelement');
el.element.fireEvent(el.getChangeEvent(), new Event.Mock(el.element, el.getChangeEvent()));

Bit of a pain, but works.

I'm trying to figure out how to get JQuery trigger() to play nice.

The reason for using el.getChangeEvent() rather than just hard coding 'change' is that some element types use 'change' and some use 'blur' as the event that things like calc and CDD elements "watch". So rather than trying to remember which is which, just use getChangeEvent().

-- hugh
 
We are in need of some funding.
More details.

Thank you.

Members online

Back
Top