Вы находитесь на странице: 1из 32

BUENAS PRCTICAS EN ANGULARJS Marino Posadas

1.X @marinoposadas
IMPORTANTE
Este documento forma parte de nuestro curso de la versin 1.X de Angular, cuyo
nombre oficial es AngularJS y que tiene previsto seguir actualizndose al menos un
par de aos ms desde la aparicin de Angular.
En Septiembre de 2016 sali Angular 2, a partir de entonces su nombre oficial es
simplemente Angular (independientemente de la versin). Angular rompe la
compatibilidad hacia atrs con AngularJS, ya que se trata de un framework
totalmente diferente que ha sido reescrito desde cero.
Si vas a empezar un proyecto nuevo y no sabes por cual decidirte, te recomendamos
Angular. Si necesitas aprender este nuevo framework, tambin podemos ayudarte.

www.campusmvp.es
GUA DE BUENAS PRCTICAS
Existen algunas guas que los expertos recomiendan basadas en los miles de
proyectos que estn actualmente en produccin.
Dos de esas guas (las ms valoradas), son las de John Papa y Todd Moto (ambos
son Google Developer Experts).
Lo que sigue es un compendio de los puntos ms relevantes dentro de ellas,
diseadas especialmente para las versiones 1.x de Angular.

www.campusmvp.es
PUNTOS PRINCIPALES
Estas buenas prcticas se basan en un conjunto de puntos que recogen situaciones reales
del da a da del desarrollo con aplicaciones Angular. A destacar:

Principio de responsabilidad nica Manipulacin del DOM


mbitos de JavaScript Proceso de minificacin
Mdulos Guas de Nomenclatura
Funciones con Nombre vs. Funciones Estructura de la aplicacin
annimas
Mdulos y dependencias de mdulos
Controladores
Servicios y Factories
Resolucin de Promises
www.campusmvp.es
PRINCIPIO DE RESPONSABILIDAD NICA
Un componente por archivo, y mejor que no supere las 400 lneas de cdigo.
Eso facilita los test unitarios y los "mocking"
Hace que sean ms fciles de leer y de mantener, evitando colisiones en el equipo de trabajo.
Puede evitar "bugs" ocultos que surgen a menudo cuando se combinan componentes en un archivo que puede
compartir variables o dependencias inesperadas.

Tambin puede entenderse que la construccin de funciones pequeas (o sea, < 75 lneas de
cdigo) es preferible por mantenimiento, reutilizacin y deployment.

www.campusmvp.es
PRINCIPIO DE RESPONSABILIDAD NICA
/* recomendado */ // app.module.js
angular.module('app', ['ngRoute']);

/* recomendado */ // some.controller.js
angular.module('app').controller('SomeController', SomeController);
function SomeController() { }

/* recomendado */ // some.factory.js
angular.module('app').factory('someFactory', someFactory); function
someFactory() { }

www.campusmvp.es
MBITOS DE JAVASCRIPT
Envolver los componentes Angular en Immediately Invoked Function Expressions (IIFEs).

/* Evitar */
// logger.js
angular.module('app').
factory('logger', logger); // logger se aade como variable global

// storage.js
angular.module('app')
.factory('storage', storage); // storage se aade como variable global

Esto contribuye a rellenar innecesariamente el entorno global, y aumenta la posibilidad de


"memory leaks", y colisin de definiciones.
Especialmente idneo para "helpers", constantes y recursos similares.
www.campusmvp.es
MBITOS DE JAVASCRIPT
En lugar de lo anterior, es preferible usar:
/** recomendado * * no se dejan globales detrs*/
// logger.js

(function() {
'use strict';

angular.module('app').
factory('logger', logger);
function logger() { }

})();

