Search code examples
javascriptfrprxjsbacon.js

RxJS equivalent of `Bacon.when()` with properties (which are sampled but not part of the synchronization pattern)


Consider the following Bacon.js code sample (loosely based on the code here, uses bacon.model & bacon.jquery):

<input id="total" type="text" placeholder="total">
/
<input id="quantity" type="text" placeholder="quantity" value="1">
=
<span id="price"></span>
<script>

  var $total = Bacon.$.textFieldValue($("#total")).changes();

  var quantity = Bacon.$.textFieldValue($("#quantity"));

  var price = Bacon.when(
    [$total, quantity], function(x,y) { return x/y; }
  ).toProperty(0);

  price.onValue(function (p) {
    $('#price').text(p);
  });

</script>

What happens here is that the stream $total and the property quantity are joined into a property price. Both $total and quantity get their input from a text input field, but only $total prompts price to be updated. (I.e. quantity is sampled but not part of the synchronization pattern.)

I am trying to achieve this same behavior using RxJS instead of Bacon.js, but all I can come up with is super ugly solutions. I reckon I must be overlooking something…?

My best shot so far (behavior not identical, but you get the idea):

var totalChange = Rx.Observable.fromEvent($('#total'), 'input');

var totalValue = totalChange.map(function () {
  return parseInt($('#total').val(), 10);
});
var quantityValue = totalChange.map(function () {
  return parseInt($('#quantity').val(), 10);
});

var price = Rx.Observable.zip(
  totalValue,
  quantityValue,
  function (x,y) {
    return x/y;
  }
);

price.subscribe(function (p) {
  $('#price').text(p);
});

Because neither combineLatest or zip offer the desired behavior on observables that represent the two text fields directly, all I can think of is to create an observable for quantity based on input events in the text field for total. Which works, but it feels pretty far-fetched.

Any suggestions for alternative approaches?


Solution

  • Use the recently added withLatestFrom instead of combineLatest.

    var price = totalValue.withLatestFrom(quantityValue, (x,y) => x/y);
    

    Compare their diagrams: