简体   繁体   English

AngularJS如何抽象服务?

[英]AngularJS how to abstract a service?

I have a data factory defined in CoffeeScript like: 我有一个在CoffeeScript中定义的数据工厂,如:

dataModule.factory "Dates", ->
  object:
    value: null

and a service defined in CoffeeScript like so: 以及CoffeeScript中定义的服务,如下所示:

dataModule.service "DatesService",
["$http", "Dates", "Requesting"
($http, Dates, Requesting) ->
  @url = "api/0/dates/"
  @object = Dates.object

  @getFromServer = (object, onSuccess, onError) ->
    Requesting.status = true
    $http.get(@url + getCacheBuster()
    ).success((data, status) ->
      data = null unless _.isObject(data)
      object.value = data
      Requesting.status = false
      if onSuccess
        onSuccess data

    ).error (data, status) ->
      data = null unless _.isObject(data)
      object.value = null
      Requesting.status = false
      if onError
        onError data, status


  @get = (onSuccess, onError) ->
    if null is @object.value
      @getFromServer @object, onSuccess, onError
    else
      if onSuccess
        onSuccess @object.value


  @clear = () ->
    @object.value = null
]

I then use the service like this: 然后我使用这样的服务:

dataModule.controller "DatesCtrl",
["$scope", "Dates", "DatesService", "Error"
($scope, Dates, DatesService, Error) ->
  $scope.dates = Dates.object
  $scope.error = Error

  $scope.handleError = ->
    $scope.dates.value = null
    $scope.error.value = "Did not get valid information from server."

  $scope.getDates = ->
    DatesService.get (data) ->

      if not data
        $scope.handleError()
        return
    ,
    (data, status) ->
      $scope.handleError()
      return

  $scope.getDates()
]

Notice that the service provides a simple caching mechanism. 请注意,该服务提供了一种简单的缓存机制。 This is so multiple controllers within the same view can get the data from the service, but the service only asks the server once. 这样,同一视图中的多个控制器可以从服务中获取数据,但服务只询问服务器一次。

Also notice that the injection of Dates, the @url and @value members are the only things that make this specific DatesService not reusable. 另请注意,注入Dates,@ url和@value成员是使这个特定DatesService不可重用的唯一因素。 I would like to abstract this service so that I can "derive"/"extend"/"do other thing" a CachedService into specific "concrete" services that have specific dependency injected, a specific @url and a specific @value. 我想抽象这个服务,以便我可以“推导”/“扩展”/“做其他事情”CachedService到具有特定依赖注入的特定“具体”服务,特定的@url和特定的@value。

I have three other services that I want to apply the same pattern to, each with their own data factory and url. 我有三个其他服务,我想应用相同的模式,每个服务都有自己的数据工厂和网址。

This is probably simple but I am struggling with the right vocabulary to properly search for the answer. 这可能很简单,但我正在努力用正确的词汇来正确搜索答案。

Can someone demonstrate the proper language mechanism(s) to do this abstraction? 有人可以演示正确的语言机制来进行抽象吗?

BETTER ANSWER 更好的答案

We figured it out, here's a way you can do it that's both simple and clean. 我们想通了,这是一种既简单又干净的方法。

function serviceMaker(service){
  return function($injector, /*default injections like*/ $q, $http){
    //Add shared functionality here
    this.foo = function(){ 
      //to reference dependent variables, I recommend using method access to avoid timing/order issues

      return this.MyURL(); //will be declared in extending service
    };

    //Can use local injections
    this.getPromise = $q.when('Definitely using injection too');

    //Finishes the other injections
    $injector.invoke(service, this);
  };
}

angular.module('test', [])

//Then, setup your service like this
.service('GreatService', serviceMaker(function($rootScope){ /*Can inject like normal!*/
    //remember to declare dependent methods
    this.MyURL= function(){return "http://stackoverflow.com";}

    //Can still add your own things
    this.bar = function(){
      //properties from base service are still accessible
      return this.foo()+"?param=huzzah";
    }
}))

