Criação de um módulo de serviço de dados reutilizável para sua API em AngularJS

Tenho pensado muito em serviços de dados e é a única parte do Angular que me incomoda. Desde então, percebi que isso só me incomoda porque o Angular não dita nem sugere realmente como você deve gerenciar seus serviços de dados. Então, estou sempre me questionando e me perguntando “estou fazendo isso certo?” Claro que a resposta é: se funciona para você, então está certo, porque o Angular não se importa. Isso funcionou até agora, mas gerou um código confuso que era difícil de transferir para outros projetos. Então voltamos para a prancheta.

Depois de ler vários posts e assistir a vídeos sobre modelagem de dados avançada no Angular, ainda achei que muitas das soluções eram um pouco complicadas. Não estou convencido de que estamos certos ainda, mas sugerimos o seguinte, que funciona bem para nossas necessidades e parece ser uma abordagem bastante decente, o que não é muito incompreensível. Eu queria publicar um código aqui para ajudar qualquer pessoa que está se sentindo da mesma forma que eu sobre o problema e para obter feedback.

services.js

Este é o serviço de base que iremos utilizar. Isso nos permite incluir um módulo em nosso aplicativo para incluir todos os serviços necessários para interagir com a API Mallzee. O legal disso é que nos permite criar chamadas de API para cada serviço que o estende e mantém grande parte do nosso código DRY.

Podemos criar muitas chamadas comuns aqui, como fetch, fetchOne, query, etc. O principal driver por trás dessa abordagem era porque estávamos nos repetindo escrevendo código de mesclagem de cache em nossos serviços. Queremos a capacidade de extrair nossos dados de LocalStorage primeiro, fazer uma solicitação para os dados ativos e atualizar quaisquer alterações. Mais sobre isso em outro post. Essa técnica nos permite fazer isso uma vez em nossas chamadas comuns.

Também criamos um provedor para que este serviço e seu uso de restangular possam ser acoplados a outros serviços sem causar problemas com URLs de base e similares.

angular.module('mallzee.services', [
'restangular',
'mallzee.services.brands',
'mallzee.services.products'
]).provider('MallzeeService', function MallzeeServiceProvider() {

var baseUrl = 'https://api.mallzee.com';

var scope = this;
this.$get = ['MallzeeRestangular', function (MallzeeRestangular) {

MallzeeRestangular.setBaseUrl(baseUrl);

var MallzeeService = function () { };
MallzeeService.prototype = {
scope
: this,
initialise
: function () {
scope
= this;

if (scope.key) {
this[scope.key] = [];
}
if (scope.key && scope.model) {
// Extend any objects sourced from Local Storage
angular
.forEach(scope[scope.key], function (obj) {
angular
.extend(obj, scope.model);
});

// Extend any future objects we retrieve with Restangular
MallzeeRestangular.extendModel(scope.key, function (model) {
return angular.extend(model, scope.model);
});
}
},
fetch
: function () {
scope
= this;
MallzeeRestangular.all(scope.key).getList().then(function (data) {
angular
.extend(scope[scope.key], data);
});
},
fetchOne
: function() {} // Removed to keep things short
query
: function () {} // Removed to keep things short
};

return {
getInstance
: function () {
return new MallzeeService();
}
}
}];

this.setBaseUrl = function (url) {
baseUrl
= url;
};

}).factory('MallzeeRestangular', ['Restangular', function (Restangular) {
return Restangular.withConfig(function(RestangularConfigurator) {
// Configure the version of Restangular used for this API
// i.e. set headers, object transforms etc
});
}]);

services / marks.js

Quando criamos nosso serviço, apenas injetamos nosso serviço de baseMallzeeService e estendemos uma instância dele para nos fornecer uma API específica de serviço.

Aproveitamos a fábrica para dar aos itens devolvidos uma estrutura tipo modelo, este é um lugar perfeito para colocar a lógica do negócio e mantê-la nossa dos controladores.

angular.module('mallzee.services.brands', [])
// This will act as the model for each brand item received from the API
.factory('Brand', [function () {
return {
toggle
: function () {
this.enabled = !this.enabled;
return this.enabled;
}
// Add model specific API function here
};
}])
.factory('BrandsService', ['MallzeeService', 'Brand', function (MallzeeService, Brand) {

var brandsService = angular.extend(MallzeeService.getInstance(), {
key
: 'brands',
model
: Brand

// Add any specific collection API functions to this object
});
brandsService
.initialise();

return brandsService;
}]);

controladores / marcas.js

Podemos então usar isso de forma muito simples em nossos controladores.

angular.module('mallzee.controllers.brands', [])
.controller('BrandCtrl', [
'$scope',
'BrandsService',
function ($scope, $state, BrandsService) {

$scope
.brands = BrandsService.brands;

$scope
.$on('$viewContentLoaded', function (event, view) {
if (view.stateName === 'brands') {
BrandsService.fetch();
}
});
}]);

app.js

Por fim, apenas incluímos o serviço de nível superior e o configuramos se quisermos apontar para uma URL diferente, digamos durante o desenvolvimento, ou qualquer outra coisa.

angular.module('mallzee', [
'mallzee.services'
).config(['MallzeeServiceProvider', function (MallzeeServiceProvider) {
MallzeeServiceProvider.setBaseUrl('https://dev.mallzee.com');
});

Estou gostando disso porque devemos ser capazes de despejar isso em qualquer projeto angular agora e estar confortáveis, pois nossas interações permanecerão estáveis ​​ao falar com a API. Também está configurado para funcionar bem com nosso sistema de cache e armazenamento local.

Eu adoraria ouvir a opinião das pessoas sobre isso.