Ejemplo Ciudades
Vamos a desarrollar una aplicación front-end angular para acceder a los servicios REST de ciudades que desarrollamos en la sección: Ejemplo Implementación Rest.
La implementación de estos servicios está en el repo en la rama master . |
Este ejemplo se trata de una aplicación que tiene un único módulo llamado cities
. Debemos hacer una interfaz que permita ver el listado de ciudades, crear una nueva, editar una y borrar una.
El diseño
Los estados que puede tener esta aplicación son:
La siguiente figura muestra varios elementos de la solución:
- La aplicación principal contenida en el módulo
mainApp
(dentro del archivoapp.js
), depende del módulo externo (ui-router
) y del módulo internoCityModule
. - El módulo
CityModule
(dentro del archivocities.mod.js
) define un controlador llamadocitiesCtrl
y configura los estados del módulo. - El controlador
citiesCtrl
(dentro del archivocities.ctrl.js
) se va a ocupar de las invocaciones al API REST de City. - La configuración de los estados se encuentra en el mismo archivo
cities.mod.js
y define los estados de la figura arborescente anterior: - Cada estado indica el template (html), el controlador (que en este ejemplo es el mismo para todos) y la url de la navegación.
- En el caso del estado
citiesList
, el template está definido en el archivocities.list.html
. - En el caso de los estados
cityCreate
ycityEdit
, el template (que es el mismo para ambos) está definido en el archivocities.create.html
.
- En el caso del estado
El proyecto del ejemplo se puede clonar de repo y utilizar la rama
master
.
El código
La estructura del proyecto es la que se muestra en la figura anterior. En total hay 7 archivos. Tres html:
- El
index.html
- La plantilla para desplegar la lista
cities.list.html
- La plantilla para crear/editar una ciudad
cities.create.html
.
Cuatro archivos javascript:
- El archivo principal
app.js
- El archivo que define el módulo de ciudades
cities.mod.js
- El archivo que define el controlador de las plantillas de ciudades
cities.ctrl.js
. - El archivo
http-interceptor.factory.js
que define el maneo de error cuando algo sale mal en los llamadoshttp
.
Importante |
---|
Tanto las convenciones de organización del proyecto como de nombramiento de los archivos se convierten en muy importantes cuando hay varias personas trabajando sobre el mismo proyecto y cuando éste empieza a crecer. Estas convenciones facilitan la repartición del trabajo evitando conflictos y también la localización y modificación de las partes de la aplicación. |
La siguiente figura muestra la navegación en la aplicación. La primera vez aparece un menú desplegable con dos opciones. Una lleva al estado de crear una nueva ciudad y la otra al estado de mostrar las ciudades actuales. Desde el estado CitiesList
se navega hacia el estado de edición. Asociados con Cancel
o Save
se regresa al estado de listar las ciudades.
Arranque
Ahora vamos a explicar dónde y cómo en el código se define la navegación.
- Es en el
index.html
donde se definen dos cosas:- Los enlaces al estado
cityCreate
ycitiesList
y - La vista principal o
mainView
que será utilizada por esos estados.
- Los enlaces al estado
El enlace a los dos estados se encuentra en los tags "a", utilizando la directiva ui-sref
:
<a ui-sref="cityCreate">Create</a>
<a ui-sref="citiesList">List</a>
El valor que toma ui-sref
es el nombre del estado al que se debe llegar.
La definición de estos dos estados se encuentra dentro del archivo cities.mod.js
donde se define el módulo CitiesModule
.
Estado crear una ciudad
En este estado se despliega una forma básica para crear una ciudad donde sólo se ingresa el nombre. Con respecto a la navegación, es decir las maneras de salir de la forma, la forma tiene dos botones: Cancel y Save.
En el caso de Cancel
, la decisión que se tomó es regresar al estado de listar ciudades. Esto se puede ver en el código:
<button type="button" ui-sref='citiesList'>Cancel</button>
En el caso de Save
, tienen que suceder dos cosas: primero, efectivamente salvar el registro invocando el servicio Rest (POST) para este propósito y segundo, ir al estado de listar ciudades en donde debe aparecer en la lista la nueva ciudad.
La responsabilidad de salvar la nueva ciudad es del controlador de la forma. Podemos ver el siguiente código, donde se está invocando el método save Record
que recibe como argumento el registro actual, el que se acaba de crear y está definido en el controlador de este estado:
<ng-submit="form.$valid &&(ctrl.saveRecord(currentRecord.id)">
Más adelante haremos una explicación completa del controlador; por ahora queremos ver cómo sucede la navegación cuando al salvar la forma y todo sale bien se va al estado de listar ciudades. La instrucción que nos permite cambiar de estado:
$state.go('citiesList');
Estado listar ciudades
Lo que sucede cuando se llega al estado citiesList
está configurado en la definición de los estados que se encuentra dentro del archivo cities.mod.js
y que explicaremos en un momento. La configuración para este estado dice que:
En la vista mainView
desplegar el template contenido en cities.list.html
.
Asociado con el botón Edit
encontramos la referencia al nuevo estado:
ui-sref="cityEdit({cityId: record.id})"
En este caso el estado recibe un argumento de nombre cityId
y de valor el identificador de id
de la ciudad que queremos editar.
Estado editar una ciudad
La diferencia entre este estado y el estado crear una ciudad está en que el estado recibe un identificador válido de registro que le permite saber que la ciudad en cuestión ya existe y que se va a editar y no a crear.
La Configuración de los Estados
La configuración de los estados consiste de un nombre y un objeto de configuración. ver la explicación en Los Estados.
En este ejemplo tenemos 3 estados: citiesList
, cityCreate
y cityEdit
. Note que todos configuran el mismo controlador: citiesCtrl
. El estado citiesList
configura el template para desplegar la lista de las ciudades. Los otros dos estados el template para crear/editar una ciudad. Las url del estado cityEdit
incluye el id
de la ciudad que se va a editar. Esta información se puede acceder por el controlador a través de una variable Angular que se llama $stateParams
. Para que ese valor quede dentro de esa variable, hacemos la asignación:
Param: {cityId: null }
El Controlador
Cuando se entra a un estado se ejecuta su controlador. En este ejemplo, por simplicidad, solo tenemos un controlador para todos los estados. En caso de que el código que se va a manejar en un estado sea complicado o muy extenso, vale la pena separar el controlador para que sea más fácil de manejar el código.
Este controlador tiene varias responsabilidades: cargar todas las ciudades, salvar una nueva ciudad, salvar la modificación a una ciudad que ya existía.
Cargar todas las ciudades
Get /cities
Cuando el controlador se ejecuta, carga las ciudades utilizando el servicio $http
. El código de este llamado está entre las líneas 7 y 10 del listado. Como ya explicamos, la función $http.get
retorna un promise
. Si el llamado fue exitoso, la respuesta del GET se retorna en una variable response
. Esta variable tiene varias informaciones relacionadas con el llamado pero por ahora nos interesa saber que en response.data
se encuentra el objeto json con los datos, en este caso, una colección de objetos city
donde cada uno tiene un id
y un name
.
Si el llamado no fue exitoso se ejecutará el código de la función definida en responseError
en un interceptor global de las promesas que está definido en el archivo http-interceptor.factory.js
.
Cargar una ciudad
Get /cities/:id
En la línea 19 está el código que trae la ciudad con identificador igual al pasado por argumento. Si todo sale bien actualiza el currentRecord
y si no, despliega un mensaje de error. Note que este llamado se hace únicamente si se recibió (vía $stateParams
) un identificador de la ciudad (línea 25). Esto se hace en el caso de editar una ciudad.
Crear una ciudad nueva
Post /cities
La función saveRecord
(línea 39) se ocupa de crear una nueva ciudad o de modificar una existente. la nueva ciudad se cree si esta no tiene id. En este caso se ejecuta:
$http.post(context, currentRecord).then(function () { $state.go('citiesList'); }, responseError)
Fijese que si la promesa regresa con éxito, se cambia de estado al estado citiesList
con la instrucción:
$state.go('citiesList')
Modificar una ciudad
Put /cities
La función saveRecord
salva un registro existente cuando el id
es conocido. Para esto llama:
$http.put(context + "/" + currentRecord.id, currentRecord).then(function () { $state.go('citiesList');}, responseError);
Note la construcción de la url: context + "/" + currentRecord.id
. Si queremos cambiar la información de la ciudad con identificador 1, el path será:
api/cities/1
.