欢迎来到让我们建一个Web应用的第四部分,关于使用Node创建一个web应用的新的学习指南。这个系列会引领你使用Node创建一个web应用,涵盖了在搭建你自己应用程序时需要面临的所有主要技术领域。

  • 第一部分:介绍这个系列以及讨论如何为你的Node项目选择合适的库。
  • 第二部分:安装和骨架应用,源代码提交:
  • 第三部分:RESTful方法和测试,源代码提交:
  • 第四部分:模板、模板引用以及创建和编辑Document,源代码提交:
  • 第五部分:授权、会话和中间件控制权限,代码提交:
  • 第六部分:界面基础,代码提交:
  • 第七部分:Node库的版本、Jade技巧和错误页面,代码提交:

闪存消息

闪存消息是立刻需要显示的服务端消息。在显示之前通常使用会话用来存储闪存消息,同时也会在会话中删除。Express使用Connect闪存中间件支持闪存会话:

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

第一个参数是类别,我通常用显示错误消息的CSS类与其相关联,用以区分需要不同反馈的消息。第二个参数是要显示的消息,其中可以包含一个格式器(默认只有%s可用)。

Helpers

Express有两种视图帮助器:静态的和动态的。帮助器可以作为函数的变量,可以如下方式加入到应用中:

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

我喜欢使用一个名为helpers.js的文件将我所有的帮助器都放在里面,并使用require来来加载我需要的帮助器:

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

现在更新我们的Jade模板使用帮助器:

#{nameAndVersion(appName, version)}

我把这段代码添加到Nodepad的头部。

动态帮助器可以进入req和res对象,这样我们就可以使用它来得到闪存消息。下面我将演示如何使用帮助器来显示闪存消息。

注意:动态帮助器有趣的是它会在视图之前渲染,这意味着它们将作为变量而不是函数出现。

为Nodepad添加闪存消息

我们需要一个帮助器来显示闪存消息。添加一个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;
  }
};

这个循环将遍历所有的消息类型并使用FlashMessage产生一个闪存消息。这是一个新的类以方便我重用jQuery UI的样式:

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>';
  }
};

中间件flash返回多种基于type的消息,所以上面代码使用分隔符连接每个消息来处理这种情况。

这是一个使用switch来处理flashMessages帮助器的逻辑,根据不同的消息类型使用不同的CSS类。它会产生一些HTML代码来与jQuery一起工作:

现在在app.js中装载动态的帮助器:

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

并且在一些地方添加闪存消息:

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');
    }
  }); 
});

现在在views/layout.jade中添加帮助器:

#{flashMessages}

反馈显示问题

现在的问题是目前的代码与编辑器的设计融合的不够好:

解决这个问题是在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;
}

这样会占据整个页面并在消息出现的时候逐渐变黑。为了隐藏他们我在application.js中添加了如下内容:

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

结论

Express有静态和动态的帮助器,动态的帮助器就在视图之前渲染,并且他们会传递req和res对象,在视图中可以作为变量访问。

创建一个隔离的文件来存放应用中所有的帮助器是非常容易的,也可以方便的在应用使用require来引用。

我创建了一个FlashMessages类来演示为什么使用一个帮助器文件是很必要的,并且也演示了一些简单的JavaScript OO的方法来实现getter方法。你可能习惯将闪存消息通过动态帮助器来直接开放给模板,那就意味着将要使用Jade来产生闪存消息的HTML,这可以作为一个对于我们自己的一个挑战来完成。

当前版本的Nodepad代码提交: