CRUD con Angular, Node.js y MongoDB, parte 1

Tener la mente abierta es la clave para ser desarrollador web, independientemente de que tengas mucha o poca experiencia en esto. Las tecnologías web no son equipos de fútbol, como lo son para muchos, que debaten en foros cuál es el mejor, y se pelean como “gatos” de farándula por quién tiene la razón, las tecnologías web son herramientas que nos sirven para desarrollar, algunas tienen más ventajas que otras o desventajas con respecto a otras, sin embargo el tener la voluntad de aprender lo nuevo es el primer paso a saber qué haremos en el futuro.

Es evidente que hay un cambio en la web (ni hablar del desarrollo móvil), y con esto llegan nuevas tecnologías, que pretenden solucionar problemas existentes a los desarrolladores. En esta enorme cola de caras nuevas, hay tres que se destacan: Angular, Node.js y MongoDB, en este último caso también podríamos decir para generalizar, bases de datos NoSQL.

Para realizar este ejemplo vamos a hacer un breve ejemplo de cómo agregar, listar, modificar y eliminar registros, lo que se conoce como CRUD. Tendremos una lista de clientes, y por cada uno de estos tendremos que guardar su nombre, apellido, domicilio, teléfono y correo electrónico. Veremos con Angular cómo trabajar de forma separada en el front, el código html tendrá toda la parte visual, mientras que javascript se encargará sólo de la lógica de la misma.

Primero vamos a crear un nuevo proyecto en Node y Express. Si no sabés cómo hacerlo, podés verlo en esta publicación que subí hace ya un tiempo:

https://fernando-gaitan.com.ar/introduccion-a-node-js-parte-7-instalar-express/

También necesitamos conectar nuestra aplicación a MongoDB, mediante Mongoose, si no sabés cómo:

https://fernando-gaitan.com.ar/introduccion-a-node-js-parte-12-conexion-con-mongodb/

Entonces empezamos creando nuestro proyecto yendo a la carpeta de nuestros sitios Node. Ahí deberías tener una carpeta vacía con el nombre de tu proyecto. Yo lo llamaré angular_node_mongodb. Entonces entramos a nuestro proyecto:

cd ruta_a_proyecto

Y creamos el árbol de directorios y archivos con express, escribiendo en la consola:

express

Luego abrimos el archivo package.json y agregamos la dependencia de mongoose, dentro de “dependencies”, escribimos:

"mongoose": "*"

Finalmente volvemos a la consola para instalar nuestras dependencias:

npm install

Bien, ahora vamos a separar nuestro ejemplo en dos partes, en la siguiente publicación veremos cómo trabajar con registros en la base de datos, pero en éste vamos a crear una pequeña funcionalidad que va a trabajar con registros temporales, sólo con Html y Javascript:

En primer lugar abrimos nuestro archivo app.js, en el raíz del proyecto. Eliminamos las líneas que vienen por defecto:

app.get('/', routes.index);
app.get('/users', user.list);

Y creamos nuestra propia acción:

app.get('/', function(req, res){
   res.sendfile('./public/index.html');
});

Ahora vamos a nuestra carpeta public y creamos un archivo html llamado: index.html. Por el momento vamos a dejarlo sin código.

Acto seguido vamos a necesitar tres cosas. Primero descargar la librería angular. Para ello vamos a su página oficial:

https://angularjs.org/

Ahí vamos al botón que dice: “Download” y elegimos una de las formas de incluir el archivo, en mi caso, voy a descargar el archivo angular.min.js. Lo guardamos dentro de public -> javascripts.

También a la misma altura vamos a crear un archivo vacío, al que vamos a llamar script.js.

Además, y aunque esto es opcional, yo voy a descargar Twitter Bootstrap, para darle un poco de estilo a nuestro sitio:

De acá podemos descargarlo:

https://getbootstrap.com/2.3.2/

Yo voy  guardar el archivo bootstrap.min.css en public -> stylesheets.

Bien, ahora vamos a pensar qué es lo que va a necesitar la lógica de nuestra aplicación. En primer lugar vamos a necesitar dónde guardar los datos del cliente en forma termporal: nombre, apellido, domicilio, teléfono y un correo electrónico. También un array con todos los clientes. Y acciones para guardar, eliminar, listar clientes y recuperar sus datos en forma individual.

Así que vamos dentro de public -> javascripts -> script.js y escribimos dentro de este archivo el siguiente código:

