The Reactive Extensions for JavaScript – MooTools Integration
Sunday, March 7th, 2010 by Sebastian MarkbågeThis is a follow up to my earlier post about the Reactive Extensions (Rx) for JavaScript by Microsoft’s DevLabs. This is also in response to Matthew Podwysocki’s post on jQuery integration (which deserves some credit for putting it out there).
I will assume some familiarity with Rx.
Just like any other DOM library, MooTools has a way of working with native and custom passive DOM events. We can easily give Element object and the Elements collection a method to provide these events as “Observables”. In the jQuery example the method name “ToObservable” was added to the jQuery object, accepting an event type parameter, which was my initial reaction as well. But I’m going to call mine getEvent as in “getting a stream of events given the event type“.
var observableFromEvent = function(type){ var self = this; return Rx.Observable.Create(function(observer){ var fn = function(event){ observer.OnNext(event); }; self.addEvent(type, fn); return function(){ self.removeEvent(type, fn); }; }); }; Window.implement('getEvent', observableFromEvent); Document.implement('getEvent', observableFromEvent); Element.implement('getEvent', observableFromEvent); Elements.implement('getEvent', observableFromEvent); |
These are infinite Observables but we could also make .destroy() trigger onComplete to make them finite as well.
Flickables Example
Instead of the canonical Drag and Drop example I thought I show a twist. Let’s say we want to listen to a mouse flick. The mouse position have to move over 100px in 200ms. Then we want the angle of the flick.
var angleFromPosition = function(position, center){ var diffX = position.x - center.x, diffY = position.y - center.y; var distance = Math.sqrt(diffX * diffX + diffY * diffY); var angle = Math.atan2(diffY + distance, diffX) * 360 / Math.PI; return { distance: distance, angle: angle }; }; var distanceReached = function(angle){ return angle.distance > 100; }; var timeLimit = Rx.Observable.Timer(200); var mousePositions = document.getEvent('mousemove') .Select(function(event){ return event.page; }); var flicks = document.getElements('.flickable') .getEvent('mousedown') .SelectMany(function(event){ return mousePositions .Select(angleFromPosition.bindWithEvent(null, event.page)) .TakeUntil(document.getEvent('mouseup')) .TakeUntil(timeLimit) .Where(distanceReached) .Take(1); }); // ... flicks.Subscribe(function(current){ console.log('Flicked in direction: ' + current.angle + '°'); }); |
Events Mixin
MooTools has a very strong benefit compared to many other libraries. The publish/subscribe pattern is made explicit even for custom classes, using the Events mixin. By implement our “getEvent” method on this class we can use Rx on all custom MooTools classes that provide passive events.
Events.implement('getEvent', observableFromEvent); |
Side-effects
Rx allows for the act of subscribing to an event to trigger an action/side-effect. Think of the Request object for example. You can use the act of subscribing to it, to issue a HTTP request. Then we can turn the subsequent events like success and failure into the Observable interface. This means that Request is a complete Observable in it self. This is what I was saving the conversion name toObservable for.
Request.implement({ toObservable: function(){ var self = this; return Rx.Observable.create(function(observer){ var listeners = { success: function(result){ self.removeEvents(listeners); observer.OnNext(result); observer.OnCompleted(); }, cancel: function(){ self.removeEvents(listeners); observer.OnCompleted(); }, failure: function(xhr){ self.removeEvents(listeners); observer.OnError(xhr); } }; if (!self.running || self.options.link == 'cancel'){ self.addEvents(listeners).send(); return function(){ self.removeEvents(listeners).cancel(); }; } if (self.options.link == 'chain'){ var disposed, running; self.chain(function(){ running = true; if (!disposed) self.addEvents(listeners).send(); }); return function(){ if (running) self.removeEvents(listeners).cancel(); disposed = true; }; } observer.OnComplete(); return function(){}; }); } }); |
This creates a finite stream of events – only one response to be exact. However, since the act of subscribing to it causes it to occur we can have it trigger repeatedly as part of a composite stream of events.
MooTools’ Fx provides a similar concept but slightly different. Even though we don’t get an event for each tick, we still get an asynchronous complete event. This means we can insert Fx as part of a composite stream of events.
Fx also requires from/to arguments to be passed at the start. So we add the option “defaultArgs” to allow us to pass those at initialization.
Fx.implement({ toObservable: function(){ var self = this; return Rx.Observable.create(function(observer){ var listeners = { complete: function(){ self.removeEvents(listeners); observer.OnCompleted(); }, cancel: function(){ self.removeEvents(listeners); observer.OnCompleted(); } }; if (!self.running || self.options.link == 'cancel'){ self.addEvents(listeners).start.run(self.options.defaultArgs, self); return function(){ self.removeEvents(listeners).cancel(); }; } if (self.options.link == 'chain'){ var disposed, running; self.chain(function(){ running = true; if (!disposed) self.addEvents(listeners).start.run(self.options.defaultArgs, self); }); return function(){ if (running) self.removeEvents(listeners).cancel(); disposed = true; }; } observer.OnComplete(); return function(){}; }); } }); |
Of course since there are a lot of other classes extending the Request and Fx classes, you get the same benefits on them. This is one of the true benefits of MooTools’ modular extensibility.
That is one of the benefits of using the class(ical) pattern in JavaScript. More on that next time…
Side-effects Example
var popup = document.id('popup'); var showPopup = new Fx.Morph(popup, { property: 'opacity', defaultArgs: 1 }); var feed = new Request.JSON({ url: 'mydata.json', method: 'get' }); var showFeed = feed.toObservable() .Do(function(data){ popup.set('text', data); }) .Concat(showPopup.toObservable()); // ... showFeed.Subscribe(); // loads mydata.json into #popup and displays it |
Using Arrays in Unit Tests
Since natives are allowed to be extended within the MooTools theorem, we can add a convenience method to turn an Array into an observable stream of content.
Array.implement('toObservable', function(){return Rx.Observable.FromArray(this);}); |
We can use this to fake the “flicks” event stream in our earlier example. We avoid having to include complex asynchronous tests or user action tests.
var flicks = [ { angle: 0, distance: 100 }, { angle: 45, distance: 100 }, { angle: 90, distance: 100 } ] .toObservable(); // Unit tests // Synchronously testing code that's depending on a flick event stream |
Web Sockets and Web Workers
Now imagine this on a stream of events coming in from Web Sockets or Web Workers.
You could set up a web socket to asynchronously feed you JSON objects, and easily hook that up to the rest of you UI just as easily as the Request example above.
Tags: Events, JavaScript, MooTools, Reactive Extensions, Rx
April 1st, 2011 at 05:22
I personally like the MooTools’ Fx and agree it has significant advantages to the other libraries avail.
-Colin
April 6th, 2011 at 18:46
I recently took a Java class at a local college and I have to say, it was interesting but you definitely have to practice coding to get things to stick. With that being said, MooTools is a little ahead of my expertise. I’ll keep this post in mind though and come back to it in the future if needed.
Monroe,
site owner at Concrete Cost For Driveways
April 11th, 2011 at 16:39
This would be another i.t conversation..well..you really impressed me good job…even i find difficulty in understanding more of it..but all in all it is really amazing!! thanks for the info..you really informed me..so much!i learned!thanks!
April 11th, 2011 at 22:22
This is really great java info. I’m just learning basics myself, but I’m adding this to my bookmarks to check back later and will be sending it to my brother, who is a freelance programer. Thanks for the great info :)
May 23rd, 2011 at 04:46
vip.iplayer.com.tw/home/space.php?uid=34002&do=blog&id=38174
May 27th, 2011 at 15:21
bindwithevent is deprecated
May 29th, 2011 at 09:55
Thanks for another wonderful article. Where else could anybody get that kind of information in such a perfect way of writing’ I’ve a presentation next week, and I’m on the look for such info.
June 3rd, 2011 at 10:03
it is good to see you once again guy, I have not employed any equipment that you talked about above, it is great offering to us, I adore them so significantly , it appears to be making my work a lot a lot more less complicated and it can save my many time which I shell out on online marketing. I am heading to check into all of them . I genuinely respect to you for sharing the good job.
June 7th, 2011 at 20:54
Great write-up, I’m regular visitor of one’s web site, maintain up the nice operate, and It is going to be a regular visitor for a long time.
June 26th, 2011 at 11:13
Viagra is a medication compelling for treating male propagative disability. Viagra is a dirty plate manufactured in 25, 50, and 100 mg tablets in regard to treating erectile dysfunction. Having been here object of a lot longer, Viagra has a massive safeness rails record and proven effects that start acting in 30 minutes and matrix destined for in all directions 5 hours.
June 26th, 2011 at 11:19
Viagra is a medication effective pro treating manful sexual disability. Viagra is a glum plate manufactured in 25, 50, and 100 mg tablets in regard to treating erectile dysfunction. Having been nearly object of a destiny longer, Viagra has a great safety track record and proven effects that start acting in 30 minutes and matrix to there 5 hours.
January 31st, 2012 at 05:30
Stolen credit card Preteens Thongs Pictures
%)) Candid Preteen Gallery
38748 Shocking Preteen Models
8-]] Preteen Model Cocksuckers
=]] Banned Preteen Nude
cwv Yo 10 Preteen
oxozwd Preteen Hot Movies
>:-P Hotpreteenagers Newshark.Servik.Com Oksana
548 Crystal Model Preteen
027 Preteens Young List
>:-]]]