Implementar un ABM y listado de una tabla MySQL utilizando el framework Express como base para la aplicación web.
Ya vimos en un concepto anterior la instalación del MySQL y la creación de la base de datos aquí.
c:\ejerciciosnodejs> express ejercicio23 --hbs
Estamos llamando al programa 'express' y le pasamos dos parámetros, el primero indica el nombre de nuestro proyecto y el segundo el sistema de plantillas que utilizaremos para generar nuestras páginas dinámicas (handlebars)
Ya tenemos creado la carpeta ejercicio23 y dentro de esta los archivos y subcarpetas básicos:
ejercicio23 app.js package.json bin www public images javascripts stylesheets router index.js users.js views error.hbs index.hbs layout.hbs
Instalamos todas las dependencias de módulos:
c:\ejerciciosnodejs\ejercicio23>npm install
Cuando llamamos a 'npm install' sin ningún otro parámetro lo que hace es buscar el archivo 'package.json' y proceder a instalar todos los módulos especificados en la propiedad 'dependencies'.
Ahora ya tenemos creado la carpeta 'node_modules' con las 7 carpetas que coinciden con las dependencias especificadas en el archivo json:
body-parser cookie-parser debug express hbs morgan serve-favicon
Recordemos que hasta ahora hemos creado un esqueleto funcional de una aplicación Node.js utilizando el framework Express y lo podemos ejecutar:
Podemos ejecutar nuestra aplicación mínima creada con el 'express-generador':
c:\ejerciciosnodejs\ejercicio23>node ./bin/www
Y ya podemos solicitar al servidor la página raíz del sitio:
Recordemos que otra forma de iniciar a nuestro proyecto en Node.js cuando definimos el archivo package.json:
En lugar de escribir:
c:\ejerciciosnodejs\ejercicio23>node ./bin/www
Escribimos:
c:\ejerciciosnodejs\ejercicio23>npm start
En el archivo json hay una propiedad start donde definimos el archivo que inicia nuestra aplicación:
"scripts": { "start": "node ./bin/www" },
Instalamos el módulo para comunicarnos con MySQL desde la línea de comandos:
c:\ejerciciosnodejs\ejercicio23>npm install mysql --save
Luego de esto ya tenemos instalado en la carpeta node_modules el paquete mysql y mediante la directiva --save modificamos el archivo package.json agregando la nueva dependencia:
{ "name": "ejercicio23", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www" }, "dependencies": { "body-parser": "~1.13.2", "cookie-parser": "~1.3.5", "debug": "~2.2.0", "express": "~4.13.1", "hbs": "~3.1.0", "morgan": "~1.6.1", "mysql": "^2.9.0", "serve-favicon": "~2.3.0" } }
Entramos en la carpeta routes y abrimos y modificamos el archivo index.js por el siguiente código:
var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { res.render('index'); }); module.exports = router;
Lo único que modificamos es la llamada a render borrando el segundo parámetro.
Ahora abrimos el archivo index.hbs de la carpeta views y creamos el HTML con un menú de opciones de nuestro programa:
<a href="/articulos/creartabla">Creacion de una tabla 'articulos' con MySQL</a></p> <a href="/articulos/alta">alta de articulos</a></p> <a href="/articulos/listado">Listado completo de articulos</a></p> <a href="/articulos/consulta">Consulta de un articulo por codigo</a></p> <a href="/articulos/modificacion">Modificacion de un articulo</a></p>
Si ejecutamos la aplicación ya podemos ver nuestro menú de opciones:
Agregaremos a nuestro archivo app.js una nueva ruta que se encargará todo lo relacionado con el tema de artículos:
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var routes = require('./routes/index'); var users = require('./routes/users'); var articulos = require('./routes/articulos'); var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'hbs'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', routes); app.use('/users', users); app.use('/articulos',articulos); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handlers // development error handler // will print stacktrace if (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: err }); }); } // production error handler // no stacktraces leaked to user app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: {} }); }); module.exports = app;
Las dos líneas que agregamos son el requerimiento del paquete articulos:
var articulos = require('./routes/articulos');
Y el enlace con la aplicación Express:
app.use('/articulos',articulos);
En la carpeta routes crearemos un módulo para iniciar la conexión con la base de datos y poder recuperar una referencia a la misma.
bd.js
var mysql=require('mysql'); var conexion=mysql.createConnection({ host:'localhost', user:'root', password:'', database:'base1' }); conexion.connect(function (error){ if (error) console.log('Problemas de conexion con mysql'); else console.log('se inicio conexion'); }); module.exports=conexion;
Procedemos ahora a crear el módulo 'articulos.js' en la carpeta 'routes' donde dispondremos la lógica para implementar el ABM.
articulos.js
var express = require('express'); var router = express.Router(); var bd=require('./bd'); //Creación de la tabla router.get('/creartabla', function(req, res, next) { bd.query('drop table if exists articulos',function (error,resultado){ if (error) { console.log(error); return; } }); bd.query('create table articulos ('+ 'codigo int primary key auto_increment,'+ 'descripcion varchar(50),'+ 'precio float'+ ')', function (error,resultado){ if (error) { console.log(error); return; } }); res.render('mensajearticulos',{mensaje:'La tabla se creo correctamente.'}); }); //Alta de registros router.get('/alta', function(req, res, next) { res.render('altaarticulos'); }); router.post('/alta', function(req, res, next) { var registro={ descripcion:req.body.descripcion, precio:req.body.precio }; bd.query('insert into articulos set ?',registro, function (error,resultado){ if (error){ console.log(error); return; } }); res.render('mensajearticulos',{mensaje:'La carga se efectuo correctamente'}); }); //Listado de registros router.get('/listado', function(req, res, next) { bd.query('select codigo,descripcion,precio from articulos', function(error,filas){ if (error) { console.log('error en el listado'); return; } res.render('listararticulos',{articulos:filas}); }); }); //Consulta router.get('/consulta', function(req, res, next) { res.render('consultaarticulos'); }); router.post('/consulta', function(req, res, next) { bd.query('select descripcion,precio from articulos where codigo=?',req.body.codigo, function(error,filas){ if (error) { console.log('error en la consulta'); return; } if (filas.length>0) { res.render('listadoconsulta',{articulos:filas}); } else { res.render('mensajearticulos',{mensaje:'No existe el codigo de articulo ingresado'}); } }); }); //Modificacion router.get('/modificacion', function(req, res, next) { res.render('consultamodificacion'); }); router.post('/modificar', function(req, res, next) { bd.query('select descripcion,precio,codigo from articulos where codigo=?',req.body.codigo, function(error,filas){ if (error) { console.log('error en la consulta'); return; } if (filas.length>0) { res.render('formulariomodifica',{articulos:filas}); } else { res.render('mensajearticulos',{mensaje:'No existe el codigo de articulo ingresado'}); } }); }); router.post('/confirmarmodifica', function(req, res, next) { var registro={ descripcion:req.body.descripcion, precio:req.body.precio }; bd.query('UPDATE articulos SET ? WHERE ?',[registro,{codigo:req.body.codigo}], function(error,filas){ if (error) { console.log('error en la consulta'); console.log(error); return; } res.render('mensajearticulos',{mensaje:'El articulo fue modificado'}); }); }); module.exports = router;
Lo primero que hacemos es requerir el paquete bd que se encuentra en la misma carpeta (tanto el archivo articulos.js y bd.js se ubican en la carpeta routes):
var bd=require('./bd');
Para implementar la creación de la tabla tenemos que en el menú de opciones el primer enlace pasa la ruta '/articulos/creartabla':
<a href="/articulos/creartabla">Creacion de una tabla 'articulos' con MySQL</a></p>
Luego esta ruta la capturamos mediante el método get del objeto routes y solo indicamos '/creartabla' ya que en el archivo app.js indicamos app.use('/articulos',articulos)
//Creación de la tabla router.get('/creartabla', function(req, res, next) { bd.query('drop table if exists articulos',function (error,resultado){ if (error) { console.log(error); return; } }); bd.query('create table articulos ('+ 'codigo int primary key auto_increment,'+ 'descripcion varchar(50),'+ 'precio float'+ ')', function (error,resultado){ if (error) { console.log(error); return; } }); res.render('mensajearticulos',{mensaje:'La tabla se creo correctamente.'}); });
Dentro del callback del método get procedemos a llamar al método query del objeto bd y efectuamos el borrado de la tabla articulos si ya existe y posteriormente la creamos con tres campos.
Finalmente pedimos que se muestre la plantilla 'mensajearticulos' y le pasamos como parámetro un objeto literal con un atributo llamado mensaje. El archivo mensajearticulos se almacena en la carpeta views y su contenido es:
<p>{{mensaje}}</p> <a href="/">Retornar</a>
Es decir mostramos el contenido del mensaje en un hipervínculo a la raiz del sitio web.
Si probamos de ejecutar la primer opción de nuestro menú tendremos como resultado la creación de la tabla (no olvidar de iniciar de arrancar el MySQL y crear la base de datos 'base1'):
Para implementar el alta en la tabla articulos se inicia cuando presionamos la segunda opción de nuestro menú:
<a href="/articulos/alta">alta de articulos</a></p>
Y desde el archivo articulos.js procedemos a capturar dicha ruta:
router.get('/alta', function(req, res, next) { res.render('altaarticulos'); });
En el método get procedemos a mostrar el contenido del archivo 'altaarticulos' que se encuentra como ya sabemos en la carpeta views y su contenido es:
<form method="post" action="/articulos/alta"> Ingrese descripcion del articulo: <input type="descripcion" name="descripcion" size="50"> <br> Ingrese el precio del articulo: <input type="text" name="precio" size="10"> <br> <input type="submit" value="Agregar"> </form>
Desde el navegador podemos observar el formulario de carga:
Cuando se presiona el botón submit procedemos a capturar dicha ruta en el archivo articulos.js donde procedemos a cargar los datos en la tabla de la base de datos:
router.post('/alta', function(req, res, next) { var registro={ descripcion:req.body.descripcion, precio:req.body.precio }; bd.query('insert into articulos set ?',registro, function (error,resultado){ if (error){ console.log(error); return; } }); res.render('mensajearticulos',{mensaje:'La carga se efectuo correctamente'}); });
Podemos observar que llamamos nuevamente a la plantilla 'mensajearticulos' pero con un mensaje distinto a la creación de la tabla que vimos en el paso anterior.
Para implementar el listado completo de la tabla articulos se llama desde nuestro menú:
<a href="/articulos/listado">Listado completo de articulos</a></p>
Y en el archivo 'articulos.js' procedemos a capturar dicha ruta en el método:
router.get('/listado', function(req, res, next) { bd.query('select codigo,descripcion,precio from articulos', function(error,filas){ if (error) { console.log('error en el listado'); return; } res.render('listararticulos',{articulos:filas}); }); });
Mediante un select recuperamos todas las filas de la tabla 'articulos' y llamamos al método sender pasando como segundo parámetro un objeto literal con un atributo que contiene todas las filas recuperadas.
En el archivo listaarticulos.hbs procedemos a mostrar los datos pasados en el objeto literal:
<table border="1"> <tr> <td>Codigo</td><td>Descripcion</td><td>Precio</td> </tr> {{#each articulos}} <tr> <td>{{codigo}} </td> <td>{{descripcion}}</td> <td>{{precio}}</td> </tr> {{/each}} </table> <a href="/">Retornar</a>
En el navegador podemos observar como se muestra la tabla de datos luego de procesarse la plantilla:
Para implementar la consulta de un articulo por su código llamamos desde nuestro menú:
<a href="/articulos/consulta">Consulta de un articulo por codigo</a></p>
Y en el archivo articulos.js capturamos la ruta y devolvemos la plantilla 'consultaarticulos':
router.get('/consulta', function(req, res, next) { res.render('consultaarticulos'); });
La plantilla consultaarticulos.hbs:
<form method="post" action="/articulos/consulta"> Ingrese codigo del articulo a consultar: <input type="text" name="codigo" size="5"> <br> <input type="submit" value="consultar"> </form>
En el navegador podemos ver:
Y luego que se presiona el botón submit capturamos la ruta en el método donde procedemos a buscar el código de articulo ingresado:
router.post('/consulta', function(req, res, next) { bd.query('select descripcion,precio from articulos where codigo=?',req.body.codigo, function(error,filas){ if (error) { console.log('error en la consulta'); return; } if (filas.length>0) { res.render('listadoconsulta',{articulos:filas}); } else { res.render('mensajearticulos',{mensaje:'No existe el codigo de articulo ingresado'}); } }); });
En el caso que exista el código de articulo ingresado procedemos a generar la plantilla 'listadoconsulta.hbs' y pasar un objeto literal para que se muestre los datos:
{{#each articulos}} <p>Descripcion:{{descripcion}}<br> Precio:{{precio}}</p> {{/each}} <a href="/">Retornar</a>
En el navegador podemos ver en el caso que exista el código:
Si no existe el código de artículo mostramos la plantilla 'mensajearticulos.hbs' pasando el mensaje respectivo.
Finalmente para implementar la modificación de un articulo llamamos desde nuestro menú:
<a href="/articulos/modificacion">Modificacion de un articulo</a></p>
Y en el archivo articulos.js capturamos la ruta y devolvemos la plantilla 'consultamodificacion':
router.get('/modificacion', function(req, res, next) { res.render('consultamodificacion'); });
La plantilla consultamodificacion es:
<form method="post" action="/articulos/modificar"> Ingrese codigo del articulo a modificar: <input type="text" name="codigo" size="5"> <br> <input type="submit" value="buscar"> </form>
En el navegador podemos ver:
Cuando se presiona el botón submit se captura la ruta y procedemos a consultar el código y cargar la plantilla formulariomodifica.hbs:
router.post('/modificar', function(req, res, next) { bd.query('select descripcion,precio,codigo from articulos where codigo=?',req.body.codigo, function(error,filas){ if (error) { console.log('error en la consulta'); return; } if (filas.length>0) { res.render('formulariomodifica',{articulos:filas}); } else { res.render('mensajearticulos',{mensaje:'No existe el codigo de articulo ingresado'}); } }); });
La plantilla formulariomodifica.hbs muestra la descripción y precio actual del artículo y almacena en un campo hidden el código de artículo que estamos modificando:
{{#each articulos}} <form method="post" action="/articulos/confirmarmodifica"> Ingrese nueva descripcion del articulo: <input type="descripcion" name="descripcion" size="50" value="{{descripcion}}"> <br> Ingrese nuevo precio del articulo: <input type="text" name="precio" size="10" value="{{precio}}"> <input type="hidden" name="codigo" size="10" value="{{codigo}}"> <br> <input type="submit" value="Modificar"> </form> {{/each}}
En el navegador podemos ver:
Cuando se presiona el botón 'submit' procedemos a capturar la ruta mediante el método:
router.post('/confirmarmodifica', function(req, res, next) { var registro={ descripcion:req.body.descripcion, precio:req.body.precio }; bd.query('UPDATE articulos SET ? WHERE ?',[registro,{codigo:req.body.codigo}], function(error,filas){ if (error) { console.log('error en la consulta'); console.log(error); return; } res.render('mensajearticulos',{mensaje:'El articulo fue modificado'}); }); });
En este algoritmo procedemos a modificar una fila de la tabla artículos y mostrar un mensaje que la modificación fue hecha.
Este proyecto lo puede descargar en un zip con todos los archivos desde este enlace : ejercicio23