AngularJS:服务与提供商vs工厂

问题描述 投票:3258回答:31

AngularJS中的ServiceProviderFactory有什么区别?

angularjs dependency-injection angularjs-service angularjs-factory angularjs-provider
31个回答
2849
投票

从AngularJS邮件列表中我得到了an amazing thread,它解释了服务与工厂与提供商及其注入使用情况。编译答案:

Services

语法:module.service( 'serviceName', function ); 结果:将serviceName声明为可注入参数时,将为您提供该函数的实例。换句话说new FunctionYouPassedToService()

Factories

语法:module.factory( 'factoryName', function ); 结果:当将factoryName声明为injectable参数时,将通过调用传递给module.factory的函数引用来提供返回的值。

Providers

语法:module.provider( 'providerName', function ); 结果:当将providerName声明为可注入参数时,将为您提供(new ProviderFunction()).$get()。在调用$ get方法之前实例化构造函数--ProviderFunction是传递给module.provider的函数引用。

提供商的优势在于可以在模块配置阶段配置它们。

有关提供的代码,请参阅here

这是Misko的一个很好的进一步解释:

provide.value('a', 123);

function Controller(a) {
  expect(a).toEqual(123);
}

在这种情况下,喷射器只是按原样返回值。但是,如果你想计算价值怎么办?然后使用工厂

provide.factory('b', function(a) {
  return a*2;
});

function Controller(b) {
  expect(b).toEqual(246);
}

所以factory是一个负责创造价值的函数。请注意,工厂函数可以请求其他依赖项。

但是如果你想成为更多的OO并拥有一个名为Greeter的课程怎么办?

function Greeter(a) {
  this.greet = function() {
    return 'Hello ' + a;
  }
}

然后要实例化你必须写

provide.factory('greeter', function(a) {
  return new Greeter(a);
});

然后我们可以在这样的控制器中要求'greeter'

function Controller(greeter) {
  expect(greeter instanceof Greeter).toBe(true);
  expect(greeter.greet()).toEqual('Hello 123');
}

但这太浪漫了。写这个的更短的方法是provider.service('greeter', Greeter);

但是如果我们想在注射之前配置Greeter类呢?然后我们可以写

provide.provider('greeter2', function() {
  var salutation = 'Hello';
  this.setSalutation = function(s) {
    salutation = s;
  }

  function Greeter(a) {
    this.greet = function() {
      return salutation + ' ' + a;
    }
  }

  this.$get = function(a) {
    return new Greeter(a);
  };
});

然后我们可以这样做:

angular.module('abc', []).config(function(greeter2Provider) {
  greeter2Provider.setSalutation('Halo');
});

function Controller(greeter2) {
  expect(greeter2.greet()).toEqual('Halo 123');
}

作为旁注,servicefactoryvalue都来自提供者。

provider.service = function(name, Class) {
  provider.provide(name, function() {
    this.$get = function($injector) {
      return $injector.instantiate(Class);
    };
  });
}

provider.factory = function(name, factory) {
  provider.provide(name, function() {
    this.$get = function($injector) {
      return $injector.invoke(factory);
    };
  });
}

provider.value = function(name, value) {
  provider.factory(name, function() {
    return value;
  });
};

55
投票

在与提供商玩游戏时,我注意到一些有趣的事

对于服务提供者而言,注射剂的可见性与服务和工厂的可见性不同。如果声明AngularJS为“常量”(例如,myApp.constant('a', 'Robert');),则可以将其注入服务,工厂和提供程序。

但是如果你声明一个AngularJS“值”(例如,myApp.value('b', {name: 'Jones'});),你可以将它注入服务和工厂,但不能注入提供者创建函数。但是,您可以将其注入为您的提供者定义的$get函数中。 AngularJS文档中提到了这一点,但很容易错过。您可以在值和常量方法的部分中的%提供页面上找到它。

http://jsfiddle.net/R2Frv/1/

<div ng-app="MyAppName">
    <div ng-controller="MyCtrl">
        <p>from Service: {{servGreet}}</p>
        <p>from Provider: {{provGreet}}</p>
    </div>
</div>
<script>
    var myApp = angular.module('MyAppName', []);

    myApp.constant('a', 'Robert');
    myApp.value('b', {name: 'Jones'});

    myApp.service('greetService', function(a,b) {
        this.greeter = 'Hi there, ' + a + ' ' + b.name;
    });

    myApp.provider('greetProvider', function(a) {
        this.firstName = a;
        this.$get = function(b) {
            this.lastName = b.name;
            this.fullName = this.firstName + ' ' + this.lastName;
            return this;
        };
    });

    function MyCtrl($scope, greetService, greetProvider) {
        $scope.servGreet = greetService.greeter;
        $scope.provGreet = greetProvider.fullName;
    }
</script>

44
投票

对于新手而言,这是一个非常令人困惑的部分,我试图用简单的词语来澄清它

AngularJS服务:用于与控制器中的服务引用共享实用程序功能。服务本质上是单例,因此对于一个服务,在浏览器中只创建一个实例,并在整个页面中使用相同的引用。

在服务中,我们使用此对象创建函数名称作为属性。

AngularJS Factory:Factory的目的也与Service相同,但在这种情况下,我们创建一个新对象并添加函数作为此对象的属性,最后我们返回此对象。

AngularJS Provider:这个的目的是相同的,但Provider提供了它的$ get函数的输出。

http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider解释了定义和使用服务,工厂和提供商


34
投票

对我来说,理解差异的最好和最简单的方法是:

var service, factory;
service = factory = function(injection) {}

AngularJS如何实例化特定组件(简化):

// service
var angularService = new service(injection);

// factory
var angularFactory = factory(injection);

因此,对于服务,成为AngularJS组件的是类的对象实例,它由服务声明函数表示。对于工厂,它是从工厂申报功能返回的结果。工厂的行为可能与服务相同:

var factoryAsService = function(injection) {
  return new function(injection) {
    // Service content
  }
}

最简单的思考方式如下:

  • 服务是一个单例对象实例。如果要为代码提供单例对象,请使用服务。
  • 工厂是一个班级。如果要为代码提供自定义类,请使用工厂(由于已经实例化,因此无法使用服务)。

工厂的“类”示例在评论中提供,以及提供商差异。


33
投票

我对此事的澄清:

基本上所有提到的类型(服务,工厂,提供商等)只是创建和配置全局变量(当然对于整个应用程序来说是全局变量),就像旧的全局变量一样。

虽然不推荐全局变量,但这些全局变量的实际用途是通过将变量传递给相关控制器来提供dependency injection

创建“全局变量”的值有很多级别的复杂性:

  1. 不变 这定义了一个在整个应用程序中不应该修改的实际常量,就像其他语言中的常量一样(JavaScript缺少的东西)。
  2. 值 这是一个可修改的值或对象,它可以作为一些全局变量,甚至可以在创建其他服务或工厂时注入(请参阅下文)。但是,它必须是一个“字面值”,这意味着必须写出实际值,并且不能使用任何计算或编程逻辑(换句话说39或myText或{prop:“value”}都可以,但是2 + 2不是)。
  3. 厂 更通用的值,可以立即计算。它的工作原理是将函数传递给AngularJS,其中包含计算值所需的逻辑,AngularJS执行它,并将返回值保存在命名变量中。 请注意,可以返回一个对象(在这种情况下它将起到类似于服务的作用)或一个函数(将作为回调函数保存在变量中)。
  4. 服务 服务是一个更精简的工厂版本,仅当值是一个对象时才有效,它允许直接在函数中编写任何逻辑(就好像它是一个构造函数),以及声明和访问使用this关键字的对象属性。
  5. 提供商 与作为工厂简化版本的服务不同,提供程序是一种更复杂但更灵活的初始化“全局”变量的方式,最大的灵活性是从app.config设置值的选项。 它使用服务和提供程序的组合,通过向提供程序传递一个函数,该函数具有使用this关键字声明的属性,可以在app.config中使用。 然后它需要一个单独的$ .get函数,该函数由AngularJS在通过app.config文件设置上述属性后执行,并且这个$ .get函数的行为与上面的工厂一样,因为它的返回值用于初始化“全球“变量。

