I saw some buzz today about Meteor, a new set of technologies for building JavaScript applications which blurs the lines between server code and client code. I’ve had a notion like this bouncing around in my head for the last year or so, and so I was keen to jump in and try it. I watched the screencast, installed the app, and installed the leaderboard example. The installation went flawlessly, and I was able to start the example app and bring it up in my browser without any trouble. I was off to a great start!
One of the interesting features of Meteor is that when you update a source file, the update is pushed down to clients immediately. I made some minor changes to the template and was pleased to see the changes getting pushed down as advertised. Very cool.
The leaderboard example page suggested some changes to get familiar with Meteor. The first was to add a button which would toggle sorting by score or by name. I felt confident that this should be a straightforward task, and started in with changes. First I added a button to the template. Meteor uses handlebars as its template engine (with more engine support promised), so I was able to dynamically change the button’s caption:
Saving this broke the front-end when it auto-reloaded because the template doesn’t have a sort_by_name yet. This was easily corrected:
Template.leaderboard.sort_by_name = function () { | |
return Session.get("sort_by_name"); | |
}; |
I then followed the pattern of the existing click handler for adding points to create my click handler:
Template.leaderboard.events = { | |
'click input.inc': function () { | |
Players.update(Session.get("selected_player"), {$inc: {score: 5}}); | |
}, | |
'click #sort': function () { | |
Session.set("sort_by_name", !Session.get("sort_by_name")); | |
} | |
}; |
Finally I updated the players function to return a list sorted in accordance with this new session setting:
Template.leaderboard.players = function () { | |
var sort = Session.get("sort_by_name") ? | |
{name: 1, score: -1} : | |
{score: -1, name: 1}; | |
return Players.find({}, {sort: sort}); | |
}; |
With the run of successes to this point, and the fact that this seemed like a trivial update I hit save, switched to my browser, was greeted with the new Sort button. I clicked it expecting a complete success.
Nothing happened.
Maybe the click event didn’t fire… I clicked it again – a dozen more times – strangely nothing worked. No matter how quickly or violently I clicked, nothing happened. I added a breakpoint to the event handler. Nothing. I added a console.log() call in case the running JavaScript was in a different file from the one I was looking at, but still nothing happened. I went on like this for longer than I care to admit trying to get that button to do something without any joy.
So I took a desperate leap of desperation and consulted the documentation. There it was in glorious bold red lettering – a documented bug in event handling: “If an event is filtered by a selector, then it will never be delivered to a top-level node in a template.”
My button was a top level node in its template. I wrapped that sucker in a <div> tag, hit save, switched to my browser window, clicked it and was rewarded with a sorted list! Ecstasy.
As I attempt the other challenges suggested for the leaderboard example, I won’t be so slow to consult the excellent documentation. As frustrating as this was, the fact that the bug was so clearly documented, and the fact that everything else went so smoothly makes me confident that the Meteor project will be very successful. I’ll let you know how it goes.
Very intriguing. I’m almost accomplishing the same thing with Dojo and ZF with a custom REST controller and custom Dojo REST form and a bunch of conventions but this is definitely worth checking out. I wonder if there’s a nice Dojo Meteor bundle, now that would be very cool.
Thanks for this blog. I also run on the same problem but I have a different implementation than your solution. Rather than storing the “sort” variable into Session, I store the whole Players.find() result set into Session, but this is not working. Do you know why? Is is because Session.set() method can only store JSON object and strings?
Thanks Maiah. I don’t know Meteor well enough yet to say for sure – I’m using mostly knockout for my real work. I would guess though that you need to update the model that the UI is monitoring to see the updates (Template.leaderboard.players), and that updating the values in the session won’t do this.