AngularJS
El enfoque adecuado
TADHack Madrid
Mayo 2015
whoami
Gonzalo Ruiz de Villa Suárez
CoFounder @adesis
Google Developer Expert in Angular
+GonzaloRuizdeVilla
@gruizdevilla
Enfocar:
Dirigir la atención o el interés hacia un asunto o problema desde unos supuestos previos, para tratar de resolverlo acertadamente.
¿Por qué AngularJS?
Porque queremos utilizar el enfoque adecuado para resolver cada problema.
Reduce la distracción, centrando tu atención en elementos concretos.
Elementos de enfoque
-
Testing unitario
-
Testing e2e
-
Inyección de dependencias
-
Automatización
-
Estructura del proyecto
-
Módulos y bower
- Componentes de AngularJS
- Plantillas
- Controllers
- Servicios
- Filtros
- Directivas
- Interceptors/decorators
Testing unitario
- AngularJS está diseñado pensando desde el primer instante en los tests unitarios.
- La inyección de dependencias facilita el mocking.
- (Con)céntrate solo en el elemento que tengas entre manos.
- Bad smell detector:
Encontrando el enfoque adecuado:
La importancia del primer test.
beforeEach(inject(function($filter) {
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
str = "tuvwxyz";
number = 100.045;
limitTo = $filter('limitTo');
}));
it('should return the first X items when X is positive', function() {
expect(limitTo(items, 3)).toEqual(['a', 'b', 'c']);
expect(limitTo(items, '3')).toEqual(['a', 'b', 'c']);
expect(limitTo(str, 3)).toEqual("tuv");
expect(limitTo(str, '3')).toEqual("tuv");
expect(limitTo(number, 3)).toEqual("100");
expect(limitTo(number, '3')).toEqual("100");
});
Tests e2e
it('should filter results', function() {
element(by.model('user')).sendKeys('jacksparrow');
element(by.css(':button')).click();
expect(element.all(by.repeater('task in tasks')).count()).toEqual(10);
element(by.model('filterText')).sendKeys('groceries');
expect(element.all(by.repeater('task in tasks')).count()).toEqual(1);
});
Abordan cuestiones distintas
Pero comprueban el mismo cuerpo
Inyección de dependencias
“Pedid y se os dará.”
Inyección de dependencias
module.controller("MyController", function (myService){
var vm = this;
myService
.doSomething()
.then(function (value){
vm.value = value;
});
});
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
fn.toString()
.match(FN_ARGS)
.split(FN_ARG_SPLIT)
¿Queremos minificar?
someModule.controller('MyController', ['greeter', function(myService) {
// ...
}]);
Notación de array
Anotación mediante propiedad $inject
var MyController = function(myService) {
// ...
}
MyController.$inject = ['myService'];
someModule.controller('MyController', MyController);
And try to stay
DRY!
with ng-annotate
angular.module('myModule')
.controller('MyController', MyController);
/*@ngInject*/
function MyController(myService) {
// ...
}
Automatización
Porque lo repetitivo distrae y nos hace perder el foco.
Para gustos colores
Broccoli
gulp.js
Grunt
¿Qué puedes automatizar?
- JSHint
- Minificación de HTML, CSS y JS
- Concatenación de ficheros
- ng-annotate
- livereload
- ¡mock server!
- tests, tests, tests
- Jade
- i18n
- ES6 transpile, coffescript, ...
- bump de versiones
- ....
¿Qué MÁS puedes automatizar?
Aquello que repitas habitualmente.
* "Puff, estoy hasta arriba, ya lo haré cuando termine esta entrega"
No te obsesiones con automatizar todo al principio y después tampoco seas "vago"* para automatizar lo que te haga perder tiempo.
Convention
over
configuration
Reduce el ruido
y simplifica la automatización, refactorización, el rediseño, etc.
- ¿Donde están los componentes?
- Carpetas
- Nombres de ficheros
- ¿Cómo se llaman los componentes?
- ¿Cómo se llaman los tests?
- ¿Cómo se llaman los módulos?
- ¿Cómo son los estados?
- ¿Cómo son mis APIs?
- ¿Cómo se llaman mis entidades?
- ¿Cómo formateo el código?
Convención,
porque soy como Dory
Se me olvida configurar y se me olvida lo configurado...
...y lo prefiero así.
Mantén las cosas relacionadas juntas y ordenadas
-
Elementos específicos
- Vistas, controladores y servicios
- Elementos comunes
- Servicios
- Interceptors
- Filtros
- Directivas
Inspírate en el equipo de Angular
https://github.com/angular/angular-seed
y en las guías de estilo de
https://github.com/johnpapa/angular-styleguide
Cada vez van a ser más "opinionated" sobre la estructura del código.
No tengas miedo de cambiar la convención.
Pero hazlo de golpe
Una convención bien seguida se cambia muy rápido.
La mezcla de convenciones no es una convención.
Si tu proyecto crece, trocealo
Gestiona las partes independientes de forma independiente.
Utiliza repositorios independientes para cada parte.
bower y .bowerrc
- Tu proyecto web tendrá dependencia de distintos componentes.
- Si esos componentes están en repos privados, apóyate en el shorthand-resolver
- Utiliza 'semver' para gestionar y etiquetar las versiones.
- En el proyecto principal, "bower install usuario/componente --save"
angular.module('myApp', ['miModule1', 'miModule2', 'miModule3'])
Otra vez, minimiza la configuración
main-bower-files
https://github.com/ck86/main-bower-files
Todo lo anterior multiplica la potencia de
Dirección de las dependencias
HTML y Controladores
Separación de responsabilidades.
.signup(ng-controller="SignupCtrl as signup")
form(name="signupForm" ng-show="signup.state = 'START'")
h1 Sign up
label User
input(ng-model="signup.model.user", required)
label Name
input(ng-model="signup.model.name", required)
button(
ng-disabled="signupForm.$invalid",
ng-click="signup.createAccount()") Create account
div(ng-show="signup.state == 'LOADING'")
Creating account
div(ng-show="signup.state == 'CREATED'")
Account {{signup.model.user}} created!
angular
.module('myModule')
.controller('SignupCtrl', SignupCtrl);
function SignupCtrl(signupService){
var vm = this;
vm.state = "START";
vm.model = {};
vm.createAccount = createAccount;
function createAccount(){
vm.state = "LOADING";
signupService.signup(vm.model).then(function(){
vm.state = "CREATED";
});
}
});
Servicios
Encapsulando la lógica de negocio.
angular
.module('myServices)
.service('signupService', signupService);
function signupService($http){
this.signup = signup;
function signup(data) {
return $http
.post('/api/signup', data)
.then(response => response.data);
};
};
Directivas
- El sitio donde encapsular la manipulación del DOM
- Mediante las directivas, AngularJS se convierte en un framework de frameworks: d3.js, famo.us, Raphaël, ...
pie-chart(data="populations")
NgModelController
Al adaptarnos a la filosofía AngularJS, seguimos manteniendo el foco sobre cada elemento.
angular
.module('miDirectives')
.directive('usernameAvailable', usernameAvailable);
function usernameAvailable(signupService) {
return {
require : 'ngModel',
link : function($scope, element, attrs, ngModel) {
ngModel.$asyncValidators.usernameAvailable = signupService.usernameAvailable;
};
}
}
input(ng-model="signup.user", required, username-available)
Las directivas permiten definir el comportamiento de un nuevo elemento. Aumentamos la semántica.
Las directivas permiten decorar elementos y hacer composición de funcionalidad de forma transversal.
Filtros
ul
li(ng-repeat="lineas in pedido.lineas | reverse")
.description {{linea.description}}
.ammount {{ pedido.ammount | ammount }}
p Total ammount: {{ pedido.ammount | ammount }}
Centraliza la definición de transformaciones de salida habituales. Los filtros no modifican el modelo.
angular
.module('miFilters')
.filter('ammount', ammount);
function ammount() {
return function(value) {
return value.currencyCode + ' ' + value.quantity;
})
})
Crosscutting Concerns
Y el poder de la inyección de dependencias
- ¿Quieres hacer log de las llamadas un servicio? Decóralo, interceptando las llamadas.
- ¿Quieres cachear un servicio? Decóralo, interceptando las llamadas.
- ¿Quieres enviar credenciales en peticiones Ajax? Interceptadores de http.
- ¿Quieres reintentar peticiones Ajax? Interceptadores de http.
- ¿Quieres reintentar peticiones Ajax pero después de refrescar la sesión tras un 401 unauthorized ? Interceptadores de http.
Cuando cada línea de código parece que te importa.
AngularJS. El enfoque adecuado. v2
By Gonzalo Ruiz de Villa
AngularJS. El enfoque adecuado. v2
Con AngularJS podemos escoger en cada momento el mejor enfoque para resolver cada problema que tengamos que abordar.
- 3,855