Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Bacon.js to disable submit button while deferreds are "pending"

I have a form, which has many image urls - the back-end persists url strings and the images are uploaded directly to S3. I'd like to use Bacon.js streams to handle disabling/enabling the form's submit button while uploads are in-progress.

I've tried various approaches (using a stream of streams of Bacon.fromPromises, using a stream of fromPromise-d deferreds and a bus of raw deferreds and trying to manually diff the two) but haven't found a solution that a) works as intended and b) feels like I'm not fighting the library.

This is where things stand, but as noted, the submit button is prematurely re-enabled.

function toResultStream(promise) {
  return Bacon.fromPromise(promise)
}

var deferreds = $('a').asEventStream('click', function (event) {
    event.preventDefault();
    var deferred = $.Deferred();

    // simulate upload
    setTimeout(function () {
        deferred.resolve(true);
    }, _.random(200, 1600))

    setTimeout(function () {
        deferred.rejectWith(false);
    }, _.random(200, 1600))

    return deferred;        
});

deferreds.onValue(function () {
    $('#submit').attr('disabled', true);
})

// only takes completed deferreds into consideration
var ongoingSearch = deferreds.flatMap(function (d) {
    return toResultStream(d);
})
.mapError(true)
.onValue(function () {
    $('#submit').attr('disabled', false);
});

Fiddle

*Update

@mjs2600's answer was enough to nudge me towards a solution.

Here's what I ended up doing:

var bus = new Bacon.Bus();

var deferreds = $('a').asEventStream('click', function (event) {
    // ...
    bus.push(-1);        
    // ...
});

var ongoingSearch = deferreds
    .flatMap(toResultStream)
    .mapError(1)
    .merge(bus)
    .scan(0, function (memo, n) { return memo + n; })
    .onValue(function (value) {
        var disabled = value < 0;
        $('#submit').attr('disabled', disabled);
    });

Updated Fiddle

I know using Buses is frowned upon, so if anyone has a suggestion as to how I could achieve the same behavior with streams, I'd very much like to see it.

like image 444
pdoherty926 Avatar asked Jul 13 '15 21:07

pdoherty926


1 Answers

If you know the number of urls to upload, you could add skip like this to ignore the responses from all but the last url.

var numStreams = 1;
var ongoingSearch = deferreds.flatMap(function (d) {
    return toResultStream(d);
}) 
.mapError(true)
.skip(numSteams-1)
.onValue(function () {
    alert('fooo');
    $('#submit').attr('disabled', false);
});

One thing I find a little worrisome about the code you posted is that these streams never terminate. You might consider refactoring such that the ongoingSearch is created by the click and terminates once all the streams are complete. (For example, merge the upload streams and re-enable the button in the onEnd of that merge.)

like image 93
Ed Ballot Avatar answered Oct 17 '22 08:10

Ed Ballot