Saltar al contenido

Construyendo un editor colaborativo en tiempo real con RethinkDB

A continuación, vamos a crear las bases de datos de RethinkDB. Coloca el siguiente código sobre tu sección de Socket.

<pre>1234567891011121314151617181920r.connect({ host:"localhost", port:28015},function(err, conn){if(err)throw err; r . db("test").tableList().run(conn,function(err, response){if(response.indexOf("edit")>-1){// no hacer nada de lo que se crea.. .console.log("Tabla existe, saltando crear…");console.log("Tablas – "+ respuesta);}else{// crear tabla…consola. log("Tabla no existe. Crear"); r .db("test").tableCrear("editar").run(conn);}});});</pre>
javascript

Construyendo un editor colaborativo en tiempo real con RethinkDB
Construyendo un editor colaborativo en tiempo real con RethinkDB

Cuando instalas RethinkDB, automáticamente crea una base de datos de pruebas. Vamos a usar eso para nuestro proyecto. (siéntete libre de intentar modificar tu código para añadir una nueva base de datos para que la utilices, si quieres).

La primera parte de este bloque de código nos conecta a RethinkDB. Entonces cualquier cosa que queramos hacer con la base de datos tiene que pasar dentro de ese bloque de conexión. A continuación llamamos a un .tableList() en la base de datos. Queremos ver si nuestra tabla de edición ya está creada; si lo está, entonces sólo vamos a listar las tablas actuales en la base de datos. Si la tabla no existe, entonces vamos a crearla. Es una forma sencilla de crear la base de datos en la primera ejecución, y saltarse ese código en cada ejecución posterior. De lo contrario, obtendríamos un error de que la tabla ya existe cada vez que intentamos crearla.

La mayoría de las cosas que vamos a hacer con los datos van a requerir RethinkDB, así que parchea las secciones RethinkDB y Socket.IO para que se vean así:

<pre>12345678910111213141516171819202122232425262728r.connect({ host:"localhost", port:28015},function(err, conn){if(err)throw err; r .db("test").tableList(). run(conn,function(err, response){if(response.indexOf("edit")>-1){// no hacer nada se crea…console.log("Table existe, saltando crear…");console. log("Tablas – "+ respuesta);}else{// crear tabla…console.log("Tabla no existe. Crear"); r .db("test").tableCrear("editar").run(conn);}});// Cosas del zócalo io. on("conexión",función(socket){consola.log("un usuario conectado"); socket.on("desconectar",función(){consola.log("usuario desconectado");});});</pre>
javascript

Esto significa que cualquier cosa que hagamos en el bloque de zócalo tendrá acceso a la conexión de los datos de RethinkDB.

Pasando al archivo index.html, vamos a añadir una nueva sección de código:

<pre>12345678myCodeMirror.on("keyup",function(){var msg ={ id: room, user: user, value: myCodeMirror.getValue()}; socket.emit("document-update", msg);});</pre>
javascript

En el archivo index.js, agregue el siguiente código a su bloque de zócalos.

<pre>12345678910111213socket.on("document-update",function(msg){console.log(msg); r .table("edit").insert({ id: msg.id, valor: msg. valor, usuario: msg.user},{ conflict:"update"}).run(conn,function(err, res){if(err)throw err;//console.log(JSON.stringify(res, null, 2));});</pre>
javascript

El primer conjunto de código hace que cada vez que se pulsa una tecla en el editor de CodeMirror, hagamos algo. En este caso, vamos a enviar un Socket edit etiquetado como document update con el valor del contenido del editor'tico.

En el segundo conjunto de código, vamos a recibir ese mensaje de socorro y lo guardaremos en la tabla RethinkDB. Noten que es una inserción y que tengo un pedazo de código que se parece a {conflicto: "update"}. Esto me permite insertar los datos cuando llegan. Si los datos son nuevos, entonces el código insertará un nuevo registro en RethinkDB. Sin embargo, si los datos entrantes coinciden con una identificación de habitación preexistente en la base de datos de RethinkDB, entonces actualizaremos ese registro en lugar de insertar uno nuevo.

Pero si ejecutaras esto y escribieras el editor de CodeMirror, entonces no verías nada en absoluto. Vamos a arreglar eso añadiendo el siguiente código:

Añade lo siguiente a index.js debajo del elemento de enchufe que acabamos de hacer dentro del bloque de enchufe:

12345678910r .table("edit").changes().run(conn,function(err, cursor){if(err)throw err; cursor.each(function(err, row){if(err)throw err; io.emit("doc", row);});});

javascript

Añade lo siguiente a index.html en tu sección de javascript:

1234567socket.on("doc",function(msg){if(msg.new_val.id=== room && msg.new_val.user!= user){var current_pos = myCodeMirror.getCursor(); myCodeMirror.getDoc().setValue(msg.new_val.value); myCodeMirror.setCursor(current_pos);}};

javascript

El primer bloque de código hace la magia que es RethinkDB. Estamos usando su función de cambios para ver una tabla de datos. Cada vez que una fila de datos cambie, la base de datos nos informará. Cuando nos informe del cambio, enviaremos ese documento a través del Socket a todos los clientes conectados. Esto ocurrirá en tiempo real a medida que los nuevos datos sean empujados al servidor.

El segundo bloque de código recibe los datos en el socket doc y comprueba que está destinado a este cliente. Si los datos son para la sala actual y no fueron enviados por el usuario actual, entonces los queremos.

La razón por la que no queremos datos enviados por el usuario actual es porque puede causar un error de escritura. A medida que los datos se envían al servidor, se envían de nuevo a nosotros. Y cuando los recuperamos, los almacenamos en el editor de código en espejo. Si fuimos nosotros los que lo enviamos, puede que ya hayamos escrito una nueva carta antes de recibir la actualización. Esto causaría que nuestra carta escrita desapareciera. Comprobando que los datos no fueron enviados por el usuario actual, entonces podemos evitar este problema por completo.

A continuación añadiremos algunos detalles más. Añade el siguiente código a index.js Necesita ser añadido dentro del bloque de conexión RethinkDB:

12345678910app.get("/getData/:id",function(req, res, next){ r .table("edit").get(req.params.id).run(conn,function(err, result){if(err)throw err; res.send(result);//return next(result);});});

javascript

Añade lo siguiente a la sección de javascript de tu index.html:

1234567$.ajax({ url:"/getData/"+ room,success:function(result, status, xhr){ myCodeMirror.setValue(result.value);console.log(result);}});

javascript

El primer bloque de código aquí establece una API para que nuestro cliente cargue los datos de la sala actual. Pasa un parámetro de identificación a través de la URL y luego lo utiliza para consultar RethinkDB y devolver los datos.

El segundo bloque de código establece la llamada de carga desde el lado del cliente. Almacena el resultado de la llamada en el editor de CodeMirror. Ahora cuando nos unimos a una sala, si ya hay datos en el editor, los recuperamos y los mostramos. De lo contrario, estará vacía y podremos empezar la nueva sala escribiendo en el editor.

Ahora vamos a arreglar la página un poco y añadir un poco de CSS.

Añade el siguiente CSS a tu página index.html:

12345678910111213141516171819202122html,body{height:100%;width:100%;background-color:#333;font-family: arial;}#editor-wrapper{width:70%;margin-left: auto;margin-right: auto;margin-top:3em;}#nombre de usuario{color:#f3f3f3;} cuser{color:#999;}.ouser{color:#fff;}

css

Reemplazar en index.html con el siguiente código:

1234<div;²;span;²;Usuario actual:
html

Añade el siguiente javascript a la sección de scripts de index.html:

1documento.getElementById("nombre de usuario").innerHTML= usuario;

javascript

Esto arreglará un poco la página y añadirá el nombre de usuario que especificaste a la página. A estas alturas ya deberías tener una aplicación en funcionamiento, y debería tener un aspecto parecido a este.

Y aquí hay una demostración grabada de cómo usar el editor...

Si tu solicitud se está rompiendo, y no sabes por qué, entonces aquí está exactamente cómo deberían verse tus dos archivos a estas alturas...

Aquí está el índice.js:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081var r =require("rethinkdb"); var express =require("express");var app =require("express")();var http =require("http"). Server(app);var io =require("socket.io")(http);// Setup Databaser.connect({ host: "localhost", port:28015},function(err, conn){if(err)throw err; r .db("test").tableList(). run(conn,function(err, response){if(response.indexOf("edit")-1){// no hacer nada se crea...console.log("La tabla existe, saltando crear...");console.log("Tablas - "+ respuesta);}else{// crear tabla...console.log("La tabla no existe. Creating"); r .db("test").tableCreate("edit").run(conn);}});// Socket Stuff io.on("connection",function(socket){console.log("a user connected"); socket.on("disconnect",function(){console.log("user disconnected");}); socket. on("document-update",function(msg){console.log(msg); r .table("edit").insert({ id: msg.id, value: msg.value, user: msg.user},{ conflict: "update"}).run(conn,function(err, res){if(err)throw err;//console.log(JSON. stringify(res, null, 2));});}); r .table("edit").changes().run(conn,function(err, cursor){if(err)throw err; cursor.each(function(err, row){if(err)throw err; io.emit("doc", row);});}); app.get("/getData/:id",function(req, res, next){ r . table("edit").get(req.params.id).run(conn,function(err, result){if(err)throw err; res.send(result);//return next(result);});});// Serve HTMLapp.get("/",function(req, res){ res.sendFile(__dirname +"/index.html");});app. use("/bower_components", express.static("bower_components"));// Setup Express Listenerhttp.listen(process.env.PORT, process.env.IP,function(){console.log("listening on: "+ process.env.IP+": "+ process.env.PORT);});

javascript

Aquí está el index.html:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788<! doctype html;www.doctype html;www.doctype html;www.doctype html;www.doctype html;www.doctype html;www.doctype html;www.doctype html;www.doctype.html;head;www.title;html;Collaborative Editor</title;www.style;html, body{height:100%;width: 100%;background-color:#333;font-family: arial;}#editor-wrapper{width:70%;margin-left:auto;margin-right:auto;margin-top:3em;}#nombre de usuario{color:#f3f3f3;}. cuser{color:#999;}. ouser{color:#fff;}</estilo;};³³³ón de cabeza;³³³ cuerpo;³³³div³³³³³³"span";³³³³³"usuario actual":³³³"/span";³³³"span";³³³"br³³³";³³³"textarea";³³³"textarea";³³³"/div³³³";³³³"scriptsrc="/bower_components/codemirror/lib/codemirror". js"³³³"/script³³³"link³³³"³³³"/bower_components/codemirror/mode/javascript/javascript.js"³³³"³³³"/script³³³"³³³"/socket.io/socket.io". js"></script ]scriptsrc="/bower_components/jquery/dist/jquery.min.js">/script ];script ];functiongetParameterByName(name, url){if(!url) url =window. location.href; name = nombre.replace(/[[¬]]/g,"\N$&");var regex =newRegExp("[?&]"+ nombre +"(=([^&#]*)|&|#|$)"), results = regex. exec(url);if(!results)returnnnull;if(!results[2])return$0027$0027;returnndecodeURIComponent(results[2]. replace(/+/g," "));}var room =getParameterByName($0027room$0027);var user =getParameterByName($0027user$0027);document.getElementById("username"). innerHTML= user;var myCodeMirror =CódigoMirror.fromTextArea(document.getElementById("editor"),{ lineNumbers:true, mode: "javascript"});var socket =io(); $. ajax({ url:$0027/getData/$0027+ room,success:function(result, status, xhr){ myCodeMirror.setValue(result.value);console.log(result);}}); myCodeMirror.on($0027keyup$0027,function(){var msg ={ id: room, user: user, value: myCodeMirror.getValue()} socket.emit($0027document-update$0027,msg);}); socket. on($0027doc$0027,function(msg){if(msg.new_val.id=== room && msg.new_val.user!= user){var current_pos = myCodeMirror.getCursor(); myCodeMirror. getDoc().setValue(msg.new_val.value); myCodeMirror.setCursor(current_pos);}});</script></body></html>

html