Node Tutorial Part 8

03 Jan 2011 | By Alex Young | Tags server node tutorials lmawa nodepad npm express

Welcome to part 8 of Let’s Make a Web App, a tutorial series about building a web app with Node. This series will walk you through the major areas you’ll need to face when building your own applications. These tutorials are tagged with lmawa.

Previous tutorials:

Flash Messages

Flash messages are server-side messages that are displayed once. The session is usually used to store flash messages until they’re displayed, at which point they’re deleted. Express has support for flash messages through Connect’s flash middleware:

req.flash('info', '%s items have been saved.', items.length);

The first parameter is a category for the message. I usually associate these with CSS classes to display error messages differently to general feedback. The second parameter is the message to be displayed, which can use formatters (only %s is available by default).

Helpers

Express has two kinds of view helpers: static and dynamic. Helpers can be variables or functions, and are added to the application like this:

app.helpers({
  nameAndVersion: function(name, version) {
    return name + ' v' + version;
  },

  appName: 'Nodepad',
  version: '0.1'
});

I like to make a file called helpers.js with all of my helpers in, then load it with require:

app.helpers(require('./helpers.js').helpers);

Now update the Jade templates to use the helper:

#{nameAndVersion(appName, version)}

I’ve added this to the header in Nodepad.

Dynamic helpers provide access to the req and res objects, which will mean we can use them to get at our flash messages. Next I’ll demonstrate how to use dynamic helpers to display flash messages.

Note: The interesting thing about dynamic helpers is they’re rendered before the view, which means they appear as variables rather than functions. This may catch you out.

Adding Flash Message to Nodepad

We’ll need a helper to display flash messages. Add this to helpers.js:

exports.dynamicHelpers = {
  flashMessages: function(req, res) {
    var html = '';
    ['error', 'info'].forEach(function(type) {
      var messages = req.flash(type);
      if (messages.length > 0) {
        html += new FlashMessage(type, messages).toHTML();
      }
    });
    return html;
  }
};

This loops through each message type and generates a flash message using FlashMessage. This is a new class that I’ve made to make it easier to reuse the jQuery UI styles:

function FlashMessage(type, messages) {
  this.type = type;
  this.messages = typeof messages === 'string' ? [messages] : messages;
}

FlashMessage.prototype = {
  get icon() {
    switch (this.type) {
      case 'info':
        return 'ui-icon-info';
      case 'error':
        return 'ui-icon-alert';
    }
  },

  get stateClass() {
    switch (this.type) {
      case 'info':
        return 'ui-state-highlight';
      case 'error':
        return 'ui-state-error';
    }
  },

  toHTML: function() {
    return '<div class="ui-widget flash">' +
           '<div class="' + this.stateClass + ' ui-corner-all">' +
           '<p><span class="ui-icon ' + this.icon + '"></span>' + this.messages.join(', ') + '</p>' +
           '</div>' +
           '</div>';
  }
};

The flash middleware returns multiple messages per type, so the code above handles this by joining each message with a comma.

This simplifies the flashMessages helper by using switch statements to generate appropriate CSS classes based on the message type. It then generates some HTML that will work well with jQuery UI code:

Now load the dynamic helpers in app.js:

app.dynamicHelpers(require('./helpers.js').dynamicHelpers);

And find some places to add flash messages to:

app.post('/sessions', function(req, res) {
  User.find({ email: req.body.user.email }).first(function(user) {
    if (user && user.authenticate(req.body.user.password)) {
      req.session.user_id = user.id;
      res.redirect('/documents');
    } else {
      req.flash('error', 'Incorrect credentials');
      res.redirect('/sessions/new');
    }
  }); 
});

Now add the helper to the layout, views/layout.jade:

#{flashMessages}

Feedback Display Issues

The only problem with the current code is it doesn’t sit well with the editor design:

The way around this is to use some styling. In styles.less:

.flash {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 1001;
  width: 100%;
  opacity: 0.75;
  background-color: #111;
}

.flash span {
  float: left;
  margin-right: .7em;
}

.flash .ui-corner-all {
  width: 300px;
  margin: 50px auto 0 auto;
  padding: 0 5px;
  opacity: 0.9;
  font-weight: bold;
  -moz-box-shadow: 0 0 8px #111;
  -webkit-box-shadow: 0 0 8px #111;
  box-shadow: 0 0 8px #111;
}

This will take over the entire page and fade it to black while the message is visible. To hide it, I’ve added this to application.js:

function hideFlashMessages() {
  $(this).fadeOut();
}

setTimeout(function() {
  $('.flash').each(hideFlashMessages);
}, 5000);
$('.flash').click(hideFlashMessages);

Conclusion

Express has static and dynamic helpers. Dynamic helpers are rendered just before the view, and they are passed the req and res objects. They’re accessed as variables in the views.

It’s easy to create a separate file that stores all of an application’s helpers by using require.

I built the FlashMessage class to demonstrate why using a file for helpers is useful, and also to covertly show some simple JavaScript OO with getters. You may prefer to implement this by exposing flash messages to templates through dynamic helpers, which would mean the flash HTML can be written with Jade. Why not try it out as a challenge?

The current version of Nodepad is commit df0b954.


blog comments powered by Disqus