bigskysoftware / intercooler-js

Making AJAX as easy as anchor tags
http://intercoolerjs.org
MIT License
4.78k stars 235 forks source link

Intercooler.ready calls stack handlers but don't allow them to be cleared #294

Open esb opened 5 years ago

esb commented 5 years ago

In a previous issue, I'd raised the problem of running code after the Intercooler swap had completed. It was pointed out that I could use the Intercooler.ready function in the IC-Script header to kick off my cleanup code when everything had finished.

This is great, but now I've run into a bit of a problem because each call to Intercooler.ready pushes the ready function onto a global stack. In my particular situation, I'm using IC to respond to a user request which may or may not work. If it fails, I set an error response using IC-Script and Intercooler.ready, and if it succeeds, then I set a different response to indicate that it worked.

Now, if the user makes several attempts to use this function, the previous ready handlers are still active on subsequent attempts. So, if a failed attempt was made, then on a subsequent successful attempt, you get back both the failed attempt handler AND the successful attempt handler.

What would make this workable would be if there were a single shot handler capability, like the jQuery one function. I don't need the handler to be permanent, but there's no way to turn it off once installed.

esb commented 5 years ago

A pretty trivial solution to this scenario is to add a new Intercooler.one function (or equivalent).

This simply replicates the current ready function, but creates a separate array of handlers which are automatically cleared once they have been called in the fireReadyStuff routine.

baumann74 commented 5 years ago

Did you try to have a single Intercooler.ready somewhere else on the page? Then you can put some logic inside and only perform your cleanup code when certain conditions are met.

Anders

On Thu, Oct 3, 2019, 07:48 esb notifications@github.com wrote:

A pretty trivial solution to this scenario is to add a new Intercooler.one function (or equivalent).

This simply replicates the current ready function, but creates a separate array of handlers which are automatically cleared once they have been called in the fireReadyStuff routine.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/intercoolerjs/intercooler-js/issues/294?email_source=notifications&email_token=AB2AHGHBNSGWECS5V5I2IGDQMV2TPA5CNFSM4I46BWU2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAG7ELI#issuecomment-537784877, or mute the thread https://github.com/notifications/unsubscribe-auth/AB2AHGA7DLKPUTQHVWNMRGDQMV2TPANCNFSM4I46BWUQ .

scsmash3r commented 5 years ago

A pretty trivial solution to this scenario is to add a new Intercooler.one function (or equivalent).

This simply replicates the current ready function, but creates a separate array of handlers which are automatically cleared once they have been called in the fireReadyStuff routine.

Since Intercooler depends on jQuery, you can use jQuery.one([...]) inside Intercooler.ready() function. Is it suitable solution?

esb commented 5 years ago

Not really. Every call to Intercooler.ready pushes the function onto the stack of ready handlers. Pushing multiple calls to jQuery.one doesn't really get around the problem.

scsmash3r commented 5 years ago

Not really. Every call to Intercooler.ready pushes the function onto the stack of ready handlers. Pushing multiple calls to jQuery.one doesn't really get around the problem.

Hmm... Can you link a piece of logic of your code to be sure? Maybe you want to use X-IC-Trigger instead? You could send back headers like this, i.e. (PHP example, some response, that came from backend):

// In case of failure
return $response->withHeader('X-IC-Trigger', '{"callableTrigger":["fail"]}');

// In case of success (or do some other stuff/sending another header)
return $response->withHeader('X-IC-Trigger', '{"callableTrigger":["success"]}');

And simply trigger jQuery.one() like so (JS part):

jQuery("body").one("callableTrigger", function(data) {
    if (data == 'fail') {
        Intercooler.ready(function(data) {
            //doSomething
        });
    }
});
esb commented 5 years ago

I use IC-Script to set a ready handler with Intercooler.ready to kick off some post swap cleanup actions. The problem is that the ready handler, once invoked, never goes away and gets executed even when different ready handlers are installed. This is the problem with having multiple Intercooler actions on a single page.

Using a jQuery.one handler inside the ready handler does not help, since the handler gets executed on every new IC request.

IC-Trigger is of no help. It gets triggered at the beginning of processing, so there's no callback when the transition processing has completed. Unless you use Intercooler.ready, and you're back to the same problem.

I coded up my idea of a single-shot ready handler and it solves the problem neatly, without the need to have complex logic in some sort of global handler which tries to work out which handler has been executed or not.

1cg commented 5 years ago

@esb do you have a patch? I'd be fine adding an Intercooler.once() function to like up with the JQuery API.

esb commented 5 years ago

Great! I'll code up a pull request over the weekend.

1cg commented 4 years ago

@esb ever get to making a patch?

esb commented 4 years ago

@chg20 Sorry, work has got really busy and I haven't been able to find the time to write test cases for the patch. The patch is really simple, and it's been working in production for many months now.

This is the diff

27d26
<   var _onceHandlers = [];
763,770d761
<     $.each(_onceHandlers, function(i, handler) {
<       try {
<         handler(elt);
<       } catch (e) {
<         log(elt, formatError(e), "ERROR");
<       }
<     });
<     _onceHandlers = [];
2028,2030d2018
<     once: function(onceHandler) {
<       _onceHandlers.push(onceHandler);
<     },