仍然对angularjs服务和工厂感到困惑

时间:2014-04-12 18:01:01

标签: javascript angularjs

我已经阅读了几个关于angularjs服务和工厂的主题。我知道服务是单例,工厂返回对象的实例。但我仍然不明白如何使用它们。我的应用是一个简单的社交网络。使用该应用程序的人需要登录,然后他们可以查看其他成员并向他们发送消息。

我的理想设计是:

  1. 创建一个Member对象来表示我的服务的任意成员。每当'list'或'get'操作返回数据时,它都会被包含在这个成员中,所以我可以在其上调用实用程序方法。我会用这个工厂。
  2. 当用户登录应用程序时,创建一个成员来表示它们(包含用户ID和身份验证令牌以用于将来经过身份验证的请求)。这必须可用于其他范围,因此可以附加到$ rootScope,或者是一个MemberService,它返回为经过身份验证的用户定制的成员实例。
  3. 我创建了以下内容:

    angular.module('myapp.models', [])
    .factory('Member', ['$log', 'DataService', '$http', 'Restangular',
        function($log, DataService, $http, Restangular) {
            return {
                user_id: null,
                first_name: null,
                last_name: null,
    
                authenticate: function(username, password, loginSuccessHandler, loginErrorHandler) {
                    $log.debug("inside Member.authenticate");
    
                    var authSuccessHandler = function(data, status, headers, config) {
                        $http.defaults.headers.common.Authorization = 'Basic ' + btoa(data._id + ':' + data.token);
                        var token = btoa(data._id + ':' + data.token);
                        user_id = data._id;       // attach this to the rootScope? Needs to be
                                                  // globally accessible (or even this whole object)
                        Restangular.setDefaultHeaders({'Authorization': 'Basic ' + token});
                        $log.debug("Auth successful, token stored " + token);
                        loginSuccessHandler();
                    };
    
                    DataService.authenticate(username, password, authSuccessHandler, authErrorHandler);
                },
          ...
    

    如何实例化这个并将其提供给其他范围(例如,在其他范围内,我知道登录用户的ID)?

    另外,在解析成员列表时,如何实例化此对象?例如。如果我有一个对象{first_name: "John", last_name: "Smith"}怎样才能从这个工厂获得一个具有这些属性的成员对象?

2 个答案:

答案 0 :(得分:8)

factoryservice都是provider的抽象。

这是angular用于实例化新提供程序的方法:

function provider(name, provider_) {
    if (isFunction(provider_) || isArray(provider_)) {
        provider_ = providerInjector.instantiate(provider_);
    }
    if (!provider_.$get) {
        throw Error('Provider ' + name + ' must define $get factory method.');
    }
    return providerCache[name + providerSuffix] = provider_;
}

第一部分在角度应用加载时实例化一个新的(单例)对象。 $get方法用作新对象的构造函数。这就是provider的实例化方式(来自角度的文档):

myApp.provider('unicornLauncher', function UnicornLauncherProvider() {
  var useTinfoilShielding = false;

  this.useTinfoilShielding = function(value) {
    useTinfoilShielding = !!value;
  };

  this.$get = ["apiToken", function unicornLauncherFactory(apiToken) {

    // let's assume that the UnicornLauncher constructor was also changed to
    // accept and use the useTinfoilShielding argument
    return new UnicornLauncher(apiToken, useTinfoilShielding);
  }];
});

以下是配置提供商的方法。在module.config()块中,与我们稍后将提供者注入控制器/指令/服务时不同,我们得到单例,而不是$get方法的返回值。

myApp.config(["unicornLauncherProvider", function(unicornLauncherProvider) {
  unicornLauncherProvider.useTinfoilShielding(true);
}]);

然后,每当您注入并呼叫unicornLauncher时,$get方法中的内容将被调用,您将获得一个新的UnicornLauncher,其中包含所提供的配置useTinfoilShielding = true 1}}在这种情况下。

如您所见,提供商有两个部分。首先,它是一个单例,在角度加载时进行实例化和配置,并推送到providerCache,因此您可以在整个应用程序中使用它。它还有一个用于实例化新对象的$get方法。

您可以这样看:单身人士将其属性和方法作为您在加载应用程序时设置的设置和数据,然后您可以创建使用这些设置和数据的新对象。

如果您为其他人创建了一个模块,需要单独修改每个应用程序的选项,这就是您的操作方法。您可以将提供程序注入到您的应用程序中,对其进行配置,然后根据需要将其提供给对象,以及我们在配置提供程序时设置的预设。

然后我们有服务工厂。这些实际上是provider的抽象。以下是角度用于启动factory的函数:

function factory(name, factoryFn) {
    return provider(name, { $get: factoryFn });
}

它只是一个provider,但不是返回一个包含我们看到的所有属性的对象,而是返回一个只有$get方法的对象而且没有任何对象其他可变部分,如provider

以下是service的功能:

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
        return $injector.instantiate(constructor);
    }]);
}

这里我们有一个工厂,$get方法使用提供的构造函数返回一个单例,由angular实例化。所以你有一个可以在整个应用程序中使用的单例,你可以在任何地方使用它的方法。您可以在一个位置设置其属性,并查看您在应用程序中的其他位置设置的相同值,因为无论您在何处注入它都是相同的单例对象。

<强>摘要

主要区别在于service是由声明服务时提供的构造函数的角度创建的对象,而factory只是一个可用于创建新对象的函数就像在原生JavaScript中一样。

希望有助于理解factoryservice


回到你的问题。

在您的情况下,您有一个工厂要用于为每个成员创建一个新对象。在您注入Member的控制器中,您可以执行以下操作:

var data = {first_name: "John", last_name: "Smith"} 
var member = new Member(data);

对您的工厂进行少量更改:

.factory('Member', ['$log', 'DataService', '$http', 'Restangular',
    function($log, DataService, $http, Restangular) {
        return function(data) {
        //construct the object
        }
])

对工厂的new调用(作为构造函数注入)将返回一个新的唯一对象,其构造方式与JS中的任何其他对象一样。你可以用它做任何你想做的事。您可以将其保留在控制器中,$rootScope或保存成员及其他相关数据和功能的服务中。然后,您可以在整个应用程序中随时随地注入该服务。

答案 1 :(得分:0)

我只想说服务工厂以及提供商都是单身人士。

其余请查看以下答案:AngularJS: Service vs provider vs factory