Gracias a una gran cantidad de fuentes valiosas, tengo algunas recomendaciones generales para implementar componentes en aplicaciones AngularJS:
Controlador
El controlador debe ser solo una capa intermedia entre el modelo y la vista. Intenta hacerlo lo más delgado posible.
Se recomienda evitar la lógica de negocios en el controlador. Se debe mover al modelo.
El controlador puede comunicarse con otros controladores mediante la invocación de métodos (posible cuando los niños desean comunicarse con los padres) o $ emit , $ broadcast y $ on . Los mensajes emitidos y emitidos deben mantenerse al mínimo.
El controlador no debería preocuparse por la presentación o la manipulación DOM.
Intenta evitar los controladores anidados . En este caso, el controlador principal se interpreta como modelo. Inyecte modelos como servicios compartidos en su lugar.
Ámbito de aplicación en el controlador se debe utilizar para la unión modelo con vista y
encapsular Ver Modelo como para Presentación modelo patrón de diseño.
Alcance
Trate el alcance como de solo lectura en las plantillas y de solo escritura en los controladores . El propósito del alcance es referirse al modelo, no ser el modelo.
Al hacer un enlace bidireccional (ng-model) asegúrese de no enlazar directamente a las propiedades del alcance.
Modelo
El modelo en AngularJS es un singleton definido por servicio .
El modelo proporciona una forma excelente de separar datos y mostrarlos.
Los modelos son candidatos principales para pruebas unitarias, ya que generalmente tienen exactamente una dependencia (alguna forma de emisor de eventos, en el caso común $ rootScope ) y contienen una lógica de dominio altamente comprobable .
El modelo debe considerarse como una implementación de una unidad particular. Se basa en el principio de responsabilidad única. La unidad es una instancia responsable de su propio alcance de lógica relacionada que puede representar una entidad única en el mundo real y describirla en el mundo de la programación en términos de datos y estado .
El modelo debe encapsular los datos de su aplicación y proporcionar una API
para acceder y manipular esos datos.
El modelo debe ser portátil para que pueda transportarse fácilmente a una aplicación similar.
Al aislar la lógica de la unidad en su modelo, ha facilitado la localización, actualización y mantenimiento.
Model puede usar métodos de modelos globales más generales que son comunes para toda la aplicación.
Intente evitar la composición de otros modelos en su modelo utilizando inyección de dependencia si no es realmente dependiente para disminuir el acoplamiento de componentes y aumentar la capacidad de prueba y usabilidad de la unidad .
Trate de evitar el uso de oyentes de eventos en modelos. Los hace más difíciles de probar y generalmente mata modelos en términos del principio de responsabilidad única.
Implementación del modelo
Como el modelo debería encapsular cierta lógica en términos de datos y estado, debería restringir arquitectónicamente el acceso a sus miembros para que podamos garantizar un acoplamiento flexible.
La forma de hacerlo en la aplicación AngularJS es definirlo usando el tipo de servicio de fábrica . Esto nos permitirá definir propiedades y métodos privados de manera muy fácil y también devolver los de acceso público en un solo lugar que lo hará realmente legible para el desarrollador.
Un ejemplo :
angular.module('search')
.factory( 'searchModel', ['searchResource', function (searchResource) {
var itemsPerPage = 10,
currentPage = 1,
totalPages = 0,
allLoaded = false,
searchQuery;
function init(params) {
itemsPerPage = params.itemsPerPage || itemsPerPage;
searchQuery = params.substring || searchQuery;
}
function findItems(page, queryParams) {
searchQuery = queryParams.substring || searchQuery;
return searchResource.fetch(searchQuery, page, itemsPerPage).then( function (results) {
totalPages = results.totalPages;
currentPage = results.currentPage;
allLoaded = totalPages <= currentPage;
return results.list
});
}
function findNext() {
return findItems(currentPage + 1);
}
function isAllLoaded() {
return allLoaded;
}
// return public model API
return {
/**
* @param {Object} params
*/
init: init,
/**
* @param {Number} page
* @param {Object} queryParams
* @return {Object} promise
*/
find: findItems,
/**
* @return {Boolean}
*/
allLoaded: isAllLoaded,
/**
* @return {Object} promise
*/
findNext: findNext
};
});
Crear nuevas instancias
Intente evitar tener una fábrica que devuelva una nueva función capaz, ya que esto comienza a descomponer la inyección de dependencia y la biblioteca se comportará de manera incómoda, especialmente para terceros.
Una mejor manera de lograr lo mismo es usar la fábrica como API para devolver una colección de objetos con métodos getter y setter adjuntos.
angular.module('car')
.factory( 'carModel', ['carResource', function (carResource) {
function Car(data) {
angular.extend(this, data);
}
Car.prototype = {
save: function () {
// TODO: strip irrelevant fields
var carData = //...
return carResource.save(carData);
}
};
function getCarById ( id ) {
return carResource.getById(id).then(function (data) {
return new Car(data);
});
}
// the public API
return {
// ...
findById: getCarById
// ...
};
});
Modelo global
En general, trate de evitar tales situaciones y diseñe sus modelos correctamente para que pueda inyectarse en el controlador y utilizarse a su vista.
En particular, algunos métodos requieren accesibilidad global dentro de la aplicación. Para hacerlo posible, puede definir la propiedad ' común ' en $ rootScope y vincularla a commonModel durante el arranque de la aplicación:
angular.module('app', ['app.common'])
.config(...)
.run(['$rootScope', 'commonModel', function ($rootScope, commonModel) {
$rootScope.common = 'commonModel';
}]);
Todos sus métodos globales vivirán dentro de la propiedad ' común '. Este es algún tipo de espacio de nombres .
Pero no defina ningún método directamente en su $ rootScope . Esto puede conducir a un comportamiento inesperado cuando se usa con la directiva ngModel dentro de su alcance de vista, generalmente ensuciando su alcance y conduce a problemas de anulación de métodos de alcance.
Recurso
Resource le permite interactuar con diferentes fuentes de datos .
Debe implementarse utilizando el principio de responsabilidad única .
En particular, es un proxy reutilizable para puntos finales HTTP / JSON.
Los recursos se inyectan en modelos y brindan la posibilidad de enviar / recuperar datos.
Implementación de recursos
Una fábrica que crea un objeto de recurso que le permite interactuar con fuentes de datos RESTful del lado del servidor.
El objeto de recurso devuelto tiene métodos de acción que proporcionan comportamientos de alto nivel sin la necesidad de interactuar con el servicio $ http de bajo nivel.
Servicios
Tanto el modelo como el recurso son servicios .
Los servicios son unidades de funcionalidad no asociadas y poco acopladas que son independientes.
Los servicios son una característica que Angular ofrece a las aplicaciones web del lado del cliente desde el lado del servidor, donde los servicios se han utilizado comúnmente durante mucho tiempo.
Los servicios en aplicaciones angulares son objetos sustituibles que se conectan entre sí mediante inyección de dependencia.
Angular viene con diferentes tipos de servicios. Cada uno con sus propios casos de uso. Lea los Tipos de servicio de comprensión para obtener más detalles.
Intente considerar los principios principales de la arquitectura de servicio en su aplicación.
En general, según el Glosario de servicios web :
Un servicio es un recurso abstracto que representa la capacidad de realizar tareas que forman una funcionalidad coherente desde el punto de vista de las entidades proveedoras y las entidades solicitantes. Para ser utilizado, un agente proveedor concreto debe realizar un servicio.
Estructura del lado del cliente
En general, el lado del cliente de la aplicación se divide en módulos . Cada módulo debe ser comprobable como una unidad.
Intente definir módulos dependiendo de la característica / funcionalidad o vista , no por tipo. Vea la presentación de Misko para más detalles.
Los componentes del módulo se pueden agrupar convencionalmente por tipos como controladores, modelos, vistas, filtros, directivas, etc.
Pero el módulo en sí sigue siendo reutilizable , transferible y comprobable .
También es mucho más fácil para los desarrolladores encontrar algunas partes del código y todas sus dependencias.
Consulte la Organización del código en grandes aplicaciones AngularJS y JavaScript para más detalles.
Un ejemplo de estructuración de carpetas :
|-- src/
| |-- app/
| | |-- app.js
| | |-- home/
| | | |-- home.js
| | | |-- homeCtrl.js
| | | |-- home.spec.js
| | | |-- home.tpl.html
| | | |-- home.less
| | |-- user/
| | | |-- user.js
| | | |-- userCtrl.js
| | | |-- userModel.js
| | | |-- userResource.js
| | | |-- user.spec.js
| | | |-- user.tpl.html
| | | |-- user.less
| | | |-- create/
| | | | |-- create.js
| | | | |-- createCtrl.js
| | | | |-- create.tpl.html
| |-- common/
| | |-- authentication/
| | | |-- authentication.js
| | | |-- authenticationModel.js
| | | |-- authenticationService.js
| |-- assets/
| | |-- images/
| | | |-- logo.png
| | | |-- user/
| | | | |-- user-icon.png
| | | | |-- user-default-avatar.png
| |-- index.html
Buen ejemplo de estructuración angular de aplicaciones es implementado por angular-app - https://github.com/angular-app/angular-app/tree/master/client/src
Esto también es considerado por los generadores de aplicaciones modernas: https://github.com/yeoman/generator-angular/issues/109