Victor Gil, vgil@switch.com.uy, Marzo 2015
Desarrollo front end con Angular.js
Herramientas y buenas prácticas
Contenido
-
Introducción a Angular.js
- Breve historia
- Metas de diseño
- Ejemplo
-
Core Concepts
- modules
- controllers, templates
- services
- scopes
- DI
- directives
- filters
Contenido (cont.)
- Integración con otras librerías "the angular way"
- Herramientas
- Testing unitario
- Automatización con protractor
- angular 2
introducción
Breve historia
Desarrollado en 2009 por Miško Hevery y Adam Abrons en Brat Tech LLC para un servicio de storage online JSON que finalmente no prosperó, la librería fue publicada con licencia open source.
Abrons abandonó el proyecto y Hevery, que hoy trabaja en Google, continuó con el desarrollo y mantenimiento de la librería junto con otros empleados de Google.
introduccion
-
Es usado principalmente para desarrollar single page applications
- Ideal para aplicaciones con muchos CRUDs
- Separación de lógica de negocio, modelo de datos y vistas
- Provee API para realizar peticiones AJAX con facilidad
- Maneja el historial del browser transparentemente
- Tests simples
Metas de Diseño
Principios
-
Desacoplar la manipulación del DOM de la lógica de aplicación
-
Considerar igualmente importante el testing que escribir código de aplicación.
- La dificultad del testing está fuertemente ligada con cómo estructuramos nuestro código.
-
Desacoplar el frontend del backend.
- Permite el desarrollo en paralelo
herramientas
Una vez instalado node.js, instalamos lo siguiente:
$ npm install bower -g
$ npm install grunt-cli -g
$ npm install karma
$ npm install yo generator-angular
Ejemplo (Hello World)
Invoice:
Quantity:
Costs:
Total: {{qty * cost | currency}}
Notar: attr ng-app, ng-model y expresión en {{ }}
Controllers
- Contiene sólo lógica de negocio
- Expone datos y funciones en objeto $scope, accesibles desde la vista.
- Se asocia al DOM desde la vista con la directiva ng-controller
o desde module.config() a traves de los servicios de routing (p.e.: ngRoute o ui-router).
controllers (cont.)
js
angular.module('MyApp')
.controller('MyCtrl', function($scope) {
$scope.texto = 'Hello World';
$scope.onButtonClick = function() {
// modify $scope, use services, etc.
};
});
controllers (2-way data binding)
directives
-
Angular.js nos permite extender HTML con nuevos elementos y atributos mediante directivas
- Más específicamente, son "marcadores" en un elemento DOM (p.e.: atributos, nombre de elemento, comentario o clase CSS) que le indican al framework que asocie un comportamiento específico a ese elemento DOM y/o transforme ese elemento y sus hijos
dependency injection
Patrón de diseño que se encarga de cómo los componentes obtienen acceso de sus dependencias. El servicio $injector es el encargado de la creación de estos componentes, resolver sus dependencias y proveerlos a otros componentes cuando sean requeridos.
DI es usado en todos lados a lo largo y ancho de una aplicación angular
dependency injection 2
angular.module('myModule', [])
.factory('serviceId', ['depService', function(depService) {
// ...
}])
.directive('directiveName', ['depService', function(depService) {
// ...
}])
.filter('filterName', ['depService', function(depService) {
// ...
}]);
factory methods
angular.module('myModule', [])
.config(['depProvider', function(depProvider) {
// ...
}])
.run(['depService', function(depService) {
// ...
}]);
module methods
dependency injection 3
someModule.controller('MyController',
['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
...
$scope.aMethod = function() {
...
}
...
}]);
dependency injection 4
Existen 3 formas de inyectar dependencias a un componente
-
inline array annotation
-
app.controller('Ctrl', ['$scope', 'greeter', function($scope, greeter) {..}]);
- es la recomendada
- a través de $inject
- MyController.$inject = ['$scope', 'greeter'];
- Implícitamente. Podemos inyectar las dependencias simplemente agregando un parametro a la función con el nombre del componente.
- Puede romperse al ejecutar un minifier o un obfuscator
- Existen herramientas para pasarlo automáticamente al formato inline array
Scopes
- Qué es el scope?
-
- El scope es el objeto que se refiere al application model
- Es el contexto de ejecución para las {{expressions}} en nuestra vista.
- La API de scopes provee funciones para observar ($watch) cambios en el modelo y propagar eventos ($apply).
Scopes
Isolated scope en una directiva.
unit tests
JavaScript is a dynamically typed language which comes with great power of expression, but it also comes with almost no help from the compiler.
- DI facilita el testing porque podemos hacer mocks de cualquier dependencia al testear un componente.
-
Herramientas para unit tests
- Karma - programa de linea que inicia un browser y ejecuta los tests en su runtime.
- Jasmine - framework de
unit tests
describe("sorting the list of users", function() {
// individual tests go here
});
describe('sorting the list of users', function() {
it('sorts in descending order by default', function() {
// your test assertion goes here
});
});
describe('sorting the list of users', function() {
it('sorts in descending order by default', function() {
var users = ['jack', 'igor', 'jeff'];
var sorted = sortUsers(users);
expect(sorted).toEqual(['jeff', 'jack', 'igor']);
});
});
unit tests
Ejemplo grunt+karma+jasmine
e2e tests
El equipo de Angular.js ha construido una herramienta llamada protractor para ejecutar tests e2e.
- Protractor es un programa node.js que ejecuta tests e2e escritos en javascript
- Los tests usan el formato Jasmine
e2e tests (ejemplo)
describe('TODO list', function() {
it('should filter results', function() {
// Find the element with ng-model="user" and type "jacksparrow" into it
element(by.model('user')).sendKeys('jacksparrow');
// Find the first (and only) button on the page and click it
element(by.css(':button')).click();
// Verify that there are 10 tasks
expect(element.all(by.repeater('task in tasks')).count()).toEqual(10);
// Enter 'groceries' into the element with ng-model="filterText"
element(by.model('filterText')).sendKeys('groceries');
// Verify that now there is only one item in the task list
expect(element.all(by.repeater('task in tasks')).count()).toEqual(1);
});
});
0