www.campusmvp.es
MDULOS
Para evitar colisiones de nombres, es recomendable utilizar una nomenclatura para el nombre de los
mdulos.
Con nomenclatura compuesta: se organizan claramente las dependencias de cada mdulo.
La aplicacin se llama "app", y tiene dos mdulos dependientes de ella, "app.clientes" y
"app.proveedores".
En las definiciones, si se mantiene la filosofa de un componente por archivo, es difcil que se necesite usar
variable intermedia en la declaracin del mdulo:
/* recomendado */
angular.module('app', [
'ngAnimate',
'ngRoute',
'app.clientes',
'app.proveedores' ]);
www.campusmvp.es
MDULOS
Lo mismo puede decirse de la declaracin de controladores.
Si concatenamos las definiciones de controladores, nos evitamos el uso de una variable
intermedia.
/* recomendado */

angular.module('app')
.controller('SomeController', SomeController);

function SomeController() { }

Los mdulos se crean una sola vez, y se recuperan para referencia con una llamada

// para crear un mdulo:


angular.module('app', []);
// para recuperar una referencia
angular.module('app');
www.campusmvp.es
FUNCIONES CON NOMBRE VS. ANNIMAS
Cuando se pasa un "callback", es preferible usar funciones con nombre.
El cdigo es ms legible y se reduce la cantidad de "cdigo anidado"
En vez de:
angular.
module('app').
controller('DashboardController', function() { }).
factory('logger', function() { });

Es preferible:
/* recomendado */ // dashboard.js
angular.module('app').
controller('DashboardController', DashboardController);
function DashboardController() { }

// logger.js
angular .module('app').factory('logger', logger);
function logger() { }
www.campusmvp.es
FUNCIONES CON NOMBRE PARA OCULTAR DETALLES DE IMPLEMENTACIN EN LOS CONTROLADORES
Mantn los elementos a enlazar (bindable) al principio de las declaraciones.
Usa declaraciones de funciones con nombre en lugar de "function expressions". As se evitan muchos efectos
colaterales (orden de declaracin, variables extra, etc.).
Puedes usar una llamada a funcin antes de declarar la funcin gracias al "hoisting".

/* * recomendado * Usar declaracin de funciones y los miembros bindable al principio. */


function AvengersController(avengersService, logger) {
var vm = this;
vm.avengers = [];
vm.getAvengers = getAvengers;
vm.title = 'Avengers';
activate();
function activate() {
return getAvengers().then(function() {
logger.info('Activated Avengers View');
});
}
function getAvengers() {
return avengersService.getAvengers().then(function(data) {
vm.avengers = data;
return vm.avengers; });
}
}
www.campusmvp.es
CONTROLADORES
Usar la sintaxis "controllerAs" en las vistas

Los controladores son regenerados y proporcionan una nueva nica instancia. La sintaxis controllerAs es ms
cercana al constructor de JavaScript que la sintaxis clsica.
Promueve el uso de "objetos de referencia" en la vista (por ejemplo customer.name en lugar de name), que es ms
contextual, fcil de leer, y evita cualquier problema de referencia que puedan producirse en ausencia de
"punteado".
Ayuda a evitar el uso de llamadas $parent en vistas con controladores anidados.

<!-- evitar-->
<div ng-controller="CustomerController"> {{ name }} </div>

<!-- recomendado -->


<div ng-controller="CustomerController as customer"> {{ customer.name }} </div>

www.campusmvp.es
CONTROLADORES
Usar la sintaxis "controllerAs" en los controladores

Utiliza la sintaxis controllerAs en vez de la clsica con $scope.


La sintaxis controllerAs utiliza this dentro de los controladores para referenciar $scope
controllerAs es azcar sintctico para referirse a $scope. Se puede usar igual en la vista y se accede de la misma
forma a los mtodos de $scope.
Ayuda a evitar la tentacin de utilizar mtodos $scope en el interior de un controlador cuando se puede evitar o
moverlos a una factory, y hacer referencia a ellos desde el controlador.
Considera el uso de $scope en un controlador slo cuando sea necesario. Por ejemplo, cuando hagamos publicacin
y/o suscripcin a eventos utilizando $emit, $broadcast o $on.
/* evitar- */
function CustomerController($scope) {
$scope.name = {};
$scope.sendMessage = function() { };
}
/* recomendado (pero lee la siguiente seccin) */
function CustomerController() {
this.name = {};
this.sendMessage = function() { };
} www.campusmvp.es
CONTROLADORES
Usar la sintaxis "controllerAs" en los controladores con vm

Esto es una variante de lo anterior en casos en que el contexto this pueda ser
problemtico.
Se recomienda "cachear" this en otra variable (como vm, en referencia a
"ViewModel"), y usarla en lugar de this.
/* evitar*/
function CustomerController() {
this.name = {};
this.sendMessage = function() { };
}
/* recomendada */
function CustomerController() {
var vm = this;
vm.name = {};
vm.sendMessage = function() { };
} www.campusmvp.es
LLEVAR LA LGICA DE LOS CONTROLADORES A SERVICIOS
La lgica puede ser reutilizada por mltiples controladores cuando se coloca dentro de un servicio y se expone a
travs de una funcin.

La lgica de un servicio puede ser aislada ms fcilmente en una prueba unitaria, mientras que la lgica que
llama en el controlador puede saltarse fcilmente.

Elimina dependencias y oculta los detalles de implementacin del controlador.

Mantiene el controlador ligero, "sin extras" y focalizado en su operativa.

Podemos comparar el cdigo siguiente para ver las diferencias.

www.campusmvp.es
LLEVAR LA LGICA DE LOS CONTROLADORES A SERVICIOS
/* evitar */
function OrderController($http, $q, config, userInfo) {
var vm = this;
vm.checkCredit = checkCredit;
vm.isCreditOk;
vm.total = 0;
function checkCredit() {
var settings = {};
// Obtenet la URL de base del servicio de crdito de la configuracin
// Asignar las cabeceras necesarias para llamar al servicio
// Preparar la query string (URL) o el objeto de datos para el request
// Aadir credenciales del usuario para que el servicio lo identifique adecuadamente
// Usar JSONP para el navegador si no soporta CORS
return $http.get(settings).then(function(data) {
// Desempaquetar datos JSON en el objeto Response //
// para buscar maxRemainingAmount
vm.isCreditOk = vm.total <= maxRemainingAmount
}).
catch(function(error) {
// Interpretar error //
// Lidiar con timeout? retry? Intentar servicio alternativo?
// Volver a rechazar con el error adecuado
});
};
}
www.campusmvp.es
LLEVAR LA LGICA DE LOS CONTROLADORES A SERVICIOS
Sin embargo, si la lgica se encuentra en un servicio, la expresin es mucho ms simple.
El cdigo resultante es ms reutilizable y ms sencillo de depurar.

/* recomendado */
function OrderController(creditService) {
var vm = this;
vm.checkCredit = checkCredit;
vm.isCreditOk;
vm.total = 0;
function checkCredit() {
return creditService.isOrderTotalOk(vm.total)
.then(function(isOk) {
vm.isCreditOk = isOk;
})
.catch(showError);
};
}
www.campusmvp.es
MANTENER LA LGICA DE LOS CONTROLADORES FOCALIZADA

Definir un controlador para una vista y tratar de no reutilizarlo para otras vistas.

Es mejor delegar la lgica a una factora y mantener el controlador simple y centrado en su labor.

La reutilizacin en este caso es frgil y dificulta los test end-2-end.

www.campusmvp.es
SERVICIOS VS. FACTORIES
Los servicios se instancian con new y se usa this para mtodos y variables pblicas. As que, por consistencia, es
preferible utilizar factories.
Como los servicios son singleton solo hay una instancia de un servicio dado por inyector.

// servicio
angular.module('app').service('logger', logger);

function logger() {
this.logError = function(msg) { /* */ };
}

// factory
angular.module('app').factory('logger', logger);

function logger() {
return {
logError: function(msg) { /* */ }
};
}

