Toolbox plugin - collection of utility elements

BTW, I worked around not being able to figure out why external scripts were not available in Expression by passing things off to a Run JavaScript.

I was thrilled to find that bubble_fn_index; works in Expressions.

But the explanation you provided here will let me streamline this greatly.

Thanks, again, @mishav!

1 Like

BTW, this worked great. And it is faster to have all my moment-based conversion in the Expression, rather than handing it off.

Hey @mishav: Have you ever run into a situation where delay until page loaded (entire) does not work? I have a page which is kinda like any of my other pages where it needs to do the type of date transform I’m doing.

It has an Expression on it (exactly like other ones I’ve been modifying) where I’ve moved the expression code into the Conditional tab and set the condition as page loaded entire.

However, rather than working (as all my other Expression things do), this page throws an error telling me moment-timezone is not defined. That is, it seems NOT to wait until page is fully loaded before trying to execute the Expression’s code.

I’ve looked for all the obvious things like “is there a duplicate of the Expression hiding somewhere?” (no), etc.

The only fix I’ve been able to find is to make the Element that contains the Expression NOT visible on page load and then manually display it. (Like, have a button to show it.) When I do this, it executes properly.

Anyway, it’s got me stumped as to why this specific page would act any differently…

Hi @keith, interesting you should ask, I encountered the same thing today, the page loaded event happened before the elements loaded, and it showed the sequence in the browser console.

Maybe something has changed recently in Bubble? For example, have you tried creating a new app?

My example was for a loading javascript library. I ended up adding an additional wait in a page load run javascript by doing this:

$(document).ready(my_init_function)

Do the other pages that used to work, still work?

EDIT - possible workaround, setup the above document ready on page load, feed a value in Javascript To Bubble, and use the value in the Expression condition.

Yes the other pages still work fine. I’ve not tried creating a duplicate from the problem page and seeing if that changes anything. But I will.

@mishav So, I cloned the page and the cloned page exhibits the same behavior. (Screenshot of the error thrown highlighting the problem Expression pasted below. This doesn’t tell you anything you didn’t already know, but FYI!)

I haven’t tried creating a new app.

Like you, all I’m doing here is waiting for libraries to load (moment, moment-range, moment timezone).

Sounds to me like if “page loaded entire” is being fired before that’s really true, that would be a bug. (And that’s clearly what’s happening here, though I cannot explain why it happens on THIS page and not on others.)

If you have a simple example that shows the incorrect behavior, could you notify support@bubble.com and create a bug report? My own page where this happens (and pages where it does not happen) is just a bit too complex, I fear, to be a good illustration of the problem.

Best regards,
Keith

I’m not sure I can raise this as a bug, because do Bubble actually say that “page is loaded” means all elements have finished loading? They’d probably make an exception for this when considering added HTML/javascript.

Today the same problem wasn’t occurring, and coincidentally, my internet was at reasonable-ish speeds. So I ran an experiment, I throttled the speed using Chrome’s dev tools, and did a “hard reload”.

Sure enough, the problem reappeared, on page loaded event the library hadn’t finished loading / initialising. Even my workaround of wait for document ready didn’t work.

The is using a HTML element with <script src="...">. If I switch to jquery’s getScript() this means loading at a later stage, but triggers a js event when loading is done, so should be more reliable, and testing shows this works on a slow connection. Unfortunately it is a much more complex way of loading, especially for multiple libraries.

Ideally the libraries would be loaded in the page header, but this is not always appropriate.

Hmmm. Well, Page is loaded (entire) should be akin to “ready function” right?

I dunno what to think.

I’ve gotta go back to my problem page and see if that’s still an issue.

The thing that concerns me is there was no inconsistency before, but now it seems to be a “crap shoot” as to whether the libraries load before the Expression executes…

(Aside: Moment Timezone is a potentially slow-loading library as it must be loaded with a bunch of supplemental Timezone data. It’s just interesting that this issue did not come up until very recently.)

You might want to stop answering my questions as it seems that every software thing I touch immediately breaks. It’s like the old “show and tell” joke:

“Show me a new feature and I’ll tell you about where it’s broken.” :stuck_out_tongue_winking_eye:

Consider your breaking stuff as an “app hardening exercise” :_) Like how a few years ago there was a ceramic ball created that got harder every time it was bashed with a hammer.

I agree about the inconsistency, and it could be related to the timing of the different CDN servers involved, like how many attacks they are fending off.

Is there a way you can incorporate asynchronous (or lazy) load into your page, so the user can do something else while waiting for it? "Here is a crossword to solve … "

Lolz. Well, that’s why my solution was to hide the calendar in this particular page behind a button press…

I’m just glad this isn’t happening on all pages as that would be very very bad.

I missed this comment/suggestion when I first read your comment, @mishav

