Um dos projetos pessoais que mais tenho me dedicado nos últimos tempos Independent, o CMS que criei e que uso como site pessoal. Tento escrever meus textos diretamente nele e por isso tenho gasto um tempo tentando deixar essa tarefa o mais agradável possível. Com essa linha de pensamento, o que mais sentia faltar era de um bom mecanismo de preview. Eu tinha feito um sistema de preview bem simples, onde o texto era renderizado utilizando o layout do site atual e não o do admin, porém era necessário que o artigo já estivesse persistido no banco. Mas pensando em um mecanismo melhor para poder ver a edição em tempo real, pensei em fazer algo utilizando WebSocket.
Como esse serviço é algo que pode ser separado do código do CMS resolvi utilizar uma tecnologia que nunca tinha feito nada de útil, mas que sempre escuto falar, o NodeJS.
Meu objetivo aqui não é fazer nenhum tipo de introdução à nenhuma tecnologia utilizada neste projeto. O objetivo é somente demonstrar algo que fiz com essas tecnologias.
Criei duas “classes” em Javascript para escutarem o servidor. Uma delas fica no admin e monitora as mudanças realizadas nos campos do artigo e a outra fica na página de preview e é responsável por receber as informações do servidor e colocar o resultado nos lugares corretos.
Para intermediar a conversa entre duas classes há o servidor que cria as sessões. Por enquanto ele está bem simples, mas com o tempo eu vou melhorando sua implementação.
Abaixo eu listo os códigos dos clientes. Tentei fazer algo bem simples. Como primeiro teste está funcionando muito bem.
var PreviewForm = function() {
}
PreviewForm.prototype.connect = function(session) {
var _this = this;
this.session = session;
this.connection = new WebSocket($('meta[name="preview_host"]').attr('content'));
this.connection.onopen = function() {
var message = JSON.stringify({'event': 'start', 'session': session});
_this.connection.send(message);
}
}
PreviewForm.prototype.send = function(content) {
var _this = this;
var object = {'event': 'update', 'session': _this.session};
object.content = content;
_this.connection.send(JSON.stringify(object));
}
var PreviewView = function() {
}
PreviewView.prototype.connect = function(session) {
var _this = this;
this.session = session;
this.connection = new WebSocket($('meta[name="preview_host"]').attr('content'));
this.connection.onopen = function() {
var message = JSON.stringify({'event': 'view', 'session': session});
_this.connection.send(message);
}
this.connection.onmessage = function(message) {
var content = JSON.parse(message.data);
//render
$.each(content, function(attr, value) {
$("[data-preview-attribute='" + attr + "']").html(value);
});
}
}
Utilizei para construir esse pequeno servidor o pacote ws, que pode ser
instalado através do comando npm install ws
.
var WebSocketServer = require('ws').Server;
var server = new WebSocketServer({ port: 8080 });
var sessions = {};
var nextId = 0;
server.on('connection', function connection(client) {
client.on('message', function incoming(message) {
console.log(message);
var message = JSON.parse(message);
if(message.event === 'start') newSession(message);
if(message.event === 'view') newClient(client, message);
if(message.event === 'update') updateClients(message);
});
});
function newSession(message) {
if(!sessions[message.session]) sessions[message.session] = {'clients': []};
}
function newClient(client, message) {
if(!sessions[message.session]) sessions[message.session] = {'clients': []};
sessions[message.session].clients.push(client);
}
function updateClients(message) {
for(var i = 0; i < sessions[message.session].clients.length; i++) {
var client = sessions[message.session].clients[i];
client.send(JSON.stringify(message.content), function(){/*not send*/});
}
}