Search code examples
javascriptjqueryfrpbacon.js

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.


Solution

  • I would make a stream and map it to a -1 when you make the request and a +1 when you get a response. Then, you can run a fold over the stream and just enable the button when it equals 0.