Today, I discovered another page exhibiting flaky “page is loaded (entire)” behavior. Instead of having the scripts in an HTML element, I moved them to the page object’s “HTML for Header” field and this seems to be more robust.

(At least in my particular case, loading these moment-related scripts in the header is fine.)

FWIW, while I was working on this stuff today, the problem became more frequent and after a while, I got the “We were unable to load your application data…” error on page loads. As usual when this happens, the problem quickly rectified itself, but it seems like these issues (Expressions that should wait to fire until page is loaded (entire) firing anyway) and Bubble exhibiting slow accessibility are related.

(I guess what’s going on sometimes is that things are taking too long to load and so Bubble decides to proceed with execution of certain things anyway, maybe?)

Anyway, thanks for the tip about moving scripts to headers. Hopefully this will alleviate the seemingly random issues around this.

status.bubble.is . update: you know :wink:

1 Like

Yeppers. (But the issue that I’m discussing with Misha is in fact something we see on certain pages even when things are working properly. I guess today I could have guessed that eventually I’d see the “can’t load app data” error page… Could kinda see it coming on!)

1 Like

Hi @mishav, Hey, I found a very “Bubbly” way – and “Toolboxy” way – to ensure that long-loading scripts (such as moment / moment-timezone) are loaded before firing off an Expression.

(As a quick recap: My use case has been that I’ve modified the “custom calendar” technique originally offered by @codurly to work in a more “timezone-aware” way. The new Expression code that generates the calendar dates does so using moment/moment-timezone. The issue of late has been that – unless the scripts for those are put into the HTML header of the page, the Expression will try to fire BEFORE those libraries are fully loaded.

And as you and I recently discovered, making the Expression code only load on “Page is loaded (entire)” is not 100% bulletproof. In fact, that event can happen substantially before moment/moment-timezone is ready.

I recently took the Calendar widget I’d built and turned it into a Reusable Element, further complicating the script loading issue. I didn’t want to have to move the scripts that the RE relies on into the parent page itself… And I also didn’t want to waste time loading those scripts twice [even though that’s not really an issue with moment, but of course it could be with some other types of scripts].)

So here’s what I’ve done:

In my case, there are in fact 2 things to wait for. First, moment will become ready and afterward the moment-timezone functions (moment.tz) will become ready. Here’s how I check for those and how I’ve configured the Expression element:

1. Rather than executing the Expression code on “page is loaded (entire)”, I’ve set it to wait until the value of a JS to Bubble element reaches a certain status value. Like this:

2. In my RE’s equivalent of “when page is loaded” workflow, I have a Run Javascript step. What this JavaScript does is evaluates the “typeof” of moment. This seems to be the most correct JavaScript way of evaluating whether or not a function is ready to go. If moment() is not yet callable, evaluating typeof(moment) will return ‘undefined’. If it ready, typeof(moment) will return ‘function’. Note that evaluating typeof does not actually call or attempt to trigger the function, so errors are avoided.

If moment is not yet ready, the script executes setTimeouts iteratively until it is. (This of course doesn’t block execution of anything as it’s all asynchronous JavaScript.) That looks like this:

You’ll see that on success, we fire bubble_fn_moment(1). That JS to Bubble element is set to trigger and, on reaching this state, executes the next step of our wait, which is just like the first step…

3. But now we are waiting for moment.tz to be callable. Like this:

On success, this Run JavaScript fires bubble_fn_moment(2), indicating that we are done waiting.

Upon reaching the value of 2, the Expression element becomes active as configured in Step 1, above.

This technique seems to be both quite performant and very reliable.

Testing with CPU throttling, slowed network, and cache disabled in Chrome Dev Tools yields console logs that look like this:

You’ll note that we waited for about 800 ms for moment to load beyond when Bubble tells us “page is loaded”, and an additional 100-200 ms for moment.tz to become fully ready.

Without throttling, we of course wait much less. Here’s what that looks like on my machine with net and cpu unthrottled (but still disabling cache to ensure we are not fetching these things from local storage):

Seems to work just fine, so thought I’d share this idea with you.

3 Likes

You’re putting your app in a straight jacket and handcuffs : )

Good idea. You’ve set up polling using a low overhead method.

Some small improvements if you feel like modifying something that is already working …

Can use setInterval, and cancel it when done.

Can check for both libraries loaded in the same function. This would be a little more robust, to make no assumptions about the loading order, to be more general and also when for a weird reason, moment takes longer to load than moment-timezone.

High-five for a robustness improvement exercise, Keith : )

EDIT - I wonder if, by including the JavascriptToBubble result in a dynamic expression, you’ve made the expression reactive to the result, i.e. reactive to the loading of libraries.

Then it could be part of the Expression expression, and would rerun when the loading is complete … not useful for your particular use case, but might be for other things.

1 Like

Hey, @mishav: Couple of things… 1. I actually could not do both tests at once. Because moment-Timezone extends moment (like moment.tz() ), if you:

typeof(moment.tz)

Before moment is a function, JavaScript will throw an error. This kind of seems silly but it is true. Hence the two steps.

I believe this is because the syntax something.anotherThing is unambiguous. If you check typeof that, you are saying, “OK, JavaScript, tell me about the anotherThing method of function something.” To which JS replies, “something is not a function” if indeed something is undefined. It’s the same as if you call the function before it is ready.

(Though I realize the single Run JavaScript could do both steps of these checks… good point.)

  1. I haven’t tested this in all browsers everywhere, but I could not construct a scenario in which these scripts finished loading AFTER the “Built on Bubble” banner appears in the console.

It would seem that whatever triggers THAT action is the true “page if fully loaded” event and it would be handy if Bubble exposed that condition, whatever it might be!

  1. I haven’t yet tested whether setInterval might be better than using recursive setTimeout or whether there’s any difference at all. Consensus on stackoverflow seems to be setTimeout but this may not be grounded in fact.

As for having this check inside of Expression: I had wondered about that. And I had initially tried there but got hung up on the “two wait steps” issue — that is, I could not figure that out until I broke the problem down. (I was also curious about when the workflow Run JS step might execute relative to Expression.)

Anyway, you COULD include this type of check inside the Expression, but in my case this expression gets evaluated anytime the user browses to another month (the script generates the 42 dates displayed in the calendar).

While a couple of IF checks are not high overhead, it’s just better for me if we initialize once and then know with confidence that we can allow Expression to do its thing as fast as possible.

This does lead me to a question: WHAT ARE the rules for Expressions firing? It seems that it fires (1) when its script is allowed to load and (2) whenever some underlying Bubble value that it depends on changes, yes?

Should Expressions have a trigger event? It might be nice in some cases if one could force evaluation by sending a trigger event. (I realize that duplicates some functionality of Run JavaScript.)

Thanks again for your awesome plugins!

1 Like

Try this:

if (typeof moment == 'function' && typeof moment.tz == 'function') {
  // both are ready
}

I wouldn’t rely on the banner as indicating any specific stage, there is no documentation or expectation of its timing, and so could change its timing at any time.

If you want to try loading the libraries “later”, try something like this:

setTimeout(function(){
  $.getScript('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js', function() {
    $.getScript('https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.21/moment-timezone-with-data.min.js')
  })
},2000)

I was referring to checking the result of the JavascriptToBubble, as that makes the check dynamic. Yes I agree its not suitable for your Expression.

Not a significant difference in performance, more a coding style thing, recursion vs explicit repetition.

Yes that’s generally the case. Some additional times it can be triggered:

  • When the screen size changes.
  • When inside a repeating group and gets bumped to a different display position.

Do you mean for the Expression to send a workflow event? Hmmm maybe, but having the multiple triggering times as described above might make for too many events to be useful.

Do you mean for the Expression to receive an event (or a function call) from javascript, before evaluating? Hmmm maybe that too.

Keep up this train of thought, a few examples might help to design a New Improved™ Expression element.

: )

This will not work. This is a very subtle JavaScript parsing thing:

Evaluating typeof something (or string version typeof(something)) is always possible. The object something may be undefined and that’s OK.

However, evaluating typeof something.someotherthing (or string version typeof(something.someotherthing)) does not mean:

“What’s the type of something? And further, what is the type of someotherthing?”

What JS takes it to mean is:

“What is the type of someotherthing, a property of something?”

If something is not defined, JavaScript throws an error. Because we asked it to check a property of something. If something is undefined, JavaScript cannot check properties on it. Undefined things have no properties and apparently we are supposed to know this! :astonished:

(Another way of saying this: undefined.undefined is NOT undefined. Undefined.undefined is a syntax error!)

So, I guess the second part needs to be written as a function (one that returns false WITHOUT checking something.someotherthing before something !== undefined, but once something is defined can safely check something.someotherthing without erroring out).

More this. Like it could be forced to re-evaluate by triggering it (like from a function call). Could even have a “manual” mode in which the expression ONLY evaluates when asked to (again, via a function call). Might be useful…

Javascript stops evaluating if the first part returns false, and the second part has AND condition &&.

Something like JavascriptToBubble, but when invoked by javascript or a workflow step it evaluates an expression? Could include the parameter as part of the expression …

bubble_fn_recipe(‘apple’) → or workflow step run_recipe, parameter: ‘apple’ →
ingredients.push(parameter)
ingredients.push(dynamic expression: ‘flour’)
bowl.add(ingredients)
bowl.mix().spill(floor)
floor.mop()
return bowl.soggyRemnants()

1 Like