var aplicacion = angular.module('aplicacion', []);
aplicacion.controller('Clientes', function($scope) {
   $scope._id = null;
   $scope.nombre = '';
   $scope.apellido = ''
   $scope.domicilio = '';
   $scope.telefono = '';
   $scope.email = '';
   $scope.clientes = [];
   $scope.guardarCliente = function() {
      if ($scope._id == null) {
         $scope.clientes.push({
            nombre: $scope.nombre,
            apellido: $scope.apellido,
            domicilio: $scope.domicilio,
            telefono: $scope.telefono,
            email: $scope.email
         }); 
      } else {
         $scope.clientes[$scope._id] = {
            nombre: $scope.nombre,
            apellido: $scope.apellido,
            domicilio: $scope.domicilio,
            telefono: $scope.telefono,
            email: $scope.email
         };
      }
      $scope.limpiarDatos();
   }
   $scope.recuperarCliente = function(index) {
      $scope._id = index;
      $scope.nombre = $scope.clientes[index].nombre;
      $scope.apellido = $scope.clientes[index].apellido;
      $scope.domicilio = $scope.clientes[index].domicilio;
      $scope.telefono = $scope.clientes[index].telefono;
      $scope.email = $scope.clientes[index].email;
   };
   $scope.eliminarCliente = function(indice) {
      var clientes_actualizado = [];
      for (var i = 0; i < $scope.clientes.length; i++) {
         if (i != indice) {
            clientes_actualizado.push($scope.clientes[i]);
         }
      }
      $scope.clientes = clientes_actualizado;
   };
   $scope.limpiarDatos = function() {
      $scope._id = null;
      $scope.nombre = '';
      $scope.apellido = '';
      $scope.domicilio = '';
      $scope.telefono = '';
      $scope.email = '';
   };
});

Vamos a analizar el código, en primer lugar creamos una instancia de angular:

var aplicacion = angular.module('aplicacion', []);

En angular se trabaja con módulos, cada uno de estos módulos puede tener varios controladores, que nos permitirán darle funcionalidad a los distintos bloques de nuestro código html.

Definimos un nuevo controlador al que llamamos Clientes. Los controladores recibirán dos parámetros, el nombre del mismo y una función de tipo callback a la cual le pasamos como parámetro el scope ($scope). Este scope es un objeto que nos permite asignarle propiedades y métodos al controlador.

En primer lugar creamos seis propiedades para guardar los datos temporales de cada cliente:

$scope._id = null;
$scope.nombre = '';
$scope.apellido = ''
$scope.domicilio = '';
$scope.telefono = '';
$scope.email = '';

La pripiedad _id, no es el id que se almacena en la base de datos, sino la posición que identifica al usuario dentro de la propiedad clientes:

$scope.clientes = [];

En este array iremos guardando la lista de todos los clientes.

También tendremos un método para limpiar estos datos:

$scope.limpiarDatos = function() {
   $scope._id = null;
   $scope.nombre = '';
   $scope.apellido = '';
   $scope.domicilio = '';
   $scope.telefono = '';
   $scope.email = '';
};

Para crear o modificar un cliente, tendremos el método guardarCliente():

$scope.guardarCliente = function() {
   if ($scope._id == null) {
      $scope.clientes.push({
         nombre: $scope.nombre,
         apellido: $scope.apellido,
         domicilio: $scope.domicilio,
         telefono: $scope.telefono,
         email: $scope.email
      }); 
   } else {
      $scope.clientes[$scope._id] = {
         nombre: $scope.nombre,
         apellido: $scope.apellido,
         domicilio: $scope.domicilio,
         telefono: $scope.telefono,
         email: $scope.email
      };
   }
   $scope.limpiarDatos();
}

El hecho de que cree o modifique un nuevo cliente dependerá de:

if ($scope._id == null)

Si existe un _id (posición del array clientes) va a modificar el cliente, sino va a insertar uno nuevo.

El método recuperarCliente() como su nombre lo indica va a recuperar un cliente, y va a recibir como parámetro el índice del cliente en el array:

$scope.recuperarCliente = function(index) {
   $scope._id = index;
   $scope.nombre = $scope.clientes[index].nombre;
   $scope.apellido = $scope.clientes[index].apellido;
   $scope.domicilio = $scope.clientes[index].domicilio;
   $scope.telefono = $scope.clientes[index].telefono;
   $scope.email = $scope.clientes[index].email;
};

Mientras que eliminarCliente(), también recibirá como parámetro el índice del cliente que deberá eliminar, generando un nuevo array sin la posición del cliente a remover:

$scope.eliminarCliente = function(indice) {
   var clientes_actualizado = [];
   for (var i = 0; i < $scope.clientes.length; i++) {
      if (i != indice) {
         clientes_actualizado.push($scope.clientes[i]);
      }
   }
   $scope.clientes = clientes_actualizado;
};

Bien, ya sabemos cómo funciona nuestra aplicación, ahora sólo debemos crear la vista, trabajará con esta lógica. Así que vamos a  public  -> index.html y editamos el archivo con el siguiente código:

