3

Question about how to send a jQuery callback with an onSuccess: refresh from a textInput when the user presses [Enter]. We use the [Enter] press to trigger a search callback.

Our GS Seaside app uses HAProxy. This means the onSuccess: script is handled by a different gem than the one that handles the callback. Because of this, users will sometimes get the refresh because the callback, which to them looks like a lost input (a browser F5 refresh shows the right state). If running single gem or in a VW / Pharo image this problem does not come up.

I can work around the problem by using...

async: false;

...but that prevents me from show any kind of waiting feedback (I normally use a busy gif).

So, the question is: in a multi-web server configuration, how can you code a callback to...

1 - show a busy gif

2 - trigger the search callback

3 - refresh the display when done

...all in that order.

Using a form submission callback is a problem because multiple text inputs can trigger the search, since the callback is 'set value + do search', by virtual of the default [Enter] press.

For the JS callback, I'm using...

    self onKeyPress: (
    (JSStream 
        on: '(window.event ? window.event.keyCode : event.which) == 13')
        then: (canvas jQuery ajax callback: aBlock value: canvas jQuery this value))

It all works fine, except for the missing busy gif, due to the 'async: false'. Any suggestions?

Bob Nemec
  • 366
  • 1
  • 3
  • 10

3 Answers3

2

You can define a beforeSend and a complete handler to show and hide the loading indicator while the request is being processed. The global parameter set to false is meant to ignore your existing handlers to process request start and end (the mentioned spinner), and only use these defined in the particular instance of the JQAjax object.


    ((html jQuery ajax)
        async: false;
        global: false; "https://api.jquery.com/category/ajax/global-ajax-event-handlers/"
        callback: aBlock value: canvas jQuery this value;
        onBeforeSend: (html jQuery id: 'indicator') show;
        onSuccess: ((html jQuery id: 'fieldId') load html: [:h | ]);
        onComplete: (html jQuery id: 'indicator') hide;

That said, keep in mind that doing a synchronous AJAX call is discouraged since it will block the whole UI thread until the request is resolved.

So it's not completely clear how you manage the state in different worker images (gems, in this case) returning different things (probably because of having different sessions), so it's also not clear to me why doing an async XHR request will be served differently to doing it synchronously, I never experienced that.

From the small sample you mention, it can't be deduced what is the "refresh" part of your code. So maybe, providing more context will help us give you more accurate answers.

Esteban A. Maringolo
  • 1,208
  • 1
  • 12
  • 22
  • Thanks... I should have included a longer example. Using Esteban's code: without the (...async: false;...), #onSuccess: sometimes (about 1 in 5) is handled before ##callback:value: , so the user sees the pre-callback values. Refreshing the browser (F5) then shows the post-callback values. By its name, I originally assumed that #onSuccess: would not be triggered until after #callback: , but the 'async' part of ajax really does work :-) ... using #onBeforeSend: does show the busy gif, but only after a delay and just before the #onSuccess: render. – Bob Nemec May 10 '21 at 13:20
  • The `onSuccess:` depends on the HTTP response code, I don't see how the `success` event would trigger before getting a response from your server. What happens if you handle everying in the `complete` event instead of splitting into `success` and `complete`? – Esteban A. Maringolo May 10 '21 at 13:24
  • `onComplete:` and `onSuccess:` have the same end result. Adding `onBeforeSend:` with the busy gif is better, since the busy gif does get displayed, just not immediately. Our search results are quick enough that in most cases it is not an issue. As you say, it's the `success` event being triggered early that is curious. I can live with using `async: false` but I'd really like to understand this problem better. – Bob Nemec May 10 '21 at 13:38
  • Tracing activity in each gem in a four gem configuration, I see that I incorrectly assumed the `onSuccess:` script was being sent first. Turns out it is not being sent at all. The `callback:` runs, but the view is not refreshed. The problem is the no refresh, vs.early refresh (sometimes) – Bob Nemec May 10 '21 at 14:03
  • How is the refresh performed? Vía a `ajax load` (`JQLoad`) or with `replaceWith:`, are you using the proper element ids? Maybe you're using `html jQuery this` instead of `html jQuery id: 'elementId'`? What is the response? – Esteban A. Maringolo May 10 '21 at 14:18
  • Refresh is done with a `(html jQuery id: 'ourApplicationsMainDiv') load html: [:r | ...]` ... it works nicely when sent. I can trace that in GS with a write to stdout inside the html: render block. For example, when it works I see... `2021-05-10 11:08:51.880 'renderSearchColumn:on: '->'my search string'` on gem 1 and `2021-05-10 11:08:52.297 renderOurApplicationMainDivOn:` on gem 3 ...when it does not work, there is only the first search value trace and no 2nd trace. – Bob Nemec May 10 '21 at 15:12
  • I don't understand how the multiple gems sharing the same Seaside session works, I understand that the session is in the Stone, but still. ¿Did you try with a single gem to see if it works all the times or if also fails? – Esteban A. Maringolo May 10 '21 at 22:27
  • 1
    On a multi-gem GS configuration the Seaside session is a just another persistent object, which all gems can see. Each interaction is a committed transaction, which allows for the callback to be handled by one gem and the resulting redirect by another. Each gem sees the same session state. With one gem that interaction is serialized. The problem I'm seeing never happens with one gem, nor do I see form submit callback conflicts (another multi-gem symptom). I will submit a small example to GemTalk that can demonstrate this problem. – Bob Nemec May 11 '21 at 10:43
2

Fix ended up being trivial: needed to include 'event.preventDefault();' in the [Enter] key script. Seems obvious in hindsight.

if ((window.event ? window.event.keyCode : event.which) == 13) {
            event.preventDefault();
        };'
Bob Nemec
  • 366
  • 1
  • 3
  • 10
1

This problem is confirmed to be a narrow configuration symptom: GemStone with multiple gems. On every other configuration Seaside / javascript behaves as expected. I will follow this up as a vendor problem. Thanks to everyone that looked at it (this was also posted on other forums).

Bob Nemec
  • 366
  • 1
  • 3
  • 10
  • So it was related with my latest comment about having different gems serving the same session. – Esteban A. Maringolo May 11 '21 at 10:34
  • Yes. Multiple gems on the same database, including sharing the Seaside session, is how Seaside is used with GS. But there is some subtle point that I have found yet: I can't get a simplified example to break. Curious. – Bob Nemec May 11 '21 at 14:47