Unidad 4
Unidad 4
Facultad Regional Córdoba Módulo: API Rest con NodeJS y SPA con Angular 5
Curso:
DIPLOMATURA EN DESARROLLO WEB EV 15030
Módulo:
“API Rest con NodeJS y SPA con Angular 5”
Autor:
Ing. Mario Di Giorgio
Referencias
Este material se confecciono en base a las últimas tendencias que hoy se encuentra en Internet.
Para eso se buscó el mejor material y se recompilo dicha información para facilitar el aprendizaje
del mismo.
Índice
Capítulo 9 .................................................................................................................................. 5
REST...................................................................................................................................... 5
Capítulo 10 ...............................................................................................................................18
Angular 5 ...............................................................................................................................18
10.6 Crear rutas en Express para acceder a los datos del libro vía API RESTful ............... 33
10.10 Modificar el componente de creación de libro para usar validadores especiales. ..... 55
Capítulo 9
REST
9.1 Introducción básica
El primer paso es crear un directorio en tu entorno local para la aplicación, e iniciar un repositorio
para guardar los cambios y que luego podamos desplegarlo por ejemplo en Heroku. Yo
personalmente uso Git porque es una maravilla de la creación y porque a Heroku es lo que le
mola ;)
C:\ md node-api-rest-example
C:\ cd node-api-rest-example
Antes de empezar, necesitas tener Node instalado en tu computadora, para que funcione en tu
entorno local de desarrollo.
El primer código que necesitamos escribir en una aplicación basada en Node es el archivo
package.json. Éste archivo nos indica que dependencias vamos a utilizar en ella. Este archivo
va en el directorio raíz de la aplicación:
Y ahora para descargar las dependencias escribimos lo siguiente en la consola y NPM (el gestor
de paquetes de Node) se encargará de instalarlas.
$ npm install
Con todo listo, podemos comenzar a codear de verdad. Creamos un fichero llamado app.js en el
directorio raíz que será el que ejecute nuestra aplicación y arranque nuestro server. Crearemos
en primer lugar un sencillo servidor web para comprobar que tenemos todo lo necesario
instalado, y a continuación iremos escribiendo más código.
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
methodOverride = require("method-override");
mongoose = require('mongoose');
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(bodyParser.json());
app.use(methodOverride());
app.use(router);
app.listen(3000, function () {
console.log("Node server running on http://localhost:3000");
});
¿Qué hace este código? Muy sencillo, las primeras líneas se encargan de incluir las
dependencias que vamos a usar, algo así como los includes en C o PHP, o los import de Python.
Importamos Express para facilitarnos crear el servidor y realizar llamadas HTTP. Con http
creamos el servidor que posteriormente escuchará en el puerto 3000 de nuestro ordenador (O el
que nosotros definamos).
Con bodyParser permitimos que pueda transformar JSON, methodOverride() nos permite
implementar y personalizar métodos HTTP.
Podemos declarar las rutas con app.route (nombre_de_la_ruta) seguido de los verbos .get(),
.post(), etc… y podemos crear una instancia para ellas con express.Router(). En este primer
ejemplo vamos a hacer que sólo reciba una petición GET del navegador y muestre en el mismo
la frase “Hello World”
Para ejecutar este pequeño código sólo tienes que escribir en consola lo siguiente y abrir un
navegador con la URL http://localhost:3000
C:\node_path\node app.js
Node server running on http://localhost:3000
En esta parte vamos a crear un modelo usando Mongoose para poder guardar la información en
la base de datos siguiendo el esquema. Como base de datos vamos a utilizar MongoDB.
MongoDB es una base de datos Open Source NoSQL orientada a documentos tipo JSON, lo
cual nos viene que ni pintado para entregar los datos en este formato en las llamadas a la API.
Para este ejemplo vamos a crear una base de datos de series de TV, por tanto, vamos a crear
un modelo (Archivo: models/tvshow.js) que incluya la información de una serie de TV, como
pueden ser su título, el año de inicio, país de producción, una imagen promocional, número de
temporadas, género y resumen del argumento:
var mongoose = require('mongoose'),
Autor: Ing. Mario Di Giorgio 7
UNIVERSIDAD TECNOLÓGICA NACIONAL Diplomatura en Desarrollo WEB
Facultad Regional Córdoba Módulo: API Rest con NodeJS y SPA con Angular 5
Schema = mongoose.Schema;
Con esto ya podemos implementar la conexión a la base de datos en el archivo app.js añadiendo
las siguientes líneas:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/tvshows');
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(bodyParser.json());
app.use(methodOverride());
app.use(router);
Para que esto funcione en nuestro entorno local, necesitamos tener instalado MongoDB.
Con Mongo arrancado ya podemos ejecutar la aplicación como en la parte anterior con node
app.js desde la terminal, si todo va bien tendremos algo en la pantalla como esto:
C:\node_path\node app.js
Node server running on http://localhost:3000
Connected to Database
Ahora desde otra terminal, podemos entrar al shell de MongoDB y comprobar que la base de
datos se ha creado correctamente. Para ello ejecutamos el comando mongo
C:\mongodb_path\mongo
MongoDB shell version: 2.4.1
connecting to: test
> use tvshows
switched to db tvshows
> show dbs
local 0.078125GB
tvshows (empty)
>_
Ya tenemos todo configurado y listo para albergar los datos, sólo nos queda crear las rutas que
definirán las llamadas a la API para poder guardar y consultar la información.
Los controladores de las rutas de nuestro API los vamos a crear en un archivo separad que
llamaremos controllers/tvshows.js. Gracias a exports conseguimos modularizarlo y que pueda
ser llamado desde el archivo principal de la aplicación. El código de a continuación es el comienzo
del archivo con la primera función que será la que devuelva todos los registros almacenados:
//File: controllers/tvshows.js
var mongoose = require('mongoose');
var TVShow = mongoose.model('TVShow');
console.log('GET /tvshows')
res.status(200).jsonp(tvshows);
});
};
De esta manera tan sencilla, al llamar a la función findAllTVShows se envía como respuesta toda
la colección de tvshows almacenada y en formato JSON. Si queremos que sólo nos devuelva un
registro con un identificador único, tenemos que crear una función tal que la siguiente:
//GET - Return a TVShow with specified ID
exports.findById = function (req, res) {
TVShow.findById(req.params.id, function (err, tvshow) {
if (err) return res.send(500. err.message);
Con las funciones find() y findById() podemos buscar en la base de datos a partir de un modelo.
Ahora desarrollaré el resto de funciones que permiten insertar, actualizar y borrar registros de la
base de datos. La función de a continuación sería la correspondiente al método POST y lo que
hace es añadir un nuevo objeto a la base de datos:
//POST - Insert a new TVShow in the DB
exports.addTVShow = function (req, res) {
console.log('POST');
console.log(req.body);
Primero creamos un nuevo objeto tvshow siguiendo el patrón del modelo, recogiendo los valores
del cuerpo de la petición, lo salvamos en la base de datos con el comando save() y por último lo
enviamos en la respuesta de la función.
La siguiente función nos permitirá actualizar un registro a partir de un ID. Primero buscamos en
la base de datos el registro dado el ID, y actualizamos sus campos con los valores que devuelve
el cuerpo de la petición:
//PUT - Update a register already exists
exports.updateTVShow = function (req, res) {
TVShow.findById(req.params.id, function (err, tvshow) {
tvshow.title = req.body.petId;
tvshow.year = req.body.year;
tvshow.country = req.body.country;
tvshow.poster = req.body.poster;
tvshow.seasons = req.body.seasons;
tvshow.genre = req.body.genre;
tvshow.summary = req.body.summary;
Autor: Ing. Mario Di Giorgio 11
UNIVERSIDAD TECNOLÓGICA NACIONAL Diplomatura en Desarrollo WEB
Facultad Regional Córdoba Módulo: API Rest con NodeJS y SPA con Angular 5
tvshow.save(function (err) {
if (err) return res.status(500).send(err.message);
res.status(200).jsonp(tvshow);
});
});
};
Y por último para completar la funcionalidad CRUD de nuestra API, necesitamos la función que
nos permita eliminar registros de la base de datos y eso lo podemos hacer con el código de a
continuación:
//DELETE - Delete a TVShow with specified ID
exports.deleteTVShow = function (req, res) {
TVShow.findById(req.params.id, function (err, tvshow) {
tvshow.remove(function (err) {
if (err) return res.status(500).send(err.message);
res.status(200).send();
})
});
};
Como puedes ver, usamos de nuevo el método .findById() para buscar en la base de datos y
para borrarlo usamos .remove() de la misma forma que usamos el .save() para salvar.
Ahora tenemos que unir estas funciones a las peticiones que serán nuestras llamadas al API.
Volvemos a nuestro archivo principal, app.js y declaramos las rutas, siguiendo las pautas de
Express v.4
var TVShowCtrl = require('./controllers/tvshows');
// API routes
var tvshows = express.Router();
tvshows.route('/tvshows')
.get(TVShowCtrl.findAllTVShows)
.post(TVShowCtrl.addTVShow);
tvshows.route('/tvshows/:id')
.get(TVShowCtrl.findById)
.put(TVShowCtrl.updateTVShow)
.delete(TVShowCtrl.deleteTVShow);
app.use('/api', tvshows);
A continuación, voy a probar una herramienta online que nos permite jugar con las llamadas al
API y poder consultar y almacenar datos para probarla y ver su funcionamiento un poco más
claro.
Para ello nos dirigimos a restconsole.com que es una extensión de Google Chrome, que permite
hacer lo que queremos de una manera visual y sencilla.
Antes de probarlo, debemos tener mongo y el servidor node de nuestra app corriendo. Una vez
hecho esto introducimos los siguientes datos para hacer una llamada POST que almacene un
registro en la base de datos.
Pulsamos SEND y si todo va bien, la petición se realizará y abajo de la aplicación REST Console
veremos algo como esto:
"year": 2004,
"country": "USA",
"poster": "http://ia.media-
imdb.com/images/M/MV5BMjA3NzMyMzU1MV5BMl5BanBnXkFtZTcwNjc1ODUwMg@@._V1_SY317_CR17
,0,214,317_.jpg",
"seasons": 6,
"genre": "Sci-Fi",
"summary": "The survivors of a plane crash are forced to live with each
other on a remote island, a dangerous new world that poses unique threats of its
own.",
"_id": ObjectId("51b44d7899ac57fb18000002"),
"__v": 0
}
Y ahí la tenemos, puedes probar a introducir alguna más, para tener mayor contenido con el que
probar. Una vez introduzcas varios registros, puedes probar a llamarlos a través de la petición
GET que hemos preparado: http://localhost:3000/tvshows la cuál te mostrará algo parecido a
esto:
También podemos llamar a un sólo registro gracias a la petición GET tvshows/:id que
programamos, si ejecutamos por ejemplo
http://localhost:3000/tvshow/51b44d7899ac57fb18000002 nos devolverá un único objeto:
Los métodos restantes PUT y DELETE funcionan de manera parecida al POST sólo que hay que
pasarte el valor del ID del objeto que queremos actualizar o borrar. Te invito a que lo pruebes en
la Tutorial de NodeJS, creando un API REST - REST Console.
Con esto tendríamos el funcionamiento básico y programación de lo que sería una API REST.
Como puedes ver es bastante sencillo y utilizas Javascript en todas partes, como lenguaje de
servidor (Node), como formato de datos (JSON) y como base de datos (MongoDB)
Capítulo 10
Angular 7
10.1 INTRODUCCION
ANGULAR es un framework de desarrollo para JavaScript creado por Google para crear
principalmente “single page application” (SPA), o aplicaciones de página única. Es uno de los
frameworks más populares para desarrollar aplicaciones modernas y escalables en el lado del
cliente.
En este artículo veremos cómo crear rápidamente una aplicación Angular (Angular 2, Angular
4 , Angular 5) utilizando Angular CLI (Command Line Interface) y Angular Material un módulo
que nos permite trabajar con componentes de interfaz de usuario completos y modernos que
funcionan en la web, el móvil y en escritorio basados en Material Design. (Material Design es un
conjunto de especificaciones definidas por Google para mantener una estructura coherente y
atractiva en la web y aplicaciones móviles.)
Las últimas versiones de Angular han mejorado el funcionamiento general del framework y nos
permite utilizar TypeScript para definir nuestras clases, propiedades y métodos, generando un
código mucho más limpio.
Crearemos una aplicación utilizando la versión de Angular 5 (desde cero) para mostrar logros o
historias, crearemos nuestro propio componente, mostraremos datos de variables, listaremos
arreglos y utilizaremos angular material para mejorar la interfaz de usuario.
10.2 Requisitos
Para comenzar, asegúrese de tener instalado NodeJS (v6.9 +) y NPM (v3.x +). en su máquina.
Antes que nada debemos saber que Angular a partir de la versión 2 cambia de paradigma de
programación y se va de la clásica programación funcional a la programación orientada a objetos
para lo que es necesario tener un código que sea fuertemente tipado, algo que JavaScript carece
dado sus características de lenguaje dinámico y flexible, por lo que siempre regulado por los
Autor: Ing. Mario Di Giorgio 18
UNIVERSIDAD TECNOLÓGICA NACIONAL Diplomatura en Desarrollo WEB
Facultad Regional Córdoba Módulo: API Rest con NodeJS y SPA con Angular 5
Esto se hizo en pos de poder usar codigo de manera mas segura y mejorar la depuración y el
control de errores que con JavaScript puro es muy difícil, casi imposible de mantener.
Lo que queda por saber de TypeScript es que es un lenguaje que luego debe ser transcripto
enteramente a JavaScript por eso Angular utiliza un transpilador (mezcla de traductor y
compilador) que traduce y compila el codigo TypeScript a JavaScript generando un directorio con
los archivos compilado .js que luego serán ejecutados por el browser, es por eso que cada vez
que se modifique un archivo TypeScript debe ser compilado nuevamente para poder ser
ejecutado cosa que no era necesario con JavaScript, algo que ya el propio lenguaje al ser
interpretado directamente por el browser nos permite.
A esto se lo conoce como superset, que es un lenguaje escrito sobre otro lenguaje, es decir no
se puede ejecutar directamente TypeScript sino que se hace a través del JavaScript que éste
genera.
Si todo salió bien, el comando ng --version nos mostrara las versiones instaladas:
ng --version
Ver al proceso de compilación de Angular que ejecuta el server de Angular 5 que nos proveerá
las páginas web en el puerto 4200.
** NG Live Development Server is listening on localhost:4200, open your browser
on http://localhost:4200/ **
Date: 2017-11-10T23:12:58.186Z -
Hash: a8de16d629b34a42bbda
Time: 9459ms
chunk {inline} inline.bundle.js (inline) 5.79 kB [entry] [rendered]
chunk {main} main.bundle.js (main) 20.6 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js (polyfills) 553 kB [initial] [rendered]
chunk {styles} styles.bundle.js (styles) 33.8 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js (vendor) 7.03 MB [initial] [rendered]
Ahora, abra el navegador y vaya a ' http://localhost:4200 ' debería ver esta
página.
Cerrar la aplicación Angular primero pulse 'ctrl + c' a continuación asegurese de tener instalado
el paquete de express-generator, ejecute el comando
npm install -g express-generator
Luego escriba
express --version
Deberá ver la versión del generador de servidor express, si logra verlo ahora es hora de
configurar el nuevo servidor de Node.js con Express, pero antes debe renombrar el archivo
package.json a package.json.angular para evitar que se sobrescriba y luego usamos el comando.
express .
se generarán los archivos necesarios para la ejecución del servidor express incluido el archivo
de inicio app.js.
Vamos a combinarlos en un solo archivo package.json, debe pegar todas las dependencias de
express en el archivo de angular…
package.json
"dependencies": {
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"http-errors": "~1.6.2",
"jade": "~1.11.0",
"morgan": "~1.9.0"
}
"dependencies": {
"@angular/animations": "~7.2.0",
"@angular/common": "~7.2.0",
"@angular/compiler": "~7.2.0",
"@angular/core": "~7.2.0",
"@angular/forms": "~7.2.0",
"@angular/platform-browser": "~7.2.0",
"@angular/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0",
"core-js": "^2.5.4",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26",
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"http-errors": "~1.6.2",
"jade": "~1.11.0",
"morgan": "~1.9.0"
},
A esto…
"scripts": {
"start": "ng build && node ./bin/www",
Luego de guardar todos los cambios, asegúrese de eliminar el archivo package.json generado
por express-generator y renombrar el que acabamos de editar de package.json.angular a
package.json, ahora nuestro nuevo package.json se tiene que ver de la siguiente manera…
{
"name": "ng-test",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng build && node ./bin/www",
"build": "ng build",
"test": "ng test",
Para asegurarse que los paquetes queden instalados correctamente ejecute el comando
npm install
Luego edite el archivo app.js y agregue las siguiente línea en la sección de variables:
var bodyparser = require('body-parser’);
Por
app.use(express.static(path.join(__dirname, 'dist/book-store')));
Tambien reemplace
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
Por
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({ extended: false }));
Si no posee el paquete de express generator o no desea instalarlo puede configurar los archivos
manualmente.
NOTA
EJECUTE ESTE PASO SOLO SI NO LO HA HECHO CON EL COMANDO DE EXPRESS GENERATOR
/**
* Module dependencies.
*/
/**
* Get port from environment and store in Express.
*/
/**
* Create HTTP server.
*/
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
/**
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string' ?
'pipe ' + addr :
'port ' + addr.port;
debug('Listening on ' + bind);
}
Para hacer que el servidor funcione desde bin/www, abra y edite "package.json" y reemplazar el
valor de "start".
"scripts": {
"ng": "ng",
"start": "ng build && node ./bin/www",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
app.engine('html', require('ejs').renderFile);
app.set('views', path.join(__dirname, publicdir));
app.set('view engine', 'html');
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({'extended':'false'}));
app.use(express.static(path.join(__dirname, publicdir)));
app.use('/books', express.static(path.join(__dirname, publicdir)));
app.use('/book', book);
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
module.exports = app;
A continuación, crearemos las rutas de la API para el manejo de los datos de un libro.
md routes
copy NUL > routes/book.js
module.exports = router;
Abra y edite 'app.js' y agregue estas líneas al final antes del module.exports.
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
mongoose.connect('mongodb://localhost:27017/books-store', { useNewUrlParser:
true, promiseLibrary: require('bluebird') })
.then(() => console.log('connection succesful'))
.catch((err) => console.error(err));
Para ejecutar el servicio de MongoDB manualmente, vaya a otra terminal o línea de comandos..
A continuación, puede probar la conexión a MongoDB, vuelva al otro terminal y vuelva a ejecutar
la aplicación NodeJs y verá este mensaje en el terminal.
connection succesful
Esa es la razón por qué hemos añadido módulos 'bluebird' y registrarlo como biblioteca Promises
de Mongoose .
Agregar una carpeta de modelos en la raíz de la carpeta del proyecto para mantener archivos de
modelo de Mongoose.js.
md models
Crear nuevo archivo de Javascript que se usará para el modelo Mongoose.js. Vamos a crear un
modelo de colección de libros.
copy NUL > models/Book.js
type: Date,
default: Date.now
},
});
10.6 Crear rutas en Express para acceder a los datos del libro vía API
RESTful
Abra y edite nuevamente "routes/book.js" y reemplace todos los códigos con esto.
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Book = require('../models/Book.js');
/* SAVE BOOK */
router.post('/', function(req, res, next) {
Book.create(req.body, function (err, post) {
/* UPDATE BOOK */
router.put('/:id', function(req, res, next) {
Book.findByIdAndUpdate(req.params.id, req.body, function (err, post) {
if (err) return next(err);
res.json(post);
});
});
/* DELETE BOOK */
router.delete('/:id', function(req, res, next) {
Book.findByIdAndRemove(req.params.id, req.body, function (err, post) {
if (err) return next(err);
res.json(post);
});
});
module.exports = router;
Ejecute de nuevo el servidor Express abriendo en otra terminal o línea de comandos para probar
la API Restful con este comando.
curl -i -H "Accept: application/json" localhost:3000/book
Si recibe esta respuesta del comando entonces la API REST está lista para funcionar.
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 2
ETag: W/"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w"
Date: Fri, 10 Nov 2017 23:53:52 GMT
Connection: keep-alive
Ahora, vamos a rellenar la colección de libros con datos iniciales del API RESTful. Ejecute este
comando para rellenarlo.
El comando generará todos los archivos necesarios para construir el componente book que
también será agregado automáticamente a app.module.ts.
create src/app/book/book.component.css (0 bytes)
create src/app/book/book.component.html (23 bytes)
create src/app/book/book.component.spec.ts (614 bytes)
create src/app/book/book.component.ts (321 bytes)
update src/app/app.module.ts (390 bytes)
Ahora, vamos a hacer una petición a la API RESTful book utilizando este módulo Angular
'HttpClient'.
Añadir unas pocas líneas de códigos para obtener una lista de datos libro de RESTful API dentro
de la función 'ngOnInit', recuerden que el evento ngOnInit se ejecuta luego de que se crea el
componente y es el primer evento que se produce, aquí ya tenemos todos los elementos del
componente ya creados como por ejemplo el módulo http que inyectamos en el constructor.
ngOnInit() {
this.http.get('/book').subscribe(data => {
this.books = data;
});
}
Como podemos ver usamos el módulo http que inyectamos en el constructor para hacer una
llamada http request con el comando GET, luego debemos subscribirnos a un evento de callback
con la función subscribe que nos devuelve la respuesta del servidor, este ¨subscribe¨ es una
función asíncrona por lo tanto llegará en un momento posterior a la llamada mientras tanto el
código siguiente se sigue ejecutando.
Abra y edite 'src/app/book/book.component.html' y vuelva a colocar todas las etiquetas con esta
línea de etiquetas HTML.
<div class="container">
<h1>Book List</h1>
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let book of books">
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
<td>Show Detail</td>
</tr>
</tbody>
</table>
</div>
Para que se instale bootstrap directamente en nuestra aplicación Angular usaremos los
siguientes paquetes.
npm install --save bootstrap jquery popper.js font-awesome
luego para que se carguen los componente modificar la configuración en el archivo angular.json
para que se carguen al iniciar la aplicación.
"styles": [
"src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/font-awesome/css/font-awesome.css"
]
Otro método es modificar las etiquetas HTML de la página de inicio para incluir los estilos de
Bootstrap CSS.
NOTA
MODIFIQUE EL ARCHIVO INDEX.HTML SOLO SI NO HA INSTALADO BOOTSTRAP DESDE NPM Y
CONFIGURADO COMO SE INDICA EN EL PASO ANTERIOR
<head>
<meta charset="utf-8">
<title>MeanAngular5</title>
<base href="/">
<body>
<app-root></app-root>
<!-- Latest compiled and minified JavaScript -->
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
integrity="sha384-
Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous">
</script>
</body>
</html>
Para utilizar el componente book como página de inicio predeterminada, abra y edite
'src/app/app.module.ts' agregue la importación para el enrutamiento.
import { RouterModule, Routes } from '@angular/router';
Para activar las rutas en Angular 5, abrir y editar 'src/app/app.component.html' entonces Cambie
todo el código con esto.
<router-outlet></router-outlet>
Ahora, tenemos que probar nuestra aplicación con sólo la página de lista de libros.
Ejecutar la aplicación.
npm start
Igual a la sección anterior, escriba este comando para generar el nuevo componente.
ng g component book-detail
@Component({
selector: 'app-book-detail',
templateUrl: './book-detail.component.html',
styleUrls: ['./book-detail.component.css'],
encapsulation: ViewEncapsulation.None
})
export class BookDetailComponent implements OnInit {
book = {};
ngOnInit() {
this.getBookDetail(this.route.snapshot.params['id']);
}
getBookDetail(id) {
this.http.get('/book/'+id).subscribe(data => {
this.book = data;
});
}
Con este modulo podremos obtener por ejemplo los parámetros que fueron enviados y los datos
del body del request.
Dicho parámetro es enviado desde el ruteo o desde el [routerLink] que veremos en el siguiente
punto.
Por esto..
<tr *ngFor="let book of books">
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
<td><a class="btn btn-large btn-info fa fa-search-plus" [routerLink]="['/book-
detail', book._id]" routerLinkActive="router-link-active">Show details</a></td>
</tr>
Fijarse que el atributo [routerLink] apunta a la ruta del componente “book-detail” y le pasa como
parametro el ID el “book._id” del libro que se está listando con el ngFor.
El parámetro deberá ser declarado en el ruteo tal como se muestra en el punto 10.8.1.4 Ruteo
para el componente de Detalle.
Para crear un componente para agregar un nuevo libro, escriba este comando como de
costumbre.
ng g component book-create
@Component({
selector: 'app-book-create',
templateUrl: './book-create.component.html',
styleUrls: ['./book-create.component.css'],
encapsulation: ViewEncapsulation.None
})
export class BookCreateComponent implements OnInit {
book = {};
ngOnInit() {
}
saveBook() {
this.http.post('/book', this.book)
.subscribe(res => {
let id = res['_id'];
this.router.navigate(['/book-details', id]);
}, (err) => {
console.log(err);
}
);
}
Aquí podemos ver como hemos inyectado un nuevo módulo el Router referenciado por el objeto
this.router, así como vimos que HttpClient nos permite enviar peticiones http hacia afuera de
nuestra aplicación Angular, el módulo Router nos permite hacer lo mismo pero para navegar
dentro de las rutas de nuestra aplicación Angular, es decir ruteos internos a otros componentes
Angular.
Vemos entonces como una ves que realizamos el post del nuevo libro y nos subscribimos al
callback de la respuesta, ésta nos llega con el dato del nuevo _id del libro que se creó y entonces
podemos navegar al componente de BookDetailsComponent usando
this.router.navigate(['/book-details', id]);
Entonces navigate cumple la misma función que el [routerLink] que declaramos en el html solo
que éste lo hacemos desde el código del componente.
Además, el módulo Router proporciona datos para luego ser capturados por el módulo
ActivatedRoute y genera tambien el historial de navegación del browser lo que permite que
podamos navegar hacia atrás usando el botón Back del mismo browser.
</div>
<div class="form-group">
<label for="name">Title</label>
<input type="text" class="form-control"
[(ngModel)]="book.title" name="title" required>
</div>
<div class="form-group">
<label for="name">Author</label>
<input type="text" class="form-control"
[(ngModel)]="book.author" name="author" required>
</div>
<div class="form-group">
<label for="name">Published Year</label>
<input type="number" class="form-control"
[(ngModel)]="book.published_year" name="published_year"
required>
</div>
<div class="form-group">
<label for="name">Publisher</label>
<input type="text" class="form-control"
[(ngModel)]="book.publisher" name="publisher" required>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success"
[disabled]="!bookForm.form.valid">Save</button>
</div>
</form>
</div>
</div>
</div>
Vemos aquí un claro ejemplo de bindeo doble tanto desde el codigo hacia el browser como del
browser hacia el código con el uso de la directiva [(ngModel)], es decir que si modificamos el
valor del objeto book desde el código del componente automáticamente se verá reflejado en la
página web y si desde la página web cambiamos el valor del book también se verá reflejado
automáticamente desde el código.
También hay que tener en cuenta la directiva (ngSubmit) del form y el nombre que se le aplica al
formulario con el atributo #[nombreForm]=”ngForm” de esta manera podemos referenciar a
estados del formulario directamente con el objeto [nombreForm].form en este caso llamado
bookForm.
Como de costumbre, se generan componentes para editar libro. Escriba este comando para
hacerlo.
ng g component book-edit
@Component({
selector: 'app-book-edit',
templateUrl: './book-edit.component.html',
styleUrls: ['./book-edit.component.css'],
encapsulation: ViewEncapsulation.None
})
export class BookEditComponent implements OnInit {
book = {};
ngOnInit() {
this.getBook(this.route.snapshot.params['id']);
}
getBook(id) {
this.http.get('/book/'+id).subscribe(data => {
this.book = data;
});
}
updateBook(id, data) {
this.http.put('/book/'+id, data)
Autor: Ing. Mario Di Giorgio 49
UNIVERSIDAD TECNOLÓGICA NACIONAL Diplomatura en Desarrollo WEB
Facultad Regional Córdoba Módulo: API Rest con NodeJS y SPA con Angular 5
.subscribe(res => {
let id = res['_id'];
this.router.navigate(['/book-details', id]);
}, (err) => {
console.log(err);
}
);
}
<label for="name">Publisher</label>
<input type="text" class="form-control"
[(ngModel)]="book.publisher" name="publisher" required>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success"
[disabled]="!bookForm.form.valid">Update</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-md-12">
<a [routerLink]="['/book-edit', book._id]" class="btn btn-
success">EDIT</a>
<button class="btn btn-danger" type="button"
(click)="deleteBook(book._id)">DELETE</button>
</div>
</div>
Y aquí estamos.
Además, agregar la variable bookForm para guardar los datos de validación antes del
constructor.
bookForm: FormGroup;
Con esto validaremos los campos el validador Validators.required especifica que el campo debe
tener un dato cargado de lo contrario el formulario quedará marcado como inválido.
</div>
<div class="text-danger"
*ngIf="bookForm.controls['published_year'].hasError('max')">
Minimum required number is 2100.
</div>
</div>
</div>
<div class="form-group"
[ngClass]="{ 'has-error':
bookForm.controls['publisher'].errors && (bookForm.controls['publisher'].touched
|| bookForm.controls['publisher'].dirty)}">
<label for="name">Publisher</label>
<input type="text" class="form-control"
[(ngModel)]="book.publisher" name="publisher"
formControlName="publisher" required>
<div *ngIf="bookForm.controls['publisher'].errors &&
bookForm.controls['publisher'].touched">
<div class="text-danger"
*ngIf="bookForm.controls['publisher'].hasError('required')">
Enter the Publisher name.
</div>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success"
[disabled]="!bookForm.valid">Save</button>
</div>
</form>
</div>
</div>
</div>
Navegue al sitio http://localhost:3000 y verifique que se ejecute el sitio con los formularios
de lista de libros, crear, editar y detalles.