www.campusmvp.es
FACTORIES Y EL PRINCIPIO SINGLE RESPONSIBILITY
Una factory debe tener una nica responsabilidad, que estar encapsulada por su contexto.
Una vez que una fbrica comienza a exceder ese propsito particular, se debe crear una nueva
fbrica.
Las factories son nicas y retornan un objeto que contiene los miembros del servicio.
Respecto a los servicios de datos (que devuelven datos), se debe de refactorizar la lgica de sus
operaciones e interaccin en una factora.
Hay que hacer que los servicios sean los responsables de las llamadas XHR (AJAX), almacenamiento
local, o en memoria, o cualquier otra operacin similar.
La responsabilidad de los controladores es la de recuperar informacin para la vista (nada ms).
Facilita los test de todas clases.
Adems, la implementacin del acceso al servicio puede ser muy especfica de un repositorio y no necesita
estar expuesta a otras acciones.

www.campusmvp.es
FACTORIES Y EL PRINCIPIO SINGLE RESPONSIBILITY
El cdigo siguiente ilustra la recomendacin oficial (oculta implementaciones):
/* recomendado */
// dataservice factory
angular.module('app.core')
.factory('dataservice', dataservice);

dataservice.$inject = ['$http', 'logger'];

function dataservice($http, logger) {


return { getAvengers: getAvengers };

function getAvengers() {
return $http.get('/api/funcionDeDatos')
.then(getAvengersComplete)
.catch(getAvengersFailed);

function getAvengersComplete(response) {
return response.data.results; }
function getAvengersFailed(error) {
logger.error('XHR Failed for getAvengers.' + error.data);
}
}
}
www.campusmvp.es
LAS LLAMADAS A DATOS, DEBEN DEVOLVER UNA PROMISE

Al llamar a un servicio de datos que devuelve una promesa (como $http), devolver tambin una promesa en la funcin que llama.
Por qu ?: Se pueden encadenar las promesas juntas y tomar medidas adicionales despus de la llamada de datos en funcin de
que se resuelva o rechace la promesa.
/* recomendado */
activate();
function activate() {
/** * Paso 1 * Llama a getAvengers para los datos y espera la promise */
return getAvengers().then(function() {
/** * Paso 4 * Ejecuta una accin al resolver la promise final */
logger.info('Vista de Avengers Disponible');
});
}
function getAvengers() {
/** * Paso 2 * Pide los datos al servicio y espera la promise */
return dataservice.getAvengers()
.then(function(data) {
/** * Paso 3 * Asigna los datos y resuelve la promise */
vm.avengers = data;
return vm.avengers;
});
}
www.campusmvp.es
MANIPULACIN DEL DOM

Al manipular el DOM directamente, utiliza una directiva.

Si hay formas alternativas, como el uso de CSS para definir estilos o los servicios de animacin, las
plantillas de angular, ngShow o ngHide, mejor usar estas opciones.

Por ejemplo, si la directiva simplemente esconde y muestra el DOM, utilizar ngHide / ngShow.

Razones:
La manipulacin DOM puede ser difcil de probar, depurar, y con frecuencia hay mejores formas (por ejemplo, CSS, animaciones,
plantillas).
Las opciones disponibles dentro del mdulo ngAnimate facilitan mucho esas tareas.
www.campusmvp.es
CUIDADO CON LOS PROCESOS DE MINIFYING EN D.I.
Evitar el uso de la sintaxis de acceso directo a las dependencias que se declaran sin necesidad de utilizar un enfoque
de minimizacin de fallos.
Por qu ?: Los parmetros de los componentes (por ejemplo, controller, factory, etc.) se convertirn a las variables
mudas.
Por ejemplo, si tenemos:
/* evitar- no es seguro en minification*/
angular.module('app')
.controller('DashboardController', DashboardController);

function DashboardController(common, dataservice) { }

Se pueden convertir en:

angular.module('app').controller('DashboardController', d);

function d(a, b) { }

www.campusmvp.es
CUIDADO CON LOS PROCESOS DE MINIFYING EN D.I.
Evitar crear largas listas de dependencias "in-line", y utilizar $injector en su lugar:

