Paso 1: Listar los libros en una Galería, autores y las editoriales en listas simples.

URL: https://github.com/Uniandes-isis2603/frontstepbystep.git
Release: git checkout -f paso-1

Implementación

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 tiene un estado inicial que despliega en una barra de menús con las opciones Books, Authors y Editorials.

Cuando el usuario selecciona Books se llega a un estado llamado booksList donde se despliega la lista de libros.

Cuando el usuario selecciona Authors se llega a un estado llamado authorsList donde se despliega la lista de autores.

Cuando el usuario selecciona Editorials se llega a un estado llamado editorialsList donde se despliega la lista de editoriales.

La explicación de la implementación del paso-1 la hemos dividido en:

Creación de la barra de menú

Para entender cómo se construye la barra de menús debemos revisar:

  • El index.html dónde se definen las dependencias y se construye el archivo html principal de la aplicación.
  • El app.js dónde se define el módulo principal que incluye los demás módulos.

Index.html

El index.html es el único archivo html de toda la aplicación que tiene:

  • El tag inicial <html> y el tag de terminación </html>,
  • El tag <head> y el tag <body>.
Todos los demás templates de la aplicación son fragmentos de archivos html que se incrustarán en zonas especificas del index.html.

El tag head

En el tag head definimos las dependencias externas de librerías que vamos a utilizar en la aplicación (bootstrap, angular, angular-ui-router y angular-bootstrap).

También definimos las dependencias internas a los archivos que contienen código javascript de nuestra aplicación: el archivo donde se define el módulo principal app.js y los archivos javascript, que veremos más adelante, de los módulos books, authors y editorials.

El fragmento de código donde se declaran estas dependencias es:

<!DOCTYPE html> <html ng-app="mainApp"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> <title>books</title> <link rel="shortcut icon" href=""> <!-- Dependencias Externas --> <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" /> <script src="bower_components/angular/angular.js"></script> <script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script> <script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script> <!-- Books --> <script src="src/modules/books/books.mod.js" type="text/javascript"></script> <script src="src/modules/books/books.ctrl.js" type="text/javascript"></script> <!-- Authors --> <script src="src/modules/authors/authors.mod.js" type="text/javascript"></script> <script src="src/modules/authors/authors.ctrl.js" type="text/javascript"></script> <!-- Editorials --> <script src="src/modules/editorials/editorials.mod.js" type="text/javascript"></script> <script src="src/modules/editorials/editorials.ctrl.js" type="text/javascript"></script> <script src="src/app.js" type="text/javascript"></script> <script src="src/http-interceptor.factory.js" type="text/javascript"></script> </head> <body> ... </body> </html>

El tag body

En el tag body del index va la información que verá el usuario. En este paso solo queremos construir una barra de menús con las tres opciones y definir la zona donde se desplegará más adelante la vista de los libros, autores y editoriales.

Elementos
Para que la aplicación sea responsive utilizamos de bootstrap el estilo 'container-fluid' que permite que cuando la ventana cambie de tamaño lo que hay dentro se adapte.
Luego, siguiendo el sistema de grilla (Bootstrap Grid System) de bootstrap definimos una fila <div class="row"> que contiene una única columna col-lg-12. "lg": Significa large devices.
La clase que estamos utilizando para construir la barra de navegación es nav nav-tabs (bootstrap nav components).
En el elemento de enlace: <a ui-sref="booksList">Books</a> en vez de utilizar el atributo href de html para hacer el enlace, estamos utilizando el atributo ui-sref de la librería ui.router para definir que: cuando el usuario seleccione Books la aplicación irá al estado booksList.
Después de la fila donde se define la barra de menús, está el elemento <div ui-view="mainView"></div> que utiliza el atributo ui-view para indicar que las vistas definidas en templates html en los módulos de la aplicación, remplazarán ese div.
<html> <head> ... </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-lg-12"> <ul class="nav nav-tabs"> <li><a ui-sref="booksList">Books</a></li> <li><a ui-sref="authorsList">Authors</a></li> <li><a ui-sref="editorialsList">Editorials</a></li> </ul> </div> </div> <div ui-view="mainView"> </div> </div> </body> </html>

Módulo principal

El siguiente código del archivo app.js muestra la creación del módulo principal de la aplicación. Este módulo tiene el nombre de la aplicación, el mismo que se utiliza en el index.html, y las dependencias externas (en este caso ui.router y ui.bootstrap) y las internas (en este caso bookModule, authorModule, editorialModule).

function (ng) { var app = angular.module('mainApp', [ // External dependencies 'ui.router', 'ui.bootstrap', // Internal modules dependencies 'bookModule', 'authorModule', 'editorialModule' ]); // Resuelve problemas de las promesas app.config(['$qProvider', function ($qProvider) { $qProvider.errorOnUnhandledRejections(false); }]); })(window.angular);

Creación del módulo authors y el estado authorsList

Definición del módulo

El módulo authorModule está definido en el archivo authors/authors.mod.js.

Igual que el módulo authorModule:

  1. Se define el módulo y se declara la dependencia ui-router.
  2. Se invoca la función config sobre el módulo creado. Esta función recibe un arreglo donde los dos primeros argumentos son dos variables de angular-ui-router: $stateProvider y $urlRouterProvider; el tercer argumento es una función que recibe dos argumentos. por convención el nombre es el mismo para entender que se está haciendo una asociación entre las variables del arreglo y los parámetros de la función.
  3. La función:
    • Declara una variable basePath para indicar la ruta donde están los archivos del módulo de autores.
    • Indica, utilizando la variable $urlRouterProvider que el estado por defecto será authorsList
    • Crea el estado: authorsList en la variable de angular $stateProvider.
function (ng) { // Definición del módulo var mod = ng.module("authorModule", ['ui.router']); // Configuración de los estados del módulo mod.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) { // En basePath se encuentran los templates y controladores de módulo var basePath = 'src/modules/authors/'; // Mostrar la lista de autores será el estado por defecto del módulo $urlRouterProvider.otherwise("/authorsList"); // Definición del estado 'authorsList' donde se listan los autores $stateProvider.state('authorsList', { // Url que aparecerá en el browser url: '/authors/list', views: { 'mainView': { templateUrl: basePath + 'authors.list.html', controller: 'authorCtrl', controllerAs: 'ctrl' } } }); } ]); })(window.angular);

Definición del estado authorsList

En el estado se especifica,

  1. La ruta asociada con el estado: url: '/books/list'. Cuando la aplicación esté en éste estado, en la barra de navegación del brower aparecerá el valor de la url definido en el estado.
  2. Las vistas que tiene el estado.
  3. Para cada una de las vistas, en este caso sólo hay la vista "mainView", se define:
    • El template que se desplegará en la vista. Este template remplazará el <div ui-view="mainView"></div> que definimos en el index.html.
    • El controlador que actúa con la vista y comparte la variable de angular $scope. El controlador de los autores se llama authorCtrl y le definimos un sinónimo ctlr. (controllerAs: 'ctrl')

A continuación explicamos la definición del template y del controlador para el estado authorsList.

Template para el estado authorsList

El template que listan los autores construye una simple tabla para mostrar cada elemento. El siguiente código corresponde al template: authors.list.html.

  1. Usamos el anidamiento: container, col y luego la tabla que contendrá la lista de los autores. Utilizamos el estilo table table-hoover.
  2. Enseguida están los encabezados de la tabla
  3. En el cuerpo de la tabla iteramos sobre la colección authorsRecords para mostrar cada uno de los elementos.
<div class="container-fluid"> <div class="col-md-4"> <table class="table table-hover"> <thead> <tr> <th>Name</th> <th>Birth Date</th> </tr> </thead> <tbody> <tr ng-repeat="author in authorsRecords"> <td id="{{$index}}-name"> <a ui-sref="authorDetail({authorId: author.id})">{{author.name}}</a></td> <td id="{{$index}}-descrption">{{author.birthDate}}</td> </tr> </tbody> </table> </div> </div>

El controlador del estado authorsList

En el controlador está definido en el archivo authors.ctlr.js.

  1. En la línea 2, obtenemos módulo llamado authorModule que ya fue creado en el archivo authors.mod.js. Para indicar que no estamos creando un módulo sino utilizando uno ya creado, la función ng.module sólo recibe de argumento el nombre del módulo. En caso de creación debe recibir un arreglo (así sea vacío) de las dependencias del módulo.
  2. En la línea 3 configuramos una constante que servirá más adelante para construir las urls de los llamados http.
  3. En la línea 4 definimos el controlador asociado con el módulo.
    • El controlador se llama authorCtrl
    • Es una función que recibe tres argumentos: $scope, $http, authorsContext.
    • $http es una variable de angular que permite hacer los llamados de los servicios rest.
(function (ng) { var mod = ng.module("authorModule"); mod.constant("authorsContext", "api/authors"); mod.controller('authorCtrl', ['$scope', '$http', 'authorsContext', function ($scope, $http, authorsContext) { $http.get('data/authors.json').then(function (response) { $scope.authorsRecords = response.data; }); } ]); } )(windows.angular);
Para propósitos de este paso, no nos conectaremos directamente con el back-end sino que tenemos una carpeta data donde guardaremos datos de prueba en forma de colecciones json.
$http.get('data/authors.json').then(function (response) { $scope.authorsRecords = response.data; })
  1. La url que estamos enviando a $http.get corresponde a la dirección de una variable local al proyecto que contiene una colección de autores. 'data/authors.json'.
  2. $http.get es una promesa (ver explicación de promesas aquí) en este ejemplo, definimos la función de lo que debe pasar " si todo sale bien". En este caso .then. esta función lo que hace es inicializar en el $scope del controlador la lista de autores que recuperó con $http.get('data/authors.json'). Esta variable puede entonces ser utilizada desde el template del estado.
  3. Si hubiera un error en el retorno de la promesa $http.get('data/authors.json') se ejecutará la función responseErrorque se encuentra definida en un interceptor global que atrapa todos los errores de las promesas. Este interceptor está en el archivo http-interceptor.factory.js.

Asociación del nuevo módulo con la aplicación

Cuando se crea un nuevo módulo en la aplicación, para que éste sea cargado correctamente, se debe incluir en las dependencias del módulo principal y los archivos javascript que contenga deben definirse en el index.html:

  1. Definir en las dependencias de app.js el nuevo módulo
var app = angular.module('mainApp', [ // External dependencies 'ui.router', // Internal modules dependencies 'bookModule', 'authorModule', 'editorialModule' ]);
  1. Incluir el archivo en el index.html
<!DOCTYPE html> <html ng-app="mainApp"> <head> ... <!-- Authors --> <script src="src/modules/authors/authors.mod.js" type="text/javascript"></script> <script src="src/modules/authors/authors.ctrl.js" type="text/javascript"></script> ... </head> ...

Creación del módulo editorials y el estado editorialsList

Esta implementación es igual al de authors solo que de manera correspondiente se hace referencia a los elementos del módulo editorialModule.

Creación del módulo books y el estado booksList

El módulo bookModule está definido en el archivo books/books.mod.js.

Igual que el módulo authorModule:

  1. Se define el módulo y se declara la dependencia ui-router.
  2. Se invoca la función config sobre el módulo creado. Esta función recibe un arreglo donde los dos primeros argumentos son dos variables de angular-ui-router: $stateProvider y $urlRouterProvider; el tercer argumento es una función que recibe dos argumentos. por convención el nombre es el mismo para entender que se está haciendo una asociación entre las variables del arreglo y los parámetros de la función.
  3. La función:
    • Declara una variable basePath para indicar la ruta donde están los archivos del módulo de libros.
    • Indica, utilizando la variable $urlRouterProvider que el estado por defecto será booksList
    • Crea el estado: booksList en la variable de angular $stateProvider.
// Definición del módulo var mod = ng.module("bookModule", ['ui.router']); // Configuración de los estados del módulo mod.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) { // En basePath se encuentran los templates y controladores de módulo var basePath = 'src/modules/books/'; // Mostrar la lista de libros será el estado por defecto del módulo $urlRouterProvider.otherwise("/booksList"); // Definición del estado 'booksList' donde se listan los libros $stateProvider.state('booksList', { // Url que aparecerá en el browser url: '/books/list', views: { 'mainView': { templateUrl: basePath + 'books.list.html', controller: 'bookCtrl', controllerAs: 'ctrl' } } }); } ]);

En el estado se especifica:

  • La ruta asociada con el estado: url: '/books/list'
  • Las vistas que tiene el estado. n este caso solo "mainView".

Por cada vista se define:

  • El template que se desplegará en una vista de la aplicación. En este ejemplo básico el template remplazará el <div ui-view></div> que definimos en el index.html.
  • El controlador que actúa con la vista y comparte la variable de angular $scope.
  • El controlador de los libros, llamado bookCtrl

El controlador del estado booksList

Para propósitos de este paso, no nos conectaremos directamente con el back-end sino que tenemos una carpeta data donde guardaremos datos de prueba en forma de colecciones json.

En el controlador se define:

var mod = ng.module("bookModule"); mod.constant("booksContext", "api/books"); mod.controller('bookCtrl', ['$scope', '$http', 'booksContext', function ($scope, $http, booksContext) { $http.get('data/books.json').then(function (response) { $scope.booksRecords = response.data; }); } ]);

Al hacer el llamado con $http.get estamos haciendo una petición de tipo GET que nos retorna como respuesta en el arreglo data los libros que estan almacenados en la coleccion books.json y se hace la asignación: $scope.booksRecords = response.data;.

booksRecords es la variable que utiliza el template 'books.list.html' para desplegar la lista de libros.

Definición del template del estado booksList

Para definir la vista con bootstrap de nuevo usamos el anidamiento: container, row, col.

En este caso estamos indicando que máximo podrán verse 6 imágenes en una fila "col-sm-2 col-md-2" (sm : small devices, md: medium devices)

En el template se itera sobre booksRecords línea 3:

  1. ng-repeat="book in booksRecords": la variable booksRecords es inicializada en el controlador y quedó en el scope de este módulo.
  2. la variable book es el iterador que recorre la lista.

Para mostrar la imagen de cada libro utilizamos:

  1. El estilo thumbnail línea 4
  2. El tag img y la clase img-responsive para que cuando se agrande o achique la pantalla las imágenes se auto escalen
  3. Se obtiene de cada libro su imagen book.image que va en el valor del atributo ng-src del tag img
  4. Se obtiene su nombre book.name para darle valor al atributo alt que significa que si la imagen no puede ser desplegada, alternativamente se despliega el texto.
  5. Finalmente en la línea 6 se escribe, debajo de la imagen del libro (caption) el nombre.
<div class="container-fluid"> <div class="row"> <div class="col-sm-2 col-md-2" ng-repeat="book in booksRecords"> <div class="thumbnail"> <img class="img-responsive" ng-src='{{book.image}}' alt="{{book.name}}" /> <div class="caption text-center">{{book.name}}</div> </div> </div> </div> </div>

results matching ""

    No results matching ""