Weekly Noise 2 Recap - alert("It's working!");
Series: worklog November 07, 2010
The past week I have been working on a web-based “announcement board” using a slew of Javascript libraries.
So how did I do? Well I hit all my goals for the week, which were:
- Get a static page up that receives WebSocket updates ✓
- Make a simple Admin page to CRUD announcement items ✓
- Get basic animation going for the background ✓
The static page was simple enough to setup, especially since I have used socket.io
before. I added some basic styling to play around with some CSS3 stuff (border-radius
and box-shadow
are pretty slick). I send a payload and an action type over socket.io
to the page and then perform the appropriate action – adding an announcement, editing
the message or removing it all together – with jQuery
.
Next, I setup an “Edit” page where you can CRUD announcements. I used jQuery Ajax POSTs to trigger the actions in the database.
Since I am serving the pages with node.js
, I had to adjust to an asynchronous programming
model. I haven’t been able to wrap my head around this concept in the past – my previous
attempts at using monocle
and twisted
always ended with me frustrated and discouraged.
I spent some time trying to debug why I was not able to pull some documents out of MongoDB
and pass them to the template. It took me a while to realize that I need to put the
rendering function in the callback for the database query. Otherwise, my application was
happy to go on ahead and render the page with an empty array.
//Bad!
app.get('/edit', function(req, res){
var stored_items = [];
//pull some data out of mongo
Announcement.fetch_active().all(function(results) {
for (i in results) {
stored_items.push(results[i].body);
}
});
//pass the results to the template
res.render('edit.jade', {
locals: {
title: 'Edit Announcements',
items: stored_items
}
});
});
Putting the res.render()
in the callback ensures that the query has finished and the
page will receive the proper data. I imagine this code could still be improved with some
kind of asynchronous map
function instead of a for-loop, but it works well enough at the moment.
//Good! (well, better at least...)
app.get('/edit', function(req, res){
var stored_items = [];
//pull some data out of mongo
Announcement.fetch_active().all(function(results) {
for (i in results) {
stored_items.push(results[i].body);
}
//pass the results to the template
res.render('edit.jade', {
locals: {
title: 'Edit Announcements',
items: stored_items
}
});
});
});
I also ran into a problem with binding jQuery onClick
callbacks. Since all of the CRUD functionality
uses Ajax POSTs, I was constructing the new announcement divs on-the-fly. If you use the
normal $('#foo').click( ... )
jQuery code, only the DOM elements that are present when that
function is executed actually get bound.
As a result, any new elements that I add (like when I
created a new announcement) would not respond to events. Fortunately, this is easily fixed by using
.live()
, which binds the event handlers to all selector matches,
even those created dynamically.
The last goal was to get a simple animation going in the background using raphael.js
.
I want to be able to generate a bunch of polygons and then have them animate along a path.
Once the polygon goes offscreen, my plan is to loop it around back to where it started to
give the illusion of an endless stream.
I used inkscape
to make an SVG path (which is really just a string of coordinates and
commands) and then had a spinning square follow the path. Since the SVG element lives in
the DOM along with the other page elements, I needed to set the zIndex
so that the
animation would appear underneath the other text and images on the screen.
All together, it was a productive week. Despite hitting a few obstacles, I learned a few new tricks and made good progress; the source code for the app is on Github so feel free to take a look.