//Another service
.service('NiceService', serviceMaker(function(){
    this.MyURL= function(){return "http://google.com";}
}))

This will do what you're looking for, using angular's $injector for a clean and straightforward method. 这将完成你正在寻找的东西,使用angular的$injector进行干净简单的方法。

OLD, BAD ANSWER JUST FOR CONTEXT 旧的,不好的答案只是为了上下文

I solved the same problem using the method described below... I'll claim that it works well, but my team's currently investigating some of the deeper Angular functions to see if it can be cleaned up and feel more natural. 我使用下面描述的方法解决了同样的问题......我声称它运行良好,但我的团队目前正在研究一些更深层次的Angular函数,看它是否可以清理并感觉更自然。

Here's the code for your basic service: 这是您的基本服务的代码:

var baseService = function(extraInjections, also){
  var serviceFunc = function( /*default injections here, like*/ $http, $q, /*and shame*/ param1, param2, param3, param4)
  {
     //perform other startup stuff set in custom service
     also(this, arguments);

     //apis can be set here, like
     this.getFromURL= function(url){...}

     //do common work here, like caching.
     //You can reference values that may be set in the custom service,
     if(this.url){this.getFromURL(this.url);}

  }


  //then, inject extra dependencies
  var depArray = ['$http', '$q'] //defaults
  depArray = depArray.concat(extraInjections);
  depArray.push(serviceFunc);

  return depArray; //returns service made with dependency array syntax (like ['$q', function($q){}]
}

Then, when writing your services, 然后,在编写您的服务时,

myModule.service("ServiceName", baseService([/*any extra injections, like*/'$rootScope'], function(me, injections){
  //the injections param holds injections, so here,
  var http = injections[0];
  //because $http is the first default injection.
  //Custom ones can be accessed too
  var rootScope = injections[2];

  //now, the 'me' variable is your service.
  //append api as needed
  me.getThatThing = function(){return rootScope.blah};

  //make sure you set any expected values you use in your base service
  me.url = "http://stackoverflow.com";
}

Obvious flaw that could be improved upon is that in order to add the extra injections, I have to add that silly param1, param2, param3 etc. to my serviceFunc , so that there's 'room' for the extra custom injections. 可以改进的明显缺陷是,为了添加额外的注射,我必须将愚蠢的param1, param2, param3等添加到我的serviceFunc ,以便为额外的自定义注射提供“空间”。 However, if you're okay with a little clumsiness, I can verify that this works quite well. 但是,如果你有点笨拙,我可以证实这很有效。

I've seen and tried many methods to extend an angular service. 我已经看到并尝试了许多方法来扩展角度服务。 The example here - and others which I found work well when extending a service one time. 这里的例子-等我找到工作做好延伸服务的一个时候。 When you want to extend an abstract service multiple times you could use the following approach: 当你想多次延长一个抽象的服务,你可以用下面的办法:

Abstract Service Definition 抽象服务定义

var AbstractCurdlService = function($q, $http) {

    var ENDPOINT = ""; //extend this service

    return {
        setEndpoint : function(value){
            ENDPOINT = value;
        },
        create: function(payload) {
            var deferred = $q.defer();
            $http({
                method : "POST",
                url : this.ENDPOINT,
                data: payload
            }).then(function(response){
                deferred.resolve(response.data);
            }, function(error){
                deferred.reject(error);
            });
            return deferred.promise;
        },
        /* additional service definitions */
    };
};

AngularJS Service 1 AngularJS服务1

app.factory('customerService', function($q, $http){
    var customerService = new AbstractService($q, $http);
    customerService.setEndpoint("/api/customer");
    return customerService;
});

AngularJS Service 2 AngularJS服务2

app.factory('reportService', function($q, $http){
    var reportService = new AbstractService($q, $http);
    reportService.setEndpoint("/api/report");
    return reportService;
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM