Change - Know that nothing stays the same

Providers, una mejor manera de organización

December 13, 2014

angularjs
newstyle

El uso de providers es una excelente manera de organizar nuestro código al usar una api o algún servicio externo para obtener información. Entender que es un provider: Toda nuestra aplicación es el conjunto de un grupo de objetos que interactuar y colaboran para un objetivo. Estos objetos son instancias y puestos a colaborar por el injector service (https://code.angularjs.org/1.2.26/docs/api/auto/service/$injector). El injector crea dos tipos de objetos, services u objetos especializados. Los services son objetos que su API es definida por el usuario, es decir, la estructura es definida por el usuario al crearlo. Los objetos especializados, son particulares a AngularJS, controladores, directivas, filtros o animaciones. El injector debe conocer a partir de que debe ser creado dicho objeto. Se debe indicar la receta a partir de que es creado. En total existen cinco tipos de recetas, la primera y mas sencilla es Provider, después podemos encontrar Value, Factory, Recipe y Constant. Un objeto provider es un tipo de objeto que implementa un método $get. Una factory por ejemplo crea automáticamente un Provider vacío para instanciarlo. Pero creamos un Provider o una Factory La respuesta rápida es ambos. Una manera de organizar nuestro código es crear una factory que instancie el $resource que usamos contra nuestro servicio externo de datos, usamos una factory para hacer uso de ese provider y agregaremos métodos que extiendan la lógica particular a nuestro sistema. Creamos un Provider para interactuar con el servicio externo de datos. De esta manera tenemos varias ventajas:

(function(){ 'user strict';

/* DEFINICIO DE LA APLICACION */ angular.module('BankApp', ['ngResource']);

/* DEFINICION DE CONSTANTE AccountLimit */ angular.constant('AccountLimit', 15);

/* CONFIGURARION DE LA APLICACION Y SETEO DEL LIMITE DE Account */ angular.module('BankApp').config(configure); configure.$inject = ['AccountProvider', 'AccountLimit']; function configure(AccountProvider, AccountLimit){ AccountProvider.setLimit(AccountLimit); }

/* DEFINICION DE PROVIDER Account */ angular.module('BankApp').provider('Account', Account); function Account() { var limit = 20; this.$get = ['$resource', 'apiurl', function($resource, apiurl) { var obj = $resource([api_url, '/account/:id'].join(''), { id : '@id' }, { update: { method: 'PUT', url: [api_url, '/account/:id/'].join(''), params: {id: '@id'} }, byCBU: { method: 'GET', url: [apiendpoint, apiurl, '/', resource_uri, '/?cbu=:cbu&limit=1'].join(''), params: {cbu: '@cbu'} }, transactions: { method: 'GET', url: [apiendpoint, apiurl, '/', resource_uri, '/?limit=', limit, '&offset=:offset'].join(''), params: {offset: '@offset'} } }); return obj; }]; this.setLimit = function(value){ limit = value; }; }

/* DEFINICIO DE FACTORY AccountFactory */ angular.module('BankApp').factory('AccountFactory', AccountFactory); AccountFactory.$inject = ['Account', 'AccountLimit', '$q']; function AccountFactory(Account, AccountLimit, $q) { var factory = { getByCBU: _byCBU, getTransactions: _transactions }; return factory;

 /*
  *  Es una buena practica que los metodos que dependen de un servicio
  *  externo o API devuelvan una promesa
  */

function _byCBU(cbu){
  var deferred = $q.defer();
  Account
    .byCBU({cbu: cbu}).$promise
    .then(function(response){
      !!response ? deferred.resolve(response) : deferred.reject();
    })
    .catch(function(error){ deferred.reject(error); });
  return deferred.promise;
}

function _transactions(page){
  var params = {offset: AccountLimit};
  params.offset = (page-1) * AccountLimit;
  return Account.transactions(params).$promise;
}

} })();

En el ejemplo anterior usamos una constante para definir a nivel de aplicacion cuantos registros vamos a pedir por paginaa nuestro servicio externo de datos. Este cambio se hace en la configuracion de nuestra aplicacion:

angular.constant('AccountLimit', 15);

angular.module('BankApp').config(configure); configure.$inject = ['AccountProvider', 'AccountLimit']; function configure(AccountProvider, AccountLimit){ AccountProvider.setLimit(AccountLimit); }

El provider define todo lo relativo a nuestro servicio externo de datos:

angular.module('BankApp').provider('Account', Account);

function Account() { var limit = 20; this.$get = ['$resource', 'apiurl', function($resource, apiurl) { var obj = $resource([api_url, '/account/:id'].join(''), { id : '@id' }, { update: { method: 'PUT', url: [api_url, '/account/:id/'].join(''), params: {id: '@id'} }, byCBU: { method: 'GET', url: [apiendpoint, apiurl, '/', resource_uri, '/?cbu=:cbu&limit=1'].join(''), params: {cbu: '@cbu'} }, transactions: { method: 'GET', url: [apiendpoint, apiurl, '/', resource_uri, '/?limit=', limit, '&offset=:offset'].join(''), params: {offset: '@offset'} } }); return obj; }]; this.setLimit = function(value){ limit = value; }; }

La factory usa nuestro provider para consumir datos y en la factory solo tenemos la logica aplicada a esos datos:

angular.module('BankApp').factory('AccountFactory', AccountFactory);

AccountFactory.$inject = ['Account', 'AccountLimit', '$q']; function AccountFactory(Account, AccountLimit, $q) { var factory = { getByCBU: _byCBU, getTransactions: _transactions }; return factory; /* * Es una buena practica que los metodos que dependen de un servicio * externo o API devuelvan una promesa */ function _byCBU(cbu){

  var deferred = $q.defer();
  Account
    .byCBU({cbu: cbu}).$promise
    .then(function(response){
      !!response ? deferred.resolve(response) : deferred.reject();
    })
    .catch(function(error){ deferred.reject(error); });
  return deferred.promise;
}

function _transactions(page){
  var params = {offset: AccountLimit};
  params.offset = (page-1) * AccountLimit;
  return Account.transactions(params).$promise;
}

}

Beneficios del uso de providers

Un provider permite configurar valores en el momento de configurar nuestra aplicacion, en el ejemplo vemos como cambiamos el límite máximo de items que le pedimos por transacciones, cambiandolo de 20 que es el default a 15.

Toda interacción con el servicio externo de datos esta en un solo punto, si el servicio de datos cambia en alguna forma podemos ajustarlo rápidamente.

Nuestra factory o service (también podemos consumir un provider desde un service) solo contiene código de lógica aplicada al uso de los datos enviados/recibidos.

Cualquier duda ponganse en contacto por twitter agustinvinao, comentarios.


Agustin Vinao
Agustin Vinao.

Paradox: Life is a mystery. Don't waste time trying to figure it out.
Humor: Keep a sense of humor, especially about yourself. It is a strength beyond all measure.
Change: Know that nothing stays the same.