frontbookpaso1 - keblato/TutorialesTalleres-Angular GitHub Wiki
Vamos a ir desarrollando el ejemplo de Book paso por paso introduciendo conceptos nuevos cada vez. Para ejecutar y ver este primer paso, ud debe clonar el proyecto, hacer checkout del paso y luego ejecutarlo como se explicó en ejecutar.
Paso-1 | Listar los libros en una Galería, autores y las editoriales en listas simples. |
---|---|
URL: | https://github.com/Uniandes-isis2603/front-pregrado201820.git |
Release: | git checkout -f paso-1a |
Los objetivos de este primer paso son mostrar una aplicación conformada por varios módulos, componentes y servicios, utilizar el módulo router
de Angular para implementar la navegación en la aplicación e implementar pruebas.
En el paso-1
implementamos el requerimiento de desplegar la lista de libros en una galería, la lista de autores en una lista simple y la lista de editoriales en una lista simple. La aplicación es responsive.
Al iniciar la aplicación se despliega una barra de menús con las opciones Books
, Authors
y Editorials
.
Cuando el usuario selecciona Books
se despliegan los libros en formato galería donde cada libro es presentado por la imagen de su portada.
![]() |
---|
Figura 1: lista de libros |
Cuando el usuario selecciona Authors
se despliega la lista de autores.
![]() |
---|
Figura 2: lista de autores |
Cuando el usuario selecciona Editorials
se despliega la lista de editoriales.
![]() |
---|
Figura 3: lista de editoriales |
Ir al inicio |
---|
Nuestra aplicación tiene tres módulos funcionales. En nuestra decisión de diseño, tenemos un módulo por cada concepto diferente de la aplicación (cada recurso): BookModule
, AuthorModule
y EditorialModule
.
Cada uno de los módulos definirá un componente por cada interacción distinta que el usuario necesite realizar (Podría pensarse que hay un componente por caso de uso. Si el caso de uso es complejo, es posible que más de un componente intervenga en su implementación).
El siguiente diagrama presenta los módulos de la aplicación. El principal AppModule
que importa los demás: Por un lado los funcionales BookModule
, AuthorModule
y EditorialModule
, luego está el módulo AppRoutingModule
que se ocupa de la navegación en la aplicación (explicaremos más adelante) y los módulos de Angular que se requieren para este paso.
También es importante ver la vista de desarrollo de la aplicación, es decir, la organización en carpetas y archivos los elementos del proyecto. La siguiente imagen la muestra. La carpeta src/app
contiene una carpeta por cada uno de los módulos de la aplicación:
Así como vimos en el ejemplo de la lista simple de editoriales, cada módulo funcional define:
- Sus componentes; en este caso solo está el componente de listar (
BookListComponent
,AuthorListComponent
yEditorialListComponent
) - Sus servicios: en este caso sólo tenemos un servicio que se ocupa de traer los elementos que se van a mostrar (
BookService
,AuthorService
yEditorialService
). - El tipo (
interface
Typescript) del modelo que representa el recurso (Book
,Author
yEditorial
).
Aclaración: este diagrama no incluye los elementos relacionados con las pruebas de los componentes. Este tema se abordará más adelante.
En el ejemplo LIstar Editoriales está explicado cómo funciona el servicio que trae la colección de editoriales y cómos e hace el despliegue en la vista (template html).
Aquí vamos a explicar los elementos diferentes. Uno de ellos es la galería que despliega los libros.
Ir al inicio |
---|
En el caso del componente BookListComponent
, en el componente se declara el template que se encuentra en el archivo book-list.component.html
. La diferencia de esta vista con respecto a la lista de editoriales y a la lista de autores es que se despliegan imágenes de las carátulas de los libros. Para esto utilizamos elementos de HTML 5 y de bootstrap para desplegar las imágenes.
<div class="container-fluid">
<div class="row">
<div class="col" *ngFor = "let book of books">
<figure class="figure">
<img class="img-thumbnail img-fluid" src='{{book.image}}' alt="{{book.name}}" />
<figcaption class="figure-caption text-center">{{book.name}}</figcaption>
</figure>
</div>
</div>
</div>
El tag figure
permite asociar la imagen img
con el título debajo de ella.
Ir al inicio |
---|
En este ejemplo tenemos en la parte superior de la pantalla la barra de menús. La intención es permitir al usuario navegar sobre los componentes de listar los elementos. El comportamiento que queremos es que cuando el usuario haga clic en Books
aparezca la galería de libros, clic en Editorials
la lista de editoriales y clic en Authors
la lista de los autores.
El código html que construye la barra de menús está definido dentro del archivo app.component.html
que corresponde al template definido en el componente AppComponent
. El código html de la barra de menús es muy simple:
<ul class="nav nav-tabs">
<li class="nav-item">
<a id="booksTag" class="nav-link" routerLink="/books/list">Books</a>
</li>
<li class="nav-item">
<a id="authorsTag" class="nav-link" routerLink="/authors/list">Authors</a>
</li>
<li class="nav-item">
<a id="editorialTag" class="nav-link" routerLink="/editorials/list">Editorials</a>
</li>
</ul>
<router-outlet></router-outlet>
Note que en los elementos de enlace, es decir los tag a
, en vez de tener el atributo href
tiene un atributo de Angular llamado routerLink
. Hay dos cosas que necesitamos entender:
- Qué es y dónde se define el valor del atributo
- Dónde se muestra lo que se va a desplegar. Para este punto, la respuesta es que se despliega remplazando la línea
<router-outlet></router-outlet>
que se encuentra al final del archivo en el template. En pasos posteriores veremos otras formas de definir dónde desplegar.
AppRoutingModule
es el módulo donde definimos los enrutamientos o la navegación de la aplicación y funciona gracias a los paquetes Routes
y RouterModule
de la librería @angular/router
.
Los valores que puede tomar el atributo routerLink
corresponden a rutas de navegación que debemos definir. EN este ejemplo, las hemos definido en el módulo AppRoutingModule
. Como se puede observar en el código, definimos un arreglo de rutas de navegación, llamado routes
y cuyo tipo es Routes
donde cada ruta define un path
y un componente que es el que contiene el template que se mostrará cuando el routerLink
tenga por valor el path correspondiente.
Note que podemos hacer una jerarquía de paths. Significa que si queremos que se despliegue el template definido en el componente BookListComponent
, el path será: books/list
.
Para efectos de organización, el router de esta aplicación define tres paths principales (books
, authors
y editorials
) que tienen children
, es decir rutas que continúan a partir de esa raíz (ej. books/list).
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule, Routes} from '@angular/router';
import {BookListComponent} from '../book/book-list/book-list.component';
import {AuthorListComponent} from '../author/author-list/author-list.component';
import {EditorialListComponent} from '../editorial/editorial-list/editorial-list.component';
const routes: Routes = [
{
path: 'books',
children: [
{
path: 'list',
component: BookListComponent
}
]
},
{
path: 'authors',
children: [
{
path: 'list',
component: AuthorListComponent
}
]
},
{
path: 'editorials',
children: [
{
path: 'list',
component: EditorialListComponent
}
]
}
];
@NgModule({
imports: [
CommonModule,
RouterModule.forRoot(routes)
],
exports: [RouterModule],
declarations: []
})
export class AppRoutingModule {
}
Ver Video |
---|
Ir al inicio |
---|
Angular cuenta con paquetes de pruebas que se encuentran en la librería @angular/core/testing
. Las pruebas que se pueden realizar con esos paquetes varían desde pruebas unitarias, hasta pruebas más interesantes de interfaz.
La arquitectura de estas pruebas se muestra en un diagrama simplificado a continuación (no incluye las pruebas del componente EditorialListComponent
):
![]() |
---|
Figura 5: diagrama simplificado de pruebas |
Al crear un nuevo componente por medio del CLI de Angular, este le crea a cada componente su spec de pruebas correspondiente (ej. book-list.component.spec.ts
).
En estos archivos .spec.ts
se definen las pruebas para el componente que le corresponde.
La estructura básica del archivo comienza con los imports de los elementos necesarios para las pruebas. Todas estas están escritas dentro de un "contenedor" describe
.
Antes de comenzar a describir las pruebas, es necesario definir ciertas acciones a realizar antes de cada prueba. Esto se realiza en los métodos beforeEach
.
A continuación se muestra la implementación del describe
y el beforeEach
en book-list.component.spec.ts
:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AppRoutingModule } from '../../routing-module/app-routing.module';
import { APP_BASE_HREF } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { AppModule } from '../../app.module';
import { BookListComponent } from './book-list.component';
import {BookService} from '../book.service';
import { Book } from '../book';
describe('BookListComponent', () => {
let component: BookListComponent;
let fixture: ComponentFixture<BookListComponent>;
const books: Book[] = require('../../../assets/books.json');
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ AppRoutingModule, HttpClientModule, AppModule ],
declarations: [ ],
providers: [{provide: APP_BASE_HREF, useValue: ''}, BookService ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BookListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
El primer beforeEach
se encarga de hacer las declaraciones necesarias para que las pruebas compilen.
El segundo define que en este caso la variable fixture
crea el componente. La instancia de ese componente creado se guarda en component
y es sobre esa variable que luego se hacen las pruebas.
En este ejemplo se definieron 3 pruebas para cada componente.
En el caso de la lista de libros:
it('should create', () => {
expect(component).toBeTruthy();
});
it('should have a list of books', () => {
component.books = books;
expect(component.books.length).toEqual(books.length);
});
it('a book should be a book (first and last)', () => {
component.books = books;
//revisar todos los libros
expect(component.books[0].name).toEqual(books[0].name);
expect(component.books[books.length - 1].name).toEqual(books[books.length - 1].name);
});
});
Cada prueba se escribe dentro de un it
, el String que corresponde al primer parámetro es el título de la prueba. El segundo es la función que evalúa la prueba.
La primera prueba corresponde a la básica que verifica que el componente se puede crear de manera correcta.
La segunda verifica que la lista de libros del componente sea tal como se espera. La lista es comparada con el json local (en la carpeta assets
) que contiene la lista completa de libros en tamaño, y en la verificación de algunos de los objetos en la tercera prueba.
En este primer paso 1a
, la lista es el mismo archivo así que la prueba es obvia, pero esto cambiará a partir del paso 1b
que conecta el front con el back.
Las pruebas se ejecutan con el comando/script ng test
.
Los resultados de estas se despliegan en localhost:9876
.
Pensando en la integración continua, el proyecto se configuró para que utilice PhantomJS
(un navegador headless
) que muestra el resultado de las pruebas en consola. De esta manera, las pruebas se pueden ejecutar por ejemplo en Jenkins.
![]() |
---|
Figura 6: ejemplo de la ejecución de las pruebas utilizando PhantomJs |
Ir al inicio |
---|