<!DOCTYPE html>
<html ng-app="aplicacion">
   <head>
      <meta charset="utf-8" />
      <title> Clientes </title>
      <link type="text/css" href="/stylesheets/bootstrap.min.css" rel="stylesheet" />
      <script type="text/javascript" src="/javascripts/angular.min.js"></script>
      <script type="text/javascript" src="/javascripts/script.js"></script>
   </head>
   <body>
      <div class="container" ng-controller="Clientes">
         <h1> Clientes </h1>
         <form action="javascript:void(0);">
            <div class="row">
               <div class="span4">
                  <label> Nombre </label>
                  <input type="text" ng-model="nombre" />
               </div>
               <div class="span4"> 
                  <label> Apellido </label>
                  <input type="text" ng-model="apellido" />
               </div>
               <div class="span4">
                  <label> Domicilio </label>
                  <input type="text" ng-model="domicilio" />
               </div>
            </div>
            <div class="row">
               <div class="span4">
                  <label> Teléfono </label>
                  <input type="text" ng-model="telefono" />
               </div>
               <div class="span4">
                  <label> Email </label>
                  <input type="text" ng-model="email" />
               </div> 
            </div>
            <div class="row">
               <div class="span4">
                  <input type="submit" value="Guardar" class="btn btn-success" ng-click="guardarCliente()" />
                  <input type="button" value="Limpiar" class="btn btn-danger" ng-click="limpiarDatos()" />
               </div>
            </div>
         </form>
         <table class="table">
            <thead>
               <tr>
                  <th> Nombre </th>
                  <th> Apellido </th>
                  <th> Domicilio </th>
                  <th> Teléfono </th>
                  <th> Email </th>
                  <th> </th>
                  <th> </th>
               </tr>
            </thead>
            <tbody>
               <tr ng-repeat="item in clientes">
                   <td> {{item.nombre}} </td>
                   <td> {{item.apellido}} </td>
                   <td> {{item.domicilio}} </td>
                   <td> {{item.telefono}} </td>
                   <td> {{item.email}} </td>
                   <td> <a href="javascript:void(0);" class="btn btn-info" ng-click="recuperarCliente($index)"> Editar </a> </td>
                   <td> <a href="javascript:void(0);" class="btn btn-danger" ng-click="eliminarCliente($index)"> Eliminar </a> </td>
                 </tr>
              </tbody>
           </table>
      </div>
   </body>
</html>

Vamos a analizar el código.

En primer lugar agregamos ng-app, un atributo inyectable propio de Angular, que nos permitirá definir un módulo. No es obligatorio que esté en la etiqueta html, pero sí debe ser una etiqueta padre, de por ejemplo aquellas etiquetas que hagan referencia a controladores de ese módulo. En nuestro caso ng-app, hace referencia al módulo aplicacion.

<html ng-app="aplicacion">

Luego hacemos referencia al controlador Clientes, mediante el atributo ng-controller:

<div ng-controller="Clientes">

De esta forma todas las etiquetas que estén aquí dentro podrá acceder a todos las propiedades y métodos de este controlador.

Por ejemplo, el campo para ingresar el domicilio:

<input type="text" ng-model="domicilio" />

Éste, mediante el atributo ng-model, estará vinculado directamente con la propiedad domicilio:

$scope.domicilio

Los modelos son los que trabajan con los datos. Cada vez que este campo de ingreso cambie su value, el valor de la propiedad domicilio también lo hará y viceversa. Esto es absolutamente dinámico y no debemos preocuparnos por nada más.

(Los ng son llamados directivas en Angular)

Las directivas también nos permiten llamar métodos del controlador al cual pertenecen:

<input type="submit" value="Guardar" ng-click="guardarCliente()" />
<input type="button" value="Limpiar" ng-click="limpiarDatos()" />

Creo que está de más decir que ng-click hace referencia al evento onclick.

Otro detalle importante de angular es hasta dónde llega su forma de trabajar dinámica.

<tr ng-repeat="item in clientes">
   <td> {{item.nombre}} </td>
   <td> {{item.apellido}} </td>
   <td> {{item.domicilio}} </td>
   <td> {{item.telefono}} </td>
   <td> {{item.email}} </td>
   <td> <a href="javascript:void(0);" ng-click="recuperarCliente($index)"> Editar </a> </td>
   <td> <a href="javascript:void(0);" ng-click="eliminarCliente($index)"> Eliminar </a> </td>
</tr>

En este último caso, mediante la directiva ng-repeat, podremos recorrer un array, que aquí es clientes. En cuanto este array sea modificado el html se actualizará automáticamente.

Finalmente para probar nuestro ejemplo podemos ir a la consola dentro de nuestro proyecto node.js e iniciar el servidor:

node app.js

En la próxima publicación veremos como realizar esto enviando peticiones ajax y trabajando con la base de datos mongodb.

Descargar ejemplo

Ver parte 2

Redes sociables