/* evitar*/
angular.module('app')
.controller('DashboardController',
['$location', '$routeParams', 'common', 'dataservice', Dashboard]);

function Dashboard($location, $routeParams, common, dataservice) { }

/* recomendado */
angular.module('app')
.controller('DashboardController', DashboardController);

DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice'];

function DashboardController($location, $routeParams, common, dataservice) { }

www.campusmvp.es
NOMENCLATURA

Utilizar nombres coherentes para todos los componentes siguiendo un patrn que describa la funcin del componente
y (opcionalmente) su tipo.
Un patrn recomendado es caracterstica.tipo.js.
Hay 2 nombres para la mayora de los activos:
El nombre del archivo (avengers.controller.js)
El nombre del componente registrado en angular (AvengersController)

Las convenciones de nomenclatura ayudan a proporcionar una forma consistente para encontrar el contenido de un
vistazo.
La consistencia en el proyecto es de vital importancia, as como la coherencia con el equipo de desarrollo. Si se
expande a toda la empresa proporciona una enorme eficacia.
Las convenciones de nomenclatura deben simplemente ayudar a encontrar el cdigo ms rpido y hacer que sea ms
fcil de entender.

www.campusmvp.es
NOMENCLATURA: NOMBRES DE ARCHIVOS
Por ejemplo, si disponemos de controladores y servicios en archivos distintos:
/** * recomendado */ // configuracion
avengers.config.js
// controladores
avengers.controller.js
avengers.controller.spec.js // directivas
avenger-profile.directive.js
// servicios/factories avenger-profile.directive.spec.js
logger.service.js
logger.service.spec.js

// constantes
constants.js

// definicin de mdulos
avengers.module.js

// rutas
avengers.routes.js
avengers.routes.spec.js

www.campusmvp.es
ESTRUCTURA DE LA APLICACIN: LIFT
Estructurar la aplicacin de tal manera que se pueda localizar el cdigo rpidamente, e identificar el cdigo de un
solo vistazo. La estructura debe seguir las 4 pautas bsicas LIFT.
LIFT proporciona una estructura coherente que funciona bien, es modular, y hace que sea ms fcil aumentar la
eficiencia desarrollador mediante la bsqueda de cdigo rpidamente.
Otra forma de comprobar la estructura de su aplicacin es preguntarse: Con qu rapidez se puede abrir y trabajar
en todos los archivos relacionados con una caracterstica?

Localizacin fcil de nuestro cdigo


Identificar el cdigo de un vistazo
(F) Estructura plana (Flat) tanto como sea posible.
Tratar de mantener un contexto DRY (Do not Repeat Yourself)

www.campusmvp.es
ESTRUCTURA DE LA APLICACIN: MODULARIDAD
Usar muchos mdulos pequeos, auto-contenidos.
Encapsulacin de responsabilidad

Crear un mdulo aplicacin-raz (root-module).


Su papel ser aglutinar a todo el resto de mdulos y caractersticas de la aplicacin.
Ese ser su nico papel. La funcionalidad especfica se delega a cada mdulo.

Crear mdulos por reas (dominios) de aplicacin.

Si hay un bloque reutilizable, eso debera ser un mdulo.

El mdulo raz depender del resto de mdulos de dominio, y de los compartidos o reutilizables.
www.campusmvp.es
REFERENCIAS

Documentacin oficial de AngularJS: https://docs.angularjs.org/api

Gua de John Papa: https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md

Guas de Todd Moto: https://github.com/toddmotto/angular-styleguide

www.campusmvp.es
NADIE NACE SABIENDO. QUIERES APRENDER MS?

Este documento forma parte de nuestro curso online de AngularJS 1.X en


campusMVP.
Si necesitas dominar el desarrollo con AngularJS 1.X, no lo dudes. Aprenders
online, a tu ritmo y podrs resolver cualquier duda que te surja.
Si lo que quieres aprender es Angular (versin 2 y sucesivas) te recomendamos este
otro curso.
Nos vemos dentro del aula virtual!

Вам также может понравиться