Добро пожаловать в восьмую часть руководства по созданию веб-приложения с помощью Node.js. В рамках серии уроков будет рассказано про основные особенности и трудности, которые возникают при работе с Node.js.
Предыдущие части:
Flash-сообщения — это серверные сообщения, которые отображаются только один раз. Для хранения таких сообщений обычно используется сессия. После того, как сообщение показано, оно удаляется. Express поддерживает flash-сообщения через middleware Connect’a:
req.flash('info', '%s items have been saved.', items.length);
Первым параметром является категория сообщения. Я обычно связываю его с
CSS классом, чтобы выводить сообщения об ошибках, отличающихся от
обычных сообщений. Вторым параметром является сообщение, которое будет
отображено. В нем можно использовать форматтеры (по умолчанию доступно
только %s
).
Примечание
На данный момент функциональность flash-сообщений переехала в Express и не является частью Connect.
Express предоставляет два вида helper’ов: статические и динамические. Сам helper может быть переменной или функцией и добавляется в приложение следующим образом:
app.helpers({
nameAndVersion: function(name, version) {
return name + ' v' + version;
},
appName: 'Nodepad',
version: '0.1'
});
Я предпочитаю создавать отдельный файл — helpers.js
. И использовать
его по мере необходимости:
app.helpers(require('./helpers.js').helpers);
В Jade использовать helper можно следующим образом:
#{nameAndVersion(appName, version)}
Я добавил это в заголовок Nodepad‘a.
Динамические helper’ы предоставляют доступ к объектам req
и res
,
что означает, что мы можем их использовать для доступа к нашим
flash-сообщениям. Далее я покажу, как использовать динамические
helper’ы для вывода flash-сообщений.
Обратите внимание, что динамические helper’ы отрисовываются перед отрисовкой шаблона, что приводит к тому, что они доступны в виде переменных, а не функций. Это может ввести в заблуждение.
Нам нужен helper для вывода всплывающих сообщений. Добавим следующий код
в 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;
}
};
Для каждого типа сообщения в цикле создается flash-сообщение с помощью
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-сообщений. Новый класс это понимает и выводит все сообщения через запятую.
Использование нового класса сильно упростило helper flashMessages
:
с помощью switch
получается необходимый CSS класс в зависимости
от типа сообщения. Далее создается HTML код, который уже прекрасно
взаимодействует с кодом jQuery UI:
Чтобы загрузить динамический helper в приложение (app.js
), необходимо
сделать следующее:
app.dynamicHelpers(require('./helpers.js').dynamicHelpers);
Далее, необходимо найти места в коде, где необходим вывод flash-сообщений. Например, так:
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', 'Неверные данные');
res.redirect('/sessions/new');
}
});
});
И, наконец, добавляем helper в шаблон views/layout.jade
:
#{flashMessages}
Единственная проблема в текущем коде состоит в том, что он не соответствует дизайну редактора:
Чтобы исправить ситуацию, необходимо добавить немного CSS (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;
}
В результате, при выводе flash-сообщения основная страница уйдет в тень.
Чтобы скрыть сообщение, я добавил в application.js
следующее:
function hideFlashMessages() {
$(this).fadeOut();
}
setTimeout(function() {
$('.flash').each(hideFlashMessages);
}, 5000);
$('.flash').click(hideFlashMessages);
Express предоставляет статические и динамические helper’ы. Динамические
отрисовываются перед отрисовкой основного шаблона, и в них доступны
объекты req
и res
. В шаблоне helper’ы доступны в виде переменных.
Как правило, создают отдельный файл под helper’ы, который при необходимости
загружают в приложение через require
.
Я сделал класс FlashMessage
, чтобы показать, почему полезно использовать
отдельный файл для helper’ов, а так же ненароком продемонстрировал работу
с геттерами и ООП в JavaScript. Быть может, вам покажется предпочтительнее
реализовать это передавая flash-сообщения в шаблоны через динамические
helper’ы. В результате, вывод flash-сообщений может быть реализован
с помощью Jade, а не на HTML, как в моем варианте. Попробуете реализовать?
Текущая версия Nodepad‘a «лежит» в коммите df0b954.