26
投票

我的理解在下面非常简单。

工厂:您只需在工厂内创建一个对象并将其返回。

服务:

您只需要一个使用此关键字定义函数的标准函数。

提供者:

您定义了一个$get对象,它可用于获取返回数据的对象。


24
投票

来自Angular docs的摘要:

  • 有五种配方类型可以定义如何创建对象:qazxsw poi,qazxsw poi,qazxsw poi,ValueFactory
  • 工厂和服务是最常用的食谱。它们之间的唯一区别是Service配方更适合自定义类型的对象,而Factory可以生成JavaScript原语和函数。
  • 提供者配方是核心配方类型,所有其他配方只是语法糖。
  • 提供者是最复杂的配方类型。除非要构建需要全局配置的可重用代码,否则不需要它。

Service


来自SO的最佳答案:

ProviderConstant enter image description here


16
投票

另外一个澄清是工厂可以创建函数/原语,而服务则不能。看看这个基于Epokk的https://stackoverflow.com/a/26924234/165673https://stackoverflow.com/a/27263882/165673

工厂返回一个可以调用的函数:

https://stackoverflow.com/a/16566144/165673

工厂还可以使用可以调用的方法返回一个对象:

jsFiddle

该服务返回一个对象,该对象具有可以调用的方法:

http://jsfiddle.net/skeller88/PxdSP/1351/

有关更多详细信息,请参阅我在差异上写的帖子:myApp.factory('helloWorldFromFactory', function() { return function() { return "Hello, World!"; }; });


16
投票

已有好的答案,但我只是想分享这个。

首先:Provider是创建myApp.factory('helloWorldFromFactory', function() { return { sayHello: function() { return "Hello, World!"; } }; }); (单例对象)的方式/配方,假设由$ injector注入(AngulaJS如何处理IoC模式)。

和价值,工厂,服务和常数(4种方式) - 提供者方式/配方的语法糖。

myApp.service('helloWorldFromService', function() { this.sayHello = function() { return "Hello, World!"; }; }); 部分已被覆盖:http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/

服务完全是关于service关键字实际上我们知道4件事:

  1. 创造全新的对象
  2. 将其链接到其Service vs Factory对象
  3. https://www.youtube.com/watch?v=BLzNCkPn3ao连接到new
  4. 并返回prototype

而Factory是关于工厂模式的 - 包含返回像该服务一样的对象的函数。

  1. 能否使用其他服务(具有依赖性)
  2. 服务初始化
  3. 延迟/延迟初始化

这个简单/短视频:还包括提供商:context(你看到它们可以看到他们如何从工厂到提供商)

在应用程序完全启动/初始化之前,提供程序配方主要在应用程序配置中使用。


16
投票

所有好的答案已经存在。我想在服务和工厂上增加几点。随着服务/工厂之间的差异。人们也可以有这样的问题:

  1. 我应该使用服务还是工厂?有什么不同?
  2. 他们是一样的还是有同样的行为?

让我们从服务和工厂之间的区别开始:

  1. 两者都是单身人士:每当Angular第一次将这些视为依赖时,它就会创建一个服务/工厂实例。创建实例后,将永久使用相同的实例。
  2. 可用于对具有行为的对象建模:它们都可以包含方法,内部状态变量等。虽然你编写代码的方式会有所不同。

服务:

服务是构造函数,Angular将通过调用new this来实例化它。这意味着一些事情。

  1. 函数和实例变量将是this的属性。
  2. 您不需要返回值。当Angular调用https://www.youtube.com/watch?v=HvTZbQ_hUZY时,它会收到yourServiceName()对象,其中包含您放置的所有属性。

示例示例:

this

当Angular将这个new yourServiceName(服务注入依赖于它的控制器时,该控制器将获得一个可以调用函数的this,例如: MyService.aServiceMethod()。

小心angular.service('MyService', function() { this.aServiceVariable = "Ved Prakash" this.aServiceMethod = function() { return //code }; });

由于构造的服务是一个对象,因此当它们被调用时,它内部的方法可以引用它:

MyService

你可能很想在一个承诺链中调用MyService,例如,如果你通过从服务器抓取它来初始化得分:this这样做的问题是angular.service('ScoreKeeper', function($http) { this.score = 0; this.getScore = function() { return this.score; }; this.setScore = function(newScore) { this.score = newScore; }; this.addOne = function() { this.score++; }; }); 将被调用ScoreKeeper.setScore绑定到$http.get('/score').then(ScoreKeeper.setScore).而你会得到错误。更好的方式是ScoreKeeper.setScore。无论您是否选择在服务方法中使用此方法,请小心如何调用它们。

this返回一个值:

由于JavaScript构造函数的工作方式,如果从null函数返回复数值$http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper)),调用者将获取该Object而不是此实例。

这意味着您基本上可以从下面复制粘贴工厂示例,将Service替换为(i.e., an Object),它将起作用:

constructor

因此,当Angular使用新的MyService()构造您的服务时,它将获得该api对象而不是MyService实例。

这是任何复杂值(对象,函数)的行为,但不适用于基本类型。

工厂:

工厂是一个返回值的普通旧函数。返回值是注入依赖于工厂的东西的东西。 Angular中的典型工厂模式是返回一个具有属性的对象,如下所示:

factory

工厂依赖项的注入值是工厂的返回值,它不必是对象。它可能是一种功能

以上1和2个问题的答案:

在大多数情况下,只需坚持使用工厂的一切。他们的行为更容易理解。没有选择是否返回一个值,而且如果你做错了就不会引入错误。

不过,当我谈到将它们作为依赖项注入时,我仍将它们称为“服务”。

服务/工厂行为非常相似,有些人会说任何一个都很好。这有点真实,但我发现更容易遵循John Papa的风格指南的建议,只是坚持工厂。**


13
投票

以下是我在AngularjS中作为对象工厂的代码模板提出的一些broilerplate代码。我用Car / CarFactory作为例子来说明。在控制器中实现简单的实现代码。

service

这是一个更简单的例子。我正在使用一些第三方库,它们期望“位置”对象暴露纬度和经度,但是通过不同的对象属性。我不想破解供应商代码,所以我调整了我传递的“位置”对象。

angular.service('MyService', function($http) {
  var api = {};

  api.aServiceMethod= function() {
    return $http.get('/users');
  };
  return api;
});

;


807
投票

JS Fiddle Demo

factory / service / provider的“Hello world”示例:

var myApp = angular.module('myApp', []);

//service style, probably the simplest one
myApp.service('helloWorldFromService', function() {
    this.sayHello = function() {
        return "Hello, World!";
    };
});

//factory style, more involved but more sophisticated
myApp.factory('helloWorldFromFactory', function() {
    return {
        sayHello: function() {
            return "Hello, World!";
        }
    };
});
    
//provider style, full blown, configurable version     
myApp.provider('helloWorld', function() {

    this.name = 'Default';

    this.$get = function() {
        var name = this.name;
        return {
            sayHello: function() {
                return "Hello, " + name + "!";
            }
        }
    };

    this.setName = function(name) {
        this.name = name;
    };
});

//hey, we can configure a provider!            
myApp.config(function(helloWorldProvider){
    helloWorldProvider.setName('World');
});
        

function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {
    
    $scope.hellos = [
        helloWorld.sayHello(),
        helloWorldFromFactory.sayHello(),
        helloWorldFromService.sayHello()];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
    {{hellos}}
</div>
</body>

13
投票

阅读完所有这些帖子后,它给我带来了更多的困惑..但仍然都是值得信息的......最后我发现下面的表格会给出简单的比较信息

  • 注入器使用配方创建两种类型的对象:服务和专用对象
  • 有五种配方类型定义了如何创建对象:Value,Factory,Service,Provider和Constant。
  • 工厂和服务是最常用的食谱。它们之间的唯一区别是Service配方更适合自定义类型的对象,而Factory可以生成JavaScript原语和函数。
  • 提供者配方是核心配方类型,所有其他配方只是语法糖。
  • 提供者是最复杂的配方类型。除非要构建需要全局配置的可重用代码,否则不需要它。
  • 除Controller外的所有特殊用途对象都是通过工厂配方定义的。

angular.factory('MyFactory', function($http) { var api = {}; api.aFactoryMethod= function() { return $http.get('/users'); }; return api; });

并且对于初学者的理解: - 这可能不正确的用例,但在高级别,这是这三个用例。

  1. 如果要在角度模块中使用配置功能,则应创建为提供者

     <script>
        angular.module('app', [])
            .factory('CarFactory', function() {

                /**
                 * BroilerPlate Object Instance Factory Definition / Example
                 */
                this.Car = function() {

                    // initialize instance properties
                    angular.extend(this, {
                        color           : null,
                        numberOfDoors   : null,
                        hasFancyRadio   : null,
                        hasLeatherSeats : null
                    });

                    // generic setter (with optional default value)
                    this.set = function(key, value, defaultValue, allowUndefined) {

                        // by default,
                        if (typeof allowUndefined === 'undefined') {
                            // we don't allow setter to accept "undefined" as a value
                            allowUndefined = false;
                        }
                        // if we do not allow undefined values, and..
                        if (!allowUndefined) {
                            // if an undefined value was passed in
                            if (value === undefined) {
                                // and a default value was specified
                                if (defaultValue !== undefined) {
                                    // use the specified default value
                                    value = defaultValue;
                                } else {
                                    // otherwise use the class.prototype.defaults value
                                    value = this.defaults[key];
                                } // end if/else
                            } // end if
                        } // end if

                        // update 
                        this[key] = value;

                        // return reference to this object (fluent)
                        return this;

                    }; // end this.set()

                }; // end this.Car class definition

                // instance properties default values
                this.Car.prototype.defaults = {
                    color: 'yellow',
                    numberOfDoors: 2,
                    hasLeatherSeats: null,
                    hasFancyRadio: false
                };

                // instance factory method / constructor
                this.Car.prototype.instance = function(params) {
                    return new 
                        this.constructor()
                                .set('color',           params.color)
                                .set('numberOfDoors',   params.numberOfDoors)
                                .set('hasFancyRadio',   params.hasFancyRadio)
                                .set('hasLeatherSeats', params.hasLeatherSeats)
                    ;
                };

                return new this.Car();

            }) // end Factory Definition
            .controller('testCtrl', function($scope, CarFactory) {

                window.testCtrl = $scope;

                // first car, is red, uses class default for:
                // numberOfDoors, and hasLeatherSeats
                $scope.car1     = CarFactory
                                    .instance({
                                        color: 'red'
                                    })
                                ;

                // second car, is blue, has 3 doors, 
                // uses class default for hasLeatherSeats
                $scope.car2     = CarFactory
                                    .instance({
                                        color: 'blue',
                                        numberOfDoors: 3
                                    })
                                ;
                // third car, has 4 doors, uses class default for 
                // color and hasLeatherSeats
                $scope.car3     = CarFactory
                                    .instance({
                                        numberOfDoors: 4
                                    })
                                ;
                // sets an undefined variable for 'hasFancyRadio',
                // explicitly defines "true" as default when value is undefined
                $scope.hasFancyRadio = undefined;
                $scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true);

                // fourth car, purple, 4 doors,
                // uses class default for hasLeatherSeats
                $scope.car4     = CarFactory
                                    .instance({
                                        color: 'purple',
                                        numberOfDoors: 4
                                    });
                // and then explicitly sets hasLeatherSeats to undefined
                $scope.hasLeatherSeats = undefined;
                $scope.car4.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true);

                // in console, type window.testCtrl to see the resulting objects

            });
    </script>
  1. Ajax呼叫或第三方集成需要服务。
  2. 对于数据操作,将其创建为工厂

对于基本方案,工厂和服务的行为相同。


12
投票

使用此页面和 angular.module('app') .factory('PositionFactory', function() { /** * BroilerPlate Object Instance Factory Definition / Example */ this.Position = function() { // initialize instance properties // (multiple properties to satisfy multiple external interface contracts) angular.extend(this, { lat : null, lon : null, latitude : null, longitude : null, coords: { latitude: null, longitude: null } }); this.setLatitude = function(latitude) { this.latitude = latitude; this.lat = latitude; this.coords.latitude = latitude; return this; }; this.setLongitude = function(longitude) { this.longitude = longitude; this.lon = longitude; this.coords.longitude = longitude; return this; }; }; // end class definition // instance factory method / constructor this.Position.prototype.instance = function(params) { return new this.constructor() .setLatitude(params.latitude) .setLongitude(params.longitude) ; }; return new this.Position(); }) // end Factory Definition .controller('testCtrl', function($scope, PositionFactory) { $scope.position1 = PositionFactory.instance({latitude: 39, longitude: 42.3123}); $scope.position2 = PositionFactory.instance({latitude: 39, longitude: 42.3333}); }) // end controller (自上次我看起来似乎有很大改进)作为参考,我将以下真实( - )世界演示放在一起,该演示使用了5种提供者中的4种;价值,恒定,工厂和全面的供应商。

HTML:

enter image description here

应用

angular.module('myApp').config(function($testProvider){
$testProvider.someFunction();
})

工作documentation


12
投票

这个答案解决了主题/问题

工厂,服务和常数 - 如何只是提供者食谱之上的语法糖?

要么

工厂,服务和提供商在内部是如何相似的

基本上会发生什么

当你制作一个<div ng-controller="mainCtrl as main"> <h1>{{main.title}}*</h1> <h2>{{main.strapline}}</h2> <p>Earn {{main.earn}} per click</p> <p>You've earned {{main.earned}} by clicking!</p> <button ng-click="main.handleClick()">Click me to earn</button> <small>* Not actual money</small> </div> 时它设置你提供者的var app = angular.module('angularProviders', []); // A CONSTANT is not going to change app.constant('range', 100); // A VALUE could change, but probably / typically doesn't app.value('title', 'Earn money by clicking'); app.value('strapline', 'Adventures in ng Providers'); // A simple FACTORY allows us to compute a value @ runtime. // Furthermore, it can have other dependencies injected into it such // as our range constant. app.factory('random', function randomFactory(range) { // Get a random number within the range defined in our CONSTANT return Math.random() * range; }); // A PROVIDER, must return a custom type which implements the functionality // provided by our service (see what I did there?). // Here we define the constructor for the custom type the PROVIDER below will // instantiate and return. var Money = function(locale) { // Depending on locale string set during config phase, we'll // use different symbols and positioning for any values we // need to display as currency this.settings = { uk: { front: true, currency: '£', thousand: ',', decimal: '.' }, eu: { front: false, currency: '€', thousand: '.', decimal: ',' } }; this.locale = locale; }; // Return a monetary value with currency symbol and placement, and decimal // and thousand delimiters according to the locale set in the config phase. Money.prototype.convertValue = function(value) { var settings = this.settings[this.locale], decimalIndex, converted; converted = this.addThousandSeparator(value.toFixed(2), settings.thousand); decimalIndex = converted.length - 3; converted = converted.substr(0, decimalIndex) + settings.decimal + converted.substr(decimalIndex + 1); converted = settings.front ? settings.currency + converted : converted + settings.currency; return converted; }; // Add supplied thousand separator to supplied value Money.prototype.addThousandSeparator = function(value, symbol) { return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, symbol); }; // PROVIDER is the core recipe type - VALUE, CONSTANT, SERVICE & FACTORY // are all effectively syntactic sugar built on top of the PROVIDER construct // One of the advantages of the PROVIDER is that we can configure it before the // application starts (see config below). app.provider('money', function MoneyProvider() { var locale; // Function called by the config to set up the provider this.setLocale = function(value) { locale = value; }; // All providers need to implement a $get method which returns // an instance of the custom class which constitutes the service this.$get = function moneyFactory() { return new Money(locale); }; }); // We can configure a PROVIDER on application initialisation. app.config(['moneyProvider', function(moneyProvider) { moneyProvider.setLocale('uk'); //moneyProvider.setLocale('eu'); }]); // The ubiquitous controller app.controller('mainCtrl', function($scope, title, strapline, random, money) { // Plain old VALUE(s) this.title = title; this.strapline = strapline; this.count = 0; // Compute values using our money provider this.earn = money.convertValue(random); // random is computed @ runtime this.earned = money.convertValue(0); this.handleClick = function() { this.count ++; this.earned = money.convertValue(random * this.count); }; }); 的第二个参数中提供的demo并返回它(factory()),你得到的只是function但是没有$getprovider(name, {$get:factoryFn })以外的属性/方法(意味着你无法配置它)

工厂的源代码

provider

制作$get时,它会返回你提供一个带有provider的factory(),它注入function factory(name, factoryFn, enforce) { return provider(name, { $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn }); }; (返回你在服务中提供的构造函数的实例)并返回它

源代码服务

service()

因此,基本上在这两种情况下,您最终都会将提供者$ get设置为您提供的函数,但是您可以提供除$ get以外的任何内容,因为您最初可以在provider()中为配置块提供


11
投票

我知道很多优秀的答案,但我必须分享我的使用经验 1. function适用于大多数违约案件 2. constructor用于创建特定实例的服务

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

和使用:

service

10
投票

派对迟到了。但我认为这对于想要学习(或明确)使用工厂,服务和提供商方法开发Angular JS Custom Services的人更有帮助。

我看到了这个视频,它清楚地解释了开发AngularJS Custom Services的工厂,服务和提供商方法:

factory

源代码:// factory.js //////////////////////////// (function() { 'use strict'; angular .module('myApp.services') .factory('xFactory', xFactoryImp); xFactoryImp.$inject = ['$http']; function xFactoryImp($http) { var fac = function (params) { this._params = params; // used for query params }; fac.prototype.nextPage = function () { var url = "/_prc"; $http.get(url, {params: this._params}).success(function(data){ ... } return fac; } })(); // service.js ////////////////////////// (function() { 'use strict'; angular .module('myApp.services') .service('xService', xServiceImp); xServiceImp.$inject = ['$http']; function xServiceImp($http) { this._params = {'model': 'account','mode': 'list'}; this.nextPage = function () { var url = "/_prc"; $http.get(url, {params: this._params}).success(function(data){ ... } } })();

此处发布的代码直接从上述来源复制,以使读者受益。

基于“工厂”的自定义服务的代码如下(与同步和异步版本以及调用http服务一起使用):

controller: ['xFactory', 'xService', function(xFactory, xService){

        // books = new instance of xFactory for query 'book' model
        var books = new xFactory({'model': 'book', 'mode': 'list'});

        // accounts = new instance of xFactory for query 'accounts' model
        var accounts = new xFactory({'model': 'account', 'mode': 'list'});

        // accounts2 = accounts variable
        var accounts2 = xService;
... 

自定义服务的“服务”方法代码(这与“工厂”非常相似,但与语法不同):

自定义服务的“提供者”方法的代码(如果您想开发可配置的服务,这是必要的):

最后,UI与上述任何服务一起使用:

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcFactory',
  function($scope, calcFactory) {
    $scope.a = 10;
    $scope.b = 20;

    $scope.doSum = function() {
      //$scope.sum = calcFactory.getSum($scope.a, $scope.b); //synchronous
      calcFactory.getSum($scope.a, $scope.b, function(r) { //aynchronous
        $scope.sum = r;
      });
    };

  }
]);

app.factory('calcFactory', ['$http', '$log',
  function($http, $log) {
    $log.log("instantiating calcFactory..");
    var oCalcService = {};

    //oCalcService.getSum = function(a,b){
    //	return parseInt(a) + parseInt(b);
    //};

    //oCalcService.getSum = function(a, b, cb){
    //	var s = parseInt(a) + parseInt(b);
    //	cb(s);
    //};

    oCalcService.getSum = function(a, b, cb) { //using http service

      $http({
        url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
        method: 'GET'
      }).then(function(resp) {
        $log.log(resp.data);
        cb(resp.data);
      }, function(resp) {
        $log.error("ERROR occurred");
      });
    };

    return oCalcService;
  }
]);

10
投票

只是为了澄清一些事情,从AngularJS源代码中,您可以看到服务只调用工厂函数,而工厂函数又调用提供者函数:

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.service('calcService', ['$http', '$log', function($http, $log){
	$log.log("instantiating calcService..");
	
	//this.getSum = function(a,b){
	//	return parseInt(a) + parseInt(b);
	//};

	//this.getSum = function(a, b, cb){
	//	var s = parseInt(a) + parseInt(b);
	//	cb(s);
	//};

	this.getSum = function(a, b, cb){
		$http({
			url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
			method: 'GET'
		}).then(function(resp){
			$log.log(resp.data);
			cb(resp.data);
		},function(resp){
			$log.error("ERROR occurred");
		});
	};

}]);

10
投票

基于内存目的,控制器仅在需要时才被实例化,而在不需要时被丢弃。因此,每次切换路径或重新加载页面时,Angular都会清理当前控制器。然而,服务提供了一种在应用程序的生命周期内保持数据的方法,同时它们也可以以一致的方式在不同的控制器上使用。

Angular为我们提供了三种创建和注册自己服务的方法。

1)工厂

2)服务

3)提供者

工厂:工厂是一个简单的功能,允许您在创建对象之前添加一些逻辑。它返回创建的对象。

它只是一个类的函数集合。因此,当您使用构造函数时,它可以在不同的控制器中实例化。

服务:服务是一个构造函数,它使用new关键字创建对象。您可以使用此关键字向服务对象添加属性和函数。与工厂不同,它不会返回任何东西。

它是一个单例对象。需要在整个应用程序中共享单个对象时使用它。例如,经过身份验证的用户详细信息

提供者:提供者用于创建可配置的服务对象。它使用$ get()函数返回值。

在服务对象可用之前,需要为服务对象提供模块化配置。

运行以下代码并查看输出。

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.provider('calcService', function(){

	var baseUrl = '';

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

	this.$get = ['$log', '$http', function($log, $http){
		$log.log("instantiating calcService...")
		var oCalcService = {};

		//oCalcService.getSum = function(a,b){
		//	return parseInt(a) + parseInt(b);
		//};

		//oCalcService.getSum = function(a, b, cb){
		//	var s = parseInt(a) + parseInt(b);
		//	cb(s);	
		//};

		oCalcService.getSum = function(a, b, cb){

			$http({
				url: baseUrl + '/Sum?a=' + a + '&b=' + b,
				method: 'GET'
			}).then(function(resp){
				$log.log(resp.data);
				cb(resp.data);
			},function(resp){
				$log.error("ERROR occurred");
			});
		};		

		return oCalcService;
	}];

});

app.config(['calcServiceProvider', function(calcServiceProvider){
	calcServiceProvider.config("http://localhost:4467");
}]);

9
投票

让我们以一种简单的方式讨论在AngularJS中处理业务逻辑的三种方式:(受Yaakov的Coursera AngularJS课程的启发)

服务:

句法:

app.js

<html>
<head>
	<title></title>
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js" ></script>
	<script type="text/javascript" src="t03.js"></script>
</head>
<body ng-app="app">
	<div ng-controller="emp">
		<div>
			Value of a is {{a}},
			but you can change
			<input type=text ng-model="a" /> <br>

			Value of b is {{b}},
			but you can change
			<input type=text ng-model="b" /> <br>

		</div>
		Sum = {{sum}}<br>
		<button ng-click="doSum()">Calculate</button>
	</div>
</body>
</html>

的index.html

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

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

服务特色:

  1. Lazily Instantiated:如果没有注入,它将不会被实例化。所以要使用它必须将它注入模块。
  2. 单例:如果注入多个模块,则所有模块都只能访问一个特定实例。这就是在不同控制器之间共享数据非常方便的原因。

首先让我们来看看语法:

app.js:

<!DOCTYPE html>
<html ng-app="app">
<head>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.min.js"></script>
    <meta charset=utf-8 />
    <title>JS Bin</title>
</head>
<body ng-controller="MyCtrl">
    {{serviceOutput}}
    <br/><br/>
    {{factoryOutput}}
    <br/><br/>
    {{providerOutput}}
    <script>
        var app = angular.module( 'app', [] );
        var MyFunc = function() {
            this.name = "default name";
            this.$get = function() {
                this.name = "new name"
                return "Hello from MyFunc.$get(). this.name = " + this.name;
            };
            return "Hello from MyFunc(). this.name = " + this.name;
        };
        // returns the actual function
        app.service( 'myService', MyFunc );
        // returns the function's return value
        app.factory( 'myFactory', MyFunc );
        // returns the output of the function's $get function
        app.provider( 'myProv', MyFunc );
        function MyCtrl( $scope, myService, myFactory, myProv ) {
            $scope.serviceOutput = "myService = " + myService;
            $scope.factoryOutput = "myFactory = " + myFactory;
            $scope.providerOutput = "myProvider = " + myProv;
        }
    </script>
</body>
</html>

现在在控制器中使用以上两个:

 var app = angular.module('ServiceExample',[]);
 var serviceExampleController =
              app.controller('ServiceExampleController', ServiceExampleController);
 var serviceExample = app.service('NameOfTheService', NameOfTheService);

 ServiceExampleController.$inject = ['NameOfTheService'] //protects from minification of js files

function ServiceExampleController(NameOfTheService){
     serviceExampleController = this;
     serviceExampleController.data = NameOfTheService.getSomeData();
 }

function NameOfTheService(){
     nameOfTheService = this;
     nameOfTheService.data = "Some Data";
     nameOfTheService.getSomeData = function(){
           return nameOfTheService.data;
     }     
}

工厂特点:

  1. 遵循工厂设计模式。工厂是生产新物体或功能的中心位置。
  2. 不仅可以生产单件,还可以定制服务。
  3. <div ng-controller = "ServiceExampleController as serviceExample"> {{serviceExample.data}} </div> 方法是一个始终生成相同类型服务的工厂,它是一个单独的服务,并且没有任何简单的方法来配置它的行为。 var app = angular.module('FactoryExample',[]); var factoryController = app.controller('FactoryController', FactoryController); var factoryExampleOne = app.factory('NameOfTheFactoryOne', NameOfTheFactoryOne); var factoryExampleTwo = app.factory('NameOfTheFactoryTwo', NameOfTheFactoryTwo); //first implementation where it returns a function function NameOfTheFactoryOne(){ var factory = function(){ return new SomeService(); } return factory; } //second implementation where an object literal would be returned function NameOfTheFactoryTwo(){ var factory = { getSomeService : function(){ return new SomeService(); } }; return factory; } 方法通常用作不需要任何配置的东西的快捷方式。

提供商

让我们再次看一下Syntax:

 var factoryOne = NameOfTheFactoryOne() //since it returns a function
 factoryOne.someMethod();

 var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object
 factoryTwo.someMethod();

提供者的特点:

  1. Provider是在Angular中创建服务的最灵活方法。
  2. 我们不仅可以创建一个可动态配置的工厂,而且在使用工厂时,使用提供程序方法,我们可以在整个应用程序的引导时自定义配置工厂一次。
  3. 然后,工厂可以在整个应用程序中使用自定义设置。换句话说,我们可以在应用程序启动之前配置此工厂。事实上,在角度文档中,提到了当我们使用.service().service()方法配置服务时,提供者方法实际上是在幕后执行的。
  4. angular.module('ProviderModule', []) .controller('ProviderModuleController', ProviderModuleController) .provider('ServiceProvider', ServiceProvider) .config(Config); //optional Config.$inject = ['ServiceProvider']; function Config(ServiceProvider) { ServiceProvider.defaults.maxItems = 10; //some default value } ProviderModuleController.$inject = ['ServiceProvider']; function ProviderModuleController(ServiceProvider) { //some methods } function ServiceProvider() { var provider = this; provider.defaults = { maxItems: 10 }; provider.$get = function () { var someList = new someListService(provider.defaults.maxItems); return someList; }; } } 是一个直接附加到提供程序实例的函数。该功能是工厂功能。换句话说,它就像我们用来为.service方法提供的那个。在该功能中,我们创建自己的服务。这个.factory属性是一个功能,它使提供者成为提供者。 AngularJS希望提供者有一个$ get属性,其值是Angular将其视为工厂函数的函数。但是,整个提供程序设置非常特殊的原因是我们可以在服务提供程序中提供一些$get对象,并且通常带有默认值,我们稍后可以在步骤中覆盖这些默认值,我们可以在其中配置整个应用程序。

7
投票

工厂:工厂实际上在工厂内创建一个对象并将其返回。 service:您只有一个标准函数的服务,它使用this关键字来定义函数。 provider:提供者有一个你定义的$ get,它可以用来获取返回数据的对象。


7
投票

从本质上讲,提供商,工厂和服务都是服务。当您需要的是$ get()函数时,Factory是服务的特例,允许您使用较少的代码编写它。

服务,工厂和供应商之间的主要区别在于其复杂性。服务是最简单的形式,工厂更健壮,供应商可在运行时配置。

以下是何时使用每个的摘要:

工厂:您提供的价值需要根据其他数据计算。

服务:您正在使用方法返回一个对象。

提供程序:您希望能够在配置阶段配置将在创建之前创建的对象。在应用程序完全初始化之前,主要在应用程序配置中使用提供程序。


635
投票

TL; DR 1)当您使用工厂时,您创建一个对象,向其添加属性,然后返回该对象。当您将此工厂传递到控制器时,该对象上的这些属性现在将通过您的工厂在该控制器中可用。

app.controller(‘myFactoryCtrl’, function($scope, myFactory){
  $scope.artist = myFactory.getArtist();
});

app.factory(‘myFactory’, function(){
  var _artist = ‘Shakira’;
  var service = {};

  service.getArtist = function(){
    return _artist;
  }

  return service;
});

2)当您使用Service时,AngularJS使用'new'关键字在幕后实例化它。因此,您将向'this'添加属性,服务将返回'this'。当您将服务传递到控制器时,“this”上的这些属性现在将通过您的服务在该控制器上可用。

app.controller(‘myServiceCtrl’, function($scope, myService){
  $scope.artist = myService.getArtist();
});

app.service(‘myService’, function(){
  var _artist = ‘Nelly’;
  this.getArtist = function(){
    return _artist;
  }
});

3)提供程序是您可以传递到.config()函数的唯一服务。如果要在服务对象可用之前为其提供模块范围的配置,请使用提供程序。

app.controller(‘myProvider’, function($scope, myProvider){
  $scope.artist = myProvider.getArtist();
  $scope.data.thingFromConfig = myProvider.thingOnConfig;
});

app.provider(‘myProvider’, function(){
 //Only the next two lines are available in the app.config()
 this._artist = ‘’;
 this.thingFromConfig = ‘’;
  this.$get = function(){
    var that = this;
    return {
      getArtist: function(){
        return that._artist;
      },
      thingOnConfig: that.thingFromConfig
    }
  }
});

app.config(function(myProviderProvider){
  myProviderProvider.thingFromConfig = ‘This was set in config’;
});

不是TL; DR

1)工厂 工厂是最流行的创建和配置服务的方式。 DR说的确没有比TL更多的东西。您只需创建一个对象,向其添加属性,然后返回该对象。然后,当您将工厂传递到控制器时,该对象上的这些属性现在将通过您的工厂在该控制器中可用。下面是一个更广泛的例子。

app.factory(‘myFactory’, function(){
  var service = {};
  return service;
});

现在,当我们将'myFactory'传递给我们的控制器时,我们可以使用我们附加到'service'的任何属性。

现在让我们在回调函数中添加一些“私有”变量。这些不能直接从控制器访问,但我们最终会在'service'上设置一些getter / setter方法,以便在需要时能够改变这些'private'变量。

app.factory(‘myFactory’, function($http, $q){
  var service = {};
  var baseUrl = ‘https://itunes.apple.com/search?term=’;
  var _artist = ‘’;
  var _finalUrl = ‘’;

  var makeUrl = function(){
   _artist = _artist.split(‘ ‘).join(‘+’);
    _finalUrl = baseUrl + _artist + ‘&callback=JSON_CALLBACK’;
    return _finalUrl
  }

  return service;
});

在这里你会注意到我们没有将这些变量/函数附加到'service'。我们只是创建它们以便以后使用或修改它们。

  • baseUrl是iTunes API所需的基本URL
  • _artist是我们希望查找的艺术家
  • _finalUrl是我们将调用iTunes的最终完全构建的URL
  • makeUrl是一个创建并返回我们的iTunes友好URL的函数。

现在我们的助手/私有变量和函数已经到位,让我们为'service'对象添加一些属性。无论我们提供什么'服务'都可以直接在我们通过'myFactory'的控制器中使用。

我们将创建setArtist和getArtist方法,只返回或设置艺术家。我们还将创建一个方法,使用我们创建的URL调用iTunes API。一旦数据从iTunes API返回,此方法将返回一个承诺。如果您在AngularJS中没有多少使用承诺的经验,我强烈建议您深入了解它们。

以下setArtist接受艺术家并允许您设置艺术家。 getArtist返回艺术家。 callItunes首先调用makeUrl()以构建我们将与$ http请求一起使用的URL。然后它设置一个promise对象,用我们的最终url发出$ http请求,然后因为$ http返回一个promise,我们可以在我们的请求之后调用.success或.error。然后我们使用iTunes数据解决我们的承诺,或者我们拒绝它并显示“有错误”的消息。

app.factory('myFactory', function($http, $q){
  var service = {};
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  service.setArtist = function(artist){
    _artist = artist;
  }

  service.getArtist = function(){
    return _artist;
  }

  service.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

  return service;
});

现在我们的工厂已经完工。我们现在能够将'myFactory'注入任何控制器,然后我们就可以调用附加到服务对象(setArtist,getArtist和callItunes)的方法。

app.controller('myFactoryCtrl', function($scope, myFactory){
  $scope.data = {};
  $scope.updateArtist = function(){
    myFactory.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myFactory.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

在上面的控制器中,我们注入了'myFactory'服务。然后,我们使用'myFactory'中的数据在$ scope对象上设置属性。上面唯一棘手的代码是,如果你以前从未处理过承诺。因为callItunes正在返回一个promise,所以我们可以使用.then()方法,只有在我们的承诺与iTunes数据一起完成后才设置$ scope.data.artistData。您会注意到我们的控制器非常“薄”(这是一个很好的编码实践)。我们所有的逻辑和持久数据都位于我们的服务中,而不是我们的控制器中。 2)服务 在处理创建服务时,最重要的事情可能是它使用'new'关键字进行实例化。对于JavaScript JavaScript专家来说,这应该会给你一个关于代码本质的一个很大的暗示。对于那些JavaScript背景有限的人或那些不太熟悉'new'关键字实际工作的人,让我们回顾一下最终有助于我们理解服务性质的JavaScript基础知识。

要真正看到使用'new'关键字调用函数时发生的更改,让我们创建一个函数并使用'new'关键字调用它,然后让我们看看解释器在看到'new'关键字时的作用。最终结果将是相同的。

首先让我们创建我的构造函数。

var Person = function(name, age){
  this.name = name;
  this.age = age;
}

这是一个典型的JavaScript构造函数。现在每当我们使用'new'关键字调用Person函数时,'this'将绑定到新创建的对象。

现在让我们在Person的原型上添加一个方法,这样它就可以在Person'类'的每个实例上使用。

Person.prototype.sayName = function(){
  alert(‘My name is ‘ + this.name);
}

现在,因为我们将sayName函数放在原型上,所以Person的每个实例都能够调用sayName函数,以便提示实例的名称。

现在我们在其原型上有了Person构造函数和sayName函数,让我们实际创建Person的实例然后调用sayName函数。

var tyler = new Person(‘Tyler’, 23);
tyler.sayName(); //alerts ‘My name is Tyler’

因此,创建Person构造函数的代码,向其原型添加函数,创建Person实例,然后在其原型上调用函数就像这样。

var Person = function(name, age){
  this.name = name;
  this.age = age;
}
Person.prototype.sayName = function(){
  alert(‘My name is ‘ + this.name);
}
var tyler = new Person(‘Tyler’, 23);
tyler.sayName(); //alerts ‘My name is Tyler’

现在让我们看一下在JavaScript中使用'new'关键字时实际发生的情况。你应该注意的第一件事是在我们的例子中使用'new'后,我们能够在'tyler'上调用一个方法(sayName),就像它是一个对象一样 - 那是因为它是。首先,我们知道我们的Person构造函数正在返回一个对象,我们是否可以在代码中看到它。其次,我们知道因为我们的sayName函数位于原型而不是直接位于Person实例上,所以Person函数返回的对象必须在失败的查找中委托给它的原型。换句话说,当我们调用tyler.sayName()时,解释器说“好了,我将查看我们刚创建的'tyler'对象,找到sayName函数,然后调用它。等一下,我在这里看不到 - 我只看到名字和年龄,让我查看原型。是的,看起来像是在原型上,让我称之为。“

下面是您如何思考'new'关键字在JavaScript中实际执行的操作的代码。它基本上是上一段的代码示例。我已经把'解释器视图'或解释器看到注释中的代码的方式。

var Person = function(name, age){
  //The below line creates an object(obj) that will delegate to the person’s prototype on failed lookups.
  //var obj = Object.create(Person.prototype);

  //The line directly below this sets ‘this’ to the newly created object
  //this = obj;

  this.name = name;
  this.age = age;

  //return this;
}

现在了解'new'关键字在JavaScript中的实际功能,在AngularJS中创建服务应该更容易理解。

创建服务时要了解的最重要的事情是知道服务是使用'new'关键字实例化的。将这些知识与上面的示例相结合,您现在应该认识到您将直接将属性和方法附加到'this',然后从服务本身返回。我们来看看这个实际情况。

与我们最初对Factory示例所做的不同,我们不需要创建对象然后返回该对象,因为像之前多次提到的那样,我们使用了'new'关键字,因此解释器将创建该对象,让它委托给它是原型,然后在没有我们完成工作的情况下将它归还给我们。

首先,让我们创建我们的'私人'和帮助函数。这应该看起来非常熟悉,因为我们对我们的工厂做了同样的事情。我不会解释每一行在这里的作用,因为我在工厂示例中这样做,如果您感到困惑,请重新阅读工厂示例。

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }
});

现在,我们将把我们控制器中可用的所有方法附加到'this'。

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  this.setArtist = function(artist){
    _artist = artist;
  }

  this.getArtist = function(){
    return _artist;
  }

  this.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

});

现在就像在我们的工厂一样,setArtist,getArtist和callItunes将在我们传递myService的任何控制器中可用。这是myService控制器(几乎与我们的工厂控制器完全相同)。

app.controller('myServiceCtrl', function($scope, myService){
  $scope.data = {};
  $scope.updateArtist = function(){
    myService.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myService.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

就像我之前提到的,一旦你真正理解了“新”的含义,服务几乎与AngularJS中的工厂相同。 3)提供者

关于Providers最重要的事情是,它们是您可以传递到应用程序的app.config部分的唯一服务。如果您需要更改服务对象的某些部分,然后在应用程序中的其他任何位置可用,那么这一点非常重要。虽然与服务/工厂非常相似,但我们将讨论一些差异。

首先,我们以与服务和工厂类似的方式设置我们的提供商。下面的变量是我们的“私人”和帮助函数。

app.provider('myProvider', function(){
   var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  //Going to set this property on the config function below.
  this.thingFromConfig = ‘’;

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }
}

*再次如果上述代码的任何部分令人困惑,请查看工厂部分,在那里我将解释它所做的更多细节。

您可以将提供商视为包含三个部分。第一部分是稍后将修改/设置的“私有”变量/函数(如上所示)。第二部分是app.config函数中可用的变量/函数,因此可以在其他任何地方可用之前进行更改(如上所示)。重要的是要注意这些变量需要附加到'this'关键字。在我们的示例中,只有'thingFromConfig'可用于在app.config中进行更改。第三部分(如下所示)是将“myProvider”服务传递到特定控制器时控制器中可用的所有变量/函数。

使用Provider创建服务时,控制器中唯一可用的属性/方法是从$ get()函数返回的属性/方法。下面的代码将$ get放在'this'上(我们知道最终将从该函数返回)。现在,$ get函数返回我们希望在控制器中可用的所有方法/属性。这是一个代码示例。

this.$get = function($http, $q){
    return {
      callItunes: function(){
        makeUrl();
        var deferred = $q.defer();
        $http({
          method: 'JSONP',
          url: _finalUrl
        }).success(function(data){
          deferred.resolve(data);
        }).error(function(){
          deferred.reject('There was an error')
        })
        return deferred.promise;
      },
      setArtist: function(artist){
        _artist = artist;
      },
      getArtist: function(){
        return _artist;
      },
      thingOnConfig: this.thingFromConfig
    }
  }

现在完整的Provider代码看起来像这样

app.provider('myProvider', function(){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  //Going to set this property on the config function below
  this.thingFromConfig = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  this.$get = function($http, $q){
    return {
      callItunes: function(){
        makeUrl();
        var deferred = $q.defer();
        $http({
          method: 'JSONP',
          url: _finalUrl
        }).success(function(data){
          deferred.resolve(data);
        }).error(function(){
          deferred.reject('There was an error')
        })
        return deferred.promise;
      },
      setArtist: function(artist){
        _artist = artist;
      },
      getArtist: function(){
        return _artist;
      },
      thingOnConfig: this.thingFromConfig
    }
  }
});

就像在我们的工厂和服务中一样,setArtist,getArtist和callItunes将在我们通过myProvider的控制器中提供。这是myProvider控制器(几乎与我们的工厂/服务控制器完全相同)。

app.controller('myProviderCtrl', function($scope, myProvider){
  $scope.data = {};
  $scope.updateArtist = function(){
    myProvider.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myProvider.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }

  $scope.data.thingFromConfig = myProvider.thingOnConfig;
});

如前所述,使用Provider创建服务的重点是能够在将最终对象传递给应用程序的其余部分之前通过app.config函数更改某些变量。让我们看一个例子。

app.config(function(myProviderProvider){
  //Providers are the only service you can pass into app.config
  myProviderProvider.thingFromConfig = 'This sentence was set in app.config. Providers are the only service that can be passed into config. Check out the code to see how it works';
});

现在你可以看到'thingFromConfig'在我们的提供者中是如何作为空字符串,但当它出现在DOM中时,它将是'这句话被设置......'。


4
投票

1.服务是在必要时创建的单件对象,在应用程序生命周期结束之前(浏览器关闭时)永远不会被清除。控制器在不再需要时被销毁和清理。

2.创建服务的最简单方法是使用factory()方法。 factory()方法允许我们通过返回包含服务功能和服务数据的对象来定义服务。服务定义函数是我们放置可注射服务的地方,例如$ http和$ q。例如:

.factory

在我们的应用程序中使用factory()

在我们的应用程序中使用工厂很容易,因为我们可以在运行时将它注入我们需要的地方。

$get
  1. 另一方面,service()方法允许我们通过定义构造函数来创建服务。我们可以使用原型对象来定义我们的服务,而不是原始的javascript对象。与factory()方法类似,我们还将在函数定义中设置injectables。
  2. 创建服务的最低级别方法是使用provide()方法。这是创建我们可以使用.config()函数配置的服务的唯一方法。与前面的方法不同,我们将在一个定义的。$ get()函数定义中设置注入。

509
投票

所有服务都是单身人士;他们每个应用程序实例化一次。它们可以是任何类型,无论是原始类型,对象文字,函数,还是自定义类型的实例。

valuefactoryserviceconstantprovider方法都是提供者。他们教Injector如何实例化服务。

最详细,但也是最全面的是提供者食谱。剩下的四种食谱类型 - 价值,工厂,服务和常数 - 只是提供者食谱之上的语法糖。

  • Value Recipe是最简单的情况,您可以自己实例化服务并向注入器提供实例化值。
  • Factory配方为Injector提供了一个工厂函数,在需要实例化服务时调用它。调用时,工厂函数创建并返回服务实例。服务的依赖关系作为函数的参数注入。因此,使用此配方可添加以下功能: 能够使用其他服务(具有依赖性) 服务初始化 延迟/延迟初始化
  • 服务配方几乎与工厂配方相同,但这里Injector使用new运算符而不是工厂函数调用构造函数。
  • 提供者食谱通常是矫枉过正的。它允许您配置工厂的创建,从而增加了一层间接。 仅当您要为应用程序范围的配置公开API时才应使用Provider配方,该API必须在应用程序启动之前进行。这通常仅适用于可重用服务,其行为可能需要在应用程序之间略有不同。
  • Constant配方就像Value配方一样,除了它允许您定义配置阶段中可用的服务。比使用Value配方创建的服务早。与Values不同,它们不能使用decorator进行装饰。
See the provider documentation.

222
投票

了解AngularJS工厂,服务和提供商

所有这些都用于共享可重用的单例对象。它有助于在您的应用程序/各种组件/模块之间共享可重用的代码。

来自Docs Service/Factory

  • 懒惰地实例化 - 当应用程序组件依赖它时,Angular仅实例化服务/工厂。
  • 单身人士 - 依赖于服务的每个组件都会获得对服务工厂生成的单个实例的引用。

工厂是在创建对象之前可以操作/添加逻辑的函数,然后返回新创建的对象。

app.factory('MyFactory', function() {
    var serviceObj = {};
    //creating an object with methods/functions or variables
    serviceObj.myFunction = function() {
        //TO DO:
    };
    //return that object
    return serviceObj;
});

用法

它可以只是一个类的函数集合。因此,当您在控制器/工厂/指令功能中注入它时,它可以在不同的控制器中实例化。每个应用程序仅实例化一次。

服务

只需在查看服务时就可以考虑阵列原型。服务是使用“new”关键字实例化新对象的函数。您可以使用this关键字向服务对象添加属性和函数。与工厂不同,它不返回任何内容(它返回包含方法/属性的对象)。

app.service('MyService', function() {
    //directly binding events to this context
    this.myServiceFunction = function() {
        //TO DO:
    };
});

用法

需要在整个应用程序中共享单个对象时使用它。例如,经过身份验证的用户详细信息,可共享的方法/数据,实用程序功能等。

提供商

提供程序用于创建可配置的服务对象。您可以从配置功能配置服务设置。它使用$get()函数返回一个值。 $get函数在角度运行阶段执行。

app.provider('configurableService', function() {
    var name = '';
    //this method can be be available at configuration time inside app.config.
    this.setName = function(newName) {
        name = newName;
    };
    this.$get = function() {
        var getName = function() {
             return name;
        };
        return {
            getName: getName //exposed object to where it gets injected.
        };
    };
});

用法

当您需要在服务对象可用之前为其提供模块化配置时,例如。假设你想根据你的环境设置你的API URL,如devstageprod

注意

只有提供商将在角度配置阶段提供,而服务和工厂则不提供。

希望这已经清除了您对工厂,服务和提供商的理解。


191
投票

对我而言,当我意识到它们都以相同的方式工作时,就会出现这样的启示:通过运行一次,存储它们获得的值,然后在通过dependency injection引用时咳出相同的存储值。

说我们有:

app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);

三者之间的区别在于:

  1. a的储值来自于运行fn
  2. b的储值来自newing fn
  3. c的存储值来自于首先通过newing fn获取实例,然后运行实例的$get方法。

这意味着在AngularJS中有类似缓存对象的东西,每次注入的值只分配一次,当它们第一次注入时,其中:

cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()

这就是为什么我们在服务中使用this,并在提供者中定义this.$get


135
投票

服务与提供商对工厂:

我想保持简单。这都是关于基本的JavaScript概念。

首先,我们来谈谈AngularJS中的服务!

什么是服务:在AngularJS中,Service只是一个单独的JavaScript对象,它可以存储一些有用的方法或属性。此单例对象是基于ngApp(Angular app)创建的,并且在当前应用程序中的所有控制器之间共享。当Angularjs实例化服务对象时,它会使用唯一的服务名称注册此服务对象。因此,每当我们需要服务实例时,Angular会在注册表中搜索此服务名称,并返回对服务对象的引用。这样我们就可以在服务对象上调用方法,访问属性等。您可能有疑问是否也可以在控制器的范围对象上放置属性,方法!那你为什么需要服务对象呢?答案是:服务在多个控制器范围之间共享。如果将某些属性/方法放在控制器的作用域对象中,则它仅可用于当前作用域。但是,当您在服务对象上定义方法,属性时,它将全局可用,并且可以通过注入该服务在任何控制器的范围内访问。

因此,如果有三个控制器范围,让它成为控制器,控制器和控制器,所有将共享相同的服务实例。

<div ng-controller='controllerA'>
    <!-- controllerA scope -->
</div>
<div ng-controller='controllerB'>
    <!-- controllerB scope -->
</div>
<div ng-controller='controllerC'>
    <!-- controllerC scope -->
</div>

如何创建服务?

AngularJS提供了不同的方法来注册服务。这里我们将集中讨论三个方法工厂(..),服务(..),提供者(..);

Use this link for code reference

工厂功能:

我们可以定义一个工厂函数,如下所示。

factory('serviceName',function fnFactory(){ return serviceInstance;})

AngularJS提供'factory('serviceName',fnFactory)'方法,它接受两个参数,serviceName和JavaScript函数。 Angular通过调用函数fnFactory()创建服务实例,如下所示。

var serviceInstace = fnFactory();

传递的函数可以定义一个对象并返回该对象。 AngularJS只是将此对象引用存储到作为第一个参数传递的变量中。从fnFactory返回的任何内容都将绑定到serviceInstance。我们还可以返回函数,值等,而不是返回对象,无论我们将返回什么,都将可用于服务实例。

例:

var app= angular.module('myApp', []);
//creating service using factory method
app.factory('factoryPattern',function(){
  var data={
    'firstName':'Tom',
    'lastName':' Cruise',
    greet: function(){
      console.log('hello!' + this.firstName + this.lastName);
    }
  };

  //Now all the properties and methods of data object will be available in our service object
  return data;
});

服务功能:

service('serviceName',function fnServiceConstructor(){})

这是另一种方式,我们可以注册一项服务。唯一的区别是AngularJS尝试实例化服务对象的方式。这一次angular使用'new'关键字并调用构造函数,如下所示。

var serviceInstance = new fnServiceConstructor();

在构造函数中,我们可以使用'this'关键字向服务对象添加属性/方法。例:

//Creating a service using the service method
var app= angular.module('myApp', []);
app.service('servicePattern',function(){
  this.firstName ='James';
  this.lastName =' Bond';
  this.greet = function(){
    console.log('My Name is '+ this.firstName + this.lastName);
  };
});

提供者功能:

Provider()函数是创建服务的另一种方式。让我们有兴趣创建一个只向用户显示一些问候消息的服务。但我们还希望提供一种功能,以便用户可以设置自己的问候消息。在技​​术方面,我们希望创建可配置的服务。我们应该怎么做 ?必须有一种方法,以便app可以传递他们的自定义问候消息,Angularjs将使其可用于创建我们的服务实例的工厂/构造函数。在这种情况下,provider()函数可以完成这项工作。使用provider()函数我们可以创建可配置的服务。

我们可以使用提供程序语法创建可配置服务,如下所示。

/*step1:define a service */
app.provider('service',function serviceProviderConstructor(){});

/*step2:configure the service */
app.config(function configureService(serviceProvider){});

提供者语法内部如何工作?

1.Provider对象是使用我们在provider函数中定义的构造函数创建的。

var serviceProvider = new serviceProviderConstructor();

2.我们在app.config()中传递的函数,被执行。这称为配置阶段,在这里我们有机会定制我们的服务。

configureService(serviceProvider);

3.通过调用serviceProvider的$ get方法创建最终服务实例。

serviceInstance = serviceProvider.$get()

使用提供语法创建服务的示例代码:

var app= angular.module('myApp', []);
app.provider('providerPattern',function providerConstructor(){
  //this function works as constructor function for provider
  this.firstName = 'Arnold ';
  this.lastName = ' Schwarzenegger' ;
  this.greetMessage = ' Welcome, This is default Greeting Message' ;
  //adding some method which we can call in app.config() function
  this.setGreetMsg = function(msg){
    if(msg){
      this.greetMessage =  msg ;
    }
  };

  //We can also add a method which can change firstName and lastName
  this.$get = function(){
    var firstName = this.firstName;
    var lastName = this.lastName ;
    var greetMessage = this.greetMessage;
    var data={
       greet: function(){
         console.log('hello, ' + firstName + lastName+'! '+ greetMessage);
       }
    };
    return data ;
  };
});

app.config(
  function(providerPatternProvider){
    providerPatternProvider.setGreetMsg(' How do you do ?');
  }
);

Working Demo

摘要:


工厂使用返回服务实例的工厂函数。 serviceInstance = fnFactory();

Service使用构造函数,Angular使用'new'关键字调用此构造函数来创建服务实例。 serviceInstance = new fnServiceConstructor();

Provider定义了一个providerConstructor函数,这个providerConstructor函数定义了一个工厂函数$ get。 Angular调用$ get()来创建服务对象。提供程序语法具有在实例化之前配置服务对象的附加优势。 serviceInstance = $ get();


84
投票

正如几个人在这里正确指出的那样,工厂,提供商,服务,甚至价值和常数都是同一件事的版本。你可以将更一般的provider解剖到所有这些中。像这样:

enter image description here

这是这张图片来自的文章:

http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/


62
投票

Factory

您为AngularJS提供了一个函数,AngularJS将在请求工厂时缓存并注入返回值。

例:

app.factory('factory', function() {
    var name = '';
    // Return value **is** the object that will be injected
    return {
        name: name;
    }
})

用法:

app.controller('ctrl', function($scope, factory) {
     $scope.name = factory.name;
});

Service

你给AngularJS一个函数,AngularJS会调用new来实例化它。这是AngularJS创建的实例,它将在请求服务时进行缓存和注入。由于new用于实例化服务,因此关键字this有效并引用实例。

例:

app.service('service', function() {
     var name = '';
     this.setName = function(newName) {
         name = newName;
     }
     this.getName = function() {
         return name;
     }
});

用法:

app.controller('ctrl', function($scope, service) {
   $scope.name = service.getName();
});

Provider

你给AngularJS一个函数,AngularJS将调用它的$get函数。它是$get函数的返回值,它将在请求服务时进行缓存和注入。

提供者允许您在AngularJS调用$get方法以获取可注入之前配置提供者。

例:

app.provider('provider', function() {
     var name = '';
     this.setName = function(newName) {
          name = newName;
     }
     this.$get = function() {
         return {
            name: name
         }
     }
})

用法(作为控制器中的注射剂)

app.controller('ctrl', function($scope, provider) {
    $scope.name = provider.name;
});

用法(在调用$get之前配置提供程序以创建注入表)

app.config(function(providerProvider) {
    providerProvider.setName('John');
});
© www.soinside.com 2019 - 2024. All rights reserved.