Al igual que ocurre en cualquier lenguaje de programación y con cualquier framework del tipo MV*, nunca deberías tener tu lógica de negocio en un controlador, sino inyectar a este servicios que te provean de esta.
Otra de las cosas que tienes que tener en cuenta es la separación de cada clase/function de JavaScript en un archivo distinto y después tener una buena estrategia de despliegue con herramientas como Grunt o Gulp, esta última es la que actualmente está utilizando el equipo de Angularjs.
Una vez aclarado este punto, vamos a partir en este capítulo de un ejemplo mal realizado y su posterior corrección, para ello os planteo un simple módulo con un controlador y una llamada $http para recuperar una lista de valores desde un archivo json en el servidor.
Estructura del proyecto
app.js
Función responsable de la definición del módulo.
frameworks.json
Archivo json estático con los datos que queremos recuperar y mostrar en nuestra vista.
myController.js
En este controlador podemos observar que estamos recuperando los datos del archivo framework.json y asignandolos al objeto model de nuestro scope y es este el punto donde realmente está mal y que vamos a describir una vez hagamos una distribución correcta de nuestro código.
index.html
En la vista simplemente lo que hacemos es declarar nuestras intenciones de utilización y binding así como cargar cada uno de los archivos JavaScript necesarios para el buen funcionamiento de esta.
Te has planteado, que ocurre si tengo 1000 vistas y mil controladores, crees que puede ser una buena idea cargar los 1000 controladores en archivos separados, es por eso por lo que planteaba el uso de herramientas con Grunt o Gulp.
http://travismaynard.com/writing/getting-started-with-gulp
Aparte de estos link para que vayas aprendiendo Gulp o Grunt te recomiendo las siguientes guías de estructura de una app Angularjs y que en nuestro ejemplo no estamos cumpliendo, puesto que solo nos queremos centrar en la separación de responsabilidades de nuestro controlador.
Angular Style Guide(Jhon Papa)
Angular Style Guide(gocardless)
Piensa que ninguna de ellas es una doctrina y tu deberías de pensar que es lo más apropiado en cada una de tus app’s.
Hemos visto un mal ejemplo de un controlador por acceder a datos desde este, cuando su verdadera responsabilidad no es otra que controlar la lógica de la vista y por tanto el acceso a datos debería de estar en un servicio, tal y como se muestra a continuación.
FrameworkService.
Controller.
Centrándonos en el ejemplo correcto. Lo primero observa que ya no inyectamos en el controlador el ServiceProvider $http sino nuestro servicio frameworkService, que no hace otra cosa que recuperar el contenido del archivo framework.json y devolver una promise que enlazamos directamente a la vista por medio del objeto model.
Ojo con este tema puesto que a partir de versión 1.2.0+ de Angularjs, hay que especificar en configuración que queremos hacer esto, puesto que de serie ya no trabaja correctamente.
Para ello os mostramos que hay que hacer dentro de la configuración de vuestros módulos.
Es decir invocamos la función unwrapPromises de $parseProvider con valor a true, os pasamos unos link para que amplies información acerca de esto y por tanto dejes de utilizarlo.
Why no promise AngularJS 1.2.0+
fix($parse): remove deprecated promise unwrapping
Con esto puedes llegar a la conclusión de implementar todas tus llamadas a servidor de esta forma, te aconsejamos que mires los siguientes módulos de Angularjs antes de hacer tu propio módulo.
O bien te invitamos a que pruebes nuestro módulo mgCrud desde el cual puedes resolver el acceso a datos REST de forma declarativa.
Bueno, visto una pequeña guía de buenas prácticas en Angularjs, vamos a pasar a desvelar qué objetos son los que nos proporciona Angularjs para escribir nuestra lógica de negocio.
Values.
Nos permite almacenar valores/objetos en nuestro módulo que después pueden ser inyectables dentro de servicios, factorías, providers, directivas y controladores.
myModule.value(“valor”,1);
myModule.value(“user”, {id:1,name:”@_pedrohurtado y @xavipaper”});
Constant.
Son lo mismo que values, pero con la particularidad que se pueden inyectar en el momento de la configuración del módulo para ser utilizadas por este, cosa que no podemos hacer con values.
Factory.
Nos devuelve un value o bien la instancia de un objeto o la invocación a una función, este tipo de objetos se invocan desde el inyector por invocación no por la creación de un nuevo objeto, aunque perfectamente podrías devolver un servicio invocando a new.
myModule.service(‘service’,function(){
this.helloWord(){
console.log(“Hello World”);
};
});
myModule.Factory(‘factory’,function(service){
return new service();
});
Observa la diferencia entre factory y service como puedes adivinar una factory dada la función de javascript fx(){}; Se invoca de la siguiente forma fx(), de forma que el ámbito de this no es la propia función, mientras que en un servicio la invocación sería de la siguiente forma new fx().
Service.
Se invoca a través del patrón Constructor injector.
Provider.
Al igual que los servicios son invocados mediante el constructor. Pero con una pequeña diferencia que estos pueden ser inyectables al igual que las constantes en el config de nuestro módulo y que obligatoriamente tienen que exponer una función llamada $get que es la función inyectable.
myModule.provider(‘framework’,function(){
var _path;
this.setPatch(path){
_path = path;
};
this.$get = function($http){
function getAll(){
return $http.get(_path);
};
return getAll();
};
});
Para utilizarlo lo que tendrías que hacer es configurarlo en el config inyectandolo con el nombre de tu servicio+Provider, en nuestro caso frameworkProvider, con lo cual lo que haremos será establecer el path mediante la función setPatch en nuestro config.
myModule.config(function(frameworkProvider){
frameworkProvider.setPath(‘framework.js’);
});
Después lo puedes utilizar en un servicio, factoria, controlador o directiva pero inyectandolo sin la palabra Provider, es decir como framework, teniendo acceso de esta forma sólo a la función getAll() que retornas en this.$get.
Os pasamos unos link bastante interesantes donde se puede ver la diferencia de cada uno de estos objetos.
Service vs provider vs factory
Todos los objetos en Angular son Singleton, por ahorro lógicamente de consumo de memoria, aunque si lo necesitas puedes desde una factoría devolver una función y crearla con new desde un controlador o desde una directiva, nosotros no lo hemos hecho y nos hemos acostumbrado a trabajar con Singleton y por tanto es lo que te recomendamos.
Por último nos queda por citar los interceptors y los decorators en Angularjs, pero simplemente lo que vamos a hacer es pasar una referencia a la documentación oficial, puesto que es bastante buena, así como ver la posibilidad de decorar una directiva.
Hacking Core Directives in AngularJS
@_PedroHurtado y @XaviPaper
Vínculo hacia el capítulo anterior: (8) Forms