Skip to content

Instantly share code, notes, and snippets.

@Rich-Harris
Created May 28, 2014 18:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rich-Harris/c687e94fe2c70650c7cc to your computer and use it in GitHub Desktop.
Save Rich-Harris/c687e94fe2c70650c7cc to your computer and use it in GitHub Desktop.
Rx adaptor

Following on from this conversation...

Ractive.js is a library for building reactive user interfaces. Unlike systems that are based on library-specific event-emitting model classes (e.g. Backbone.Model, ko.observable et al) or observable values, Ractive is based on POJOs and keypaths:

ractive.set('user', {
  name: 'Alice',
  age: 42
});

// it's Alice's birthday
ractive.set('user.age', 43);
<p>{{user.name}} is {{user.age}} years old</p>

But for apps that rely on model objects or observable values, we need a way to translate change events into the equivalent ractive.set() operation. For that, we use adaptors. For example if we were using the Backbone adaptor, we could do this:

var user = new User({ name: 'Alice', age: 42 }); // User extends Backbone.Model
ractive.set('user', user);

user.set('age', 43); // adaptor 'translates' this to `ractive.set('user.age', 43)`

Where Rx comes in

I'm trying to create adaptors for Bacon.js and RxJS primitives:

I'm a total novice at this, but my understanding is that both libraries have an Observable class, and you can listen for changes with observable.subscribe(handler), and that's basically all you need to know to implement the adaptor.

In Bacon's case the handler is called immediately and on every subsequent change,.

RxJS on the other hand distinguishes between hot and cold observables, and in a case like the following (the demo code for the RxJS adaptor), subscribers aren't called initially:

up = Rx.Observable.fromEvent( ractive.find( '.up' ), 'click' );
down = Rx.Observable.fromEvent( ractive.find( '.down' ), 'click' );

counter = up.map( function () { return 1; })
  .merge( down.map( function () { return -1; }) )
  .scan( 0, function ( x, y ) {
    return x + y;
  });

So my first question is whether there's a way to coax the starting value out of an observable like counter, or whether that's entirely up to the creator of the observable? @ReactiveX suggested adding .startWith(0), which works...

up = Rx.Observable.fromEvent( ractive.find( '.up' ), 'click' );
down = Rx.Observable.fromEvent( ractive.find( '.down' ), 'click' );

counter = up.map( function () { return 1; })
  .merge( down.map( function () { return -1; }) )
  .scan( 0, function ( x, y ) {
    return x + y;
  }).startWith( 0 );

...but I was left wondering if I was doing something wrong, since in Bacon.js, the observable is already initialised with the seed value passed to .scan().

The second question is whether it's possible to somehow manually update the value of an observable. Taking our Backbone example again, we could do...

ractive.set('user.age', 44); // adaptor translates this to `user.set('age', 44)`

...but I can't wrap my head around how you'd do something similar with an Rx.Observable like counter.

Really appreciate any help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment