Питання AngularJS: служба проти постачальника проти фабрики


Які відмінності між a Service, Provider і Factory в AngularJS?


3167
2018-03-27 17:59


походження


Я виявив, що всі "Углові" умови залякують початківців. Ми почали працювати з цим cheatsheet, яке було трохи легше для наших програмістів, щоб зрозуміти під час навчання Angular demisx.github.io/angularjs/2014/09/14/.... Сподіваюсь, це також допоможе вашою команді. - demisx
На мій погляд, найкращим способом зрозуміти різницю є використання власної документації Angular: docs.angularjs.org/guide/providers це дуже добре пояснено і використовує своєрідний приклад, який допоможе вам зрозуміти це. - Rafael Merlin
@Blaise Спасибо! На мій коментар до публікації я навмисно залишив це, оскільки 99% випадків використання мого досвіду можна успішно обробляти через service.factory. Не хотів ще більше ускладнювати цей предмет. - demisx
Я вважаю цю дискусію дуже корисною stackoverflow.com/questions/18939709/... - Anand Gupta
Ось кілька хороших відповідей про те, як services, factories і providers працює - Mistalis


Відповіді:


З списку розсилки AngularJS я отримав дивовижний потік що пояснює службу проти фабрики та провайдера та їхнє вживання. Складання відповідей:

Послуги

Синтаксис: module.service( 'serviceName', function ); 
Результат: При оголошенні serviceName як ін'єкційний аргумент вам буде надано екземпляр функції. Іншими словами  new FunctionYouPassedToService().

Фабрики

Синтаксис: module.factory( 'factoryName', function ); 
Результат: оголошуючи ім'я фабрики як ін'єкційний аргумент, вам буде надано значення, яке повертається за допомогою посилання функції, переданого модулю. factory.

Провайдери

Синтаксис: module.provider( 'providerName', function ); 
Результат: При оголошенні ім'я провайдера як ін'єкційний аргумент вам буде забезпечено  (new ProviderFunction()).$get(). Функція конструктора інвенціонує перед тим, як викликати метод $ get - ProviderFunction це посилання на функцію, передане модулю. Постачальник.

Провайдери мають перевагу, що вони можуть бути налаштовані під час фази конфігурації модуля.

Побачити тут за наданий код.

Ось чудове додаткове пояснення Міско:

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 це функція, яка відповідає за створення цінності. Зверніть увагу, що функція заводу може вимагати інші залежності.

Але що, якщо ви хочете бути більш О.О. і мати клас під назвою Звітник?

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

Потім, щоб показати, вам доведеться писати

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

Тоді ми могли б попросити "вітання" в контролер, як це

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');
}

Як зауваження, service, factory, і value всі вони отримані від провайдера.

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;
  });
};

2803
2017-07-30 10:20



Дивись також stackoverflow.com/a/13763886/215945 в якому розглядаються відмінності між сервісним обслуговуванням і фабрикою. - Mark Rajcok
У редакції 611 я додавав використання кутових констант і значень. Продемонструвати відмінності вже показаних. jsbin.com/ohamub/611/edit - Nick
Хоча сервіс викликається, створюючи екземпляр функції. Це насправді створюється лише один раз за інжектор, який робить його як синглтон.docs.angularjs.org/guide/dev_guide.services.creating_services - angelokh
Цей приклад може бути неймовірним, якщо він використовує чіткий практичний приклад. Я заблукаю, намагаюся з'ясувати, наскільки важливо toEqual і greeter.Greet є Чому б не використати щось дещо більш реальне та відповідне? - Kyle Pennell
Використання функції expect () - поганий вибір, щоб щось пояснити. Використовуйте код реального світу наступного разу. - Craig


JS Fiddle Demo

Приклад "Привіт світ" з factory / service / provider:

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>


797
2018-05-15 15:53



Ні this змінити контекст у $get функція - Ви більше не звертаєтесь до екземплярного провайдера в цій функції. - Nate-Wilkins
@ Nate: this фактично не змінює контекст, тому що те, що називається, є new Provider(). $ get (), де Provider це функція, яка передається app.provider. Це потрібно сказати так $get() називається методом на побудований Provider, так this буде посилатися на Provider як наводить приклад. - Brandon
@ Брэндон О, добре, це все добре, ніж тоді. Змішаний на перший погляд - спасибі за роз'яснення! - Nate-Wilkins
Чому я отримую Unknown provider: helloWorldProvider <- helloWorld при запуску цього локально? Коментуючи це, та сама помилка для інших 2 прикладів. Чи є якась прихована конфігурація постачальника? (Кутова 1.0.8) - Знайдено: stackoverflow.com/questions/12339272/... - Antoine
Причина, чому @Antoine отримує помилку "Невідомий надавати: helloWorldProvider", оскільки в коді .config ви використовуєте "helloWorldProvider", але коли ви визначаєте постачальника в myApp.provider ('helloWorld', function ()), ви використовуєте 'Привіт Світ'? Іншими словами, у вашому конфігураційному коді, як кутовий знає, що ви маєте на увазі постачальника helloWorld? Дякую - jmtoung


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) Коли ви використовуєте Сервіс, AngularJS демонструє його за кадром за допомогою "нового" ключового слова. Через це ви додаєте властивості "цьому", і служба поверне "це". Коли ви передаєте службу в свій контролер, ці властивості на "це" тепер будуть доступні на цьому контролері через вашу послугу.

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) Заводський 
Фабрики є найпопулярнішим способом створення та налаштування сервісу. Там насправді не набагато більше, ніж те, що Т.Л., сказав Д.Р. Ви просто створюєте об'єкт, додавайте його властивості, потім повертаєте той самий об'єкт. Тоді, коли ви передаєте фабрику в свій контролер, ці властивості на об'єкті тепер будуть доступні в цьому контролері через ваш завод. Більш широкий приклад нижче.

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

Тепер будь-які властивості, які ми надаємо "службі", будуть доступні нам, коли ми передаємо "myFactory" у наш контролер.

Тепер давайте додамо деякі "приватні" змінні до нашої функції зворотного виклику. Вони не будуть доступні безпосередньо від контролера, але ми, в кінцевому підсумку, встановимо деякі методи отримання / встановлення на "службі", щоб мати можливість змінювати ці "приватні" змінні, коли це необхідно.

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;
});

Тут ви помітите, що ми не додаємо ці змінні / функцію до "служби". Ми просто створюємо їх, щоб або використовувати їх, або змінювати їх пізніше.

  • baseUrl є базовою URL-адресою, яка вимагає API iTunes
  • _artist - це художник, який ми хочемо шукати
  • _finalUrl є остаточним і повністю побудованим URL-адресою, на який ми зателефонуємо iTunes
  • makeUrl - це функція, яка створить і поверне наш дружній URL-адресу iTunes.

Тепер, коли наші допоміжні / приватні змінні та функція є на місці, давайте додамо деякі властивості до об'єкта "service". Що б ми не поставили на "службу", можна безпосередньо використати всередині будь-якого контролера, який ми передаємо "myFactory".

Ми збираємося створити методи setArtist і getArtist, які просто повертають або встановлюють виконавця. Ми також збираємося створити метод, який закликає iTunes API до нашої створеної URL-адреси. Цей метод поверне обіцянку, яка буде виконана після повернення даних з API iTunes. Якщо у вас не було багато досвіду використання обіцянок в AngularJS, я настійно рекомендую робити глибокий пірнати на них.

Нижче setArtist приймає художника і дозволяє встановити виконавця. getArtist повертає художника. call itunes Перші дзвінки makeUrl (), щоб створити URL-адресу, яку ми будемо використовувати за допомогою нашого $ http-запиту. Потім він встановлює обіцяний об'єкт, робить запит $ HTF на нашу остаточну URL-адресу, а тому, що $ HTY повертає обіцянку, після нашого запиту ми можемо викликати .success або .roror. Тоді ми вирішуємо нашу обіцянку з даними 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". Потім ми встановлюємо властивості на об'єкті $ scope з даними з "myFactory". Єдиний складний код вище, якщо ви ніколи раніше не виконували обіцянки. Оскільки call in iTunes повертає обіцянку, ми можемо використовувати метод .then (), і лише встановлюємо $ scope.data.artistData коли наші обіцянки виконуються з даними iTunes. Ви помітите, що наш контролер дуже "тонкий" (це хороша практика кодування). Всі наші логічні та постійні дані знаходяться в нашому сервісі, а не в нашому контролері.

2) Сервіс 
Можливо, найбільше, що потрібно знати при створенні сервісу, це те, що воно вказується на "нове" ключове слово. Для вас гуру JavaScript це повинно дати вам великий натяк на природу коду. Для тих з вас, хто має обмежений фон в JavaScript або для тих, хто не дуже знайомий з тим, що дійсно працює "нове" ключове слово, давайте розглянемо деякі основи JavaScript, які в кінцевому підсумку допоможуть нам зрозуміти характер Служби.

Щоб дійсно побачити зміни, що виникають при виклику функції з "новим" ключовим словом, давайте створимо функцію та зателефонуємо йому за допомогою "нового" ключового слова, а потім покажемо, що робить інтерпретатор, коли знаходить "нове" ключове слово. Кінцеві результати будуть однаковими.

Спочатку створіть наш Конструктор.

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

Це типова функція конструктора JavaScript. Тепер, коли ми запускаємо функцію "Person" за допомогою "нового" ключового слова, "це" буде пов'язано з новоствореним об'єктом.

Тепер давайте додамо метод до прототипу нашої людини, щоб він був доступний на кожному прикладі класу нашої людини.

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

Тепер, оскільки ми поставили функцію sayName на прототип, кожен примірник особи зможе викликати функцію sayName, щоб повідомити про це ім'я екземпляра.

Тепер, коли у нас є функція конструктора нашої особи та наша функція sayName на його прототипі, створіть екземпляр Person, після чого назвемо функцію sayName.

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

Тому всі разом код для створення конструктора особи, додавання функції його прототипу, створення екземпляра особи, а потім виклик функції на його прототипі виглядає так.

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. Перше, що ви повинні помітити, це те, що після використання "нового" у нашому прикладі ми можемо викликати метод (sayName) на "tyler" так, якби він був об'єктом - це тому, що це таке. Отже, по-перше, ми знаємо, що конструктор нашої особи повертає об'єкт, чи можемо ми це побачити в коді чи ні. По-друге, ми знаємо, що оскільки наша функція sayName розташована на прототипі, а не безпосередньо в екземплярі Person, об'єкт, в який повертається функція Person, повинен делегувати його прототипу при невдалому пошуку. У більш простими словами, коли ми називаємо tyler.sayName (), інтерпретатор каже: "Добре, я збираюся подивитися на об'єкт" tyler ", який ми щойно створили, знайдіть функцію sayname, а потім назвемо це. Зачекай хвилину, я тут не бачу - все, що я бачу, це ім'я та вік, дозвольте мені перевірити прототип. Так, схоже, що це на прототипі, дозвольте мені назвати це ".

Нижче наведено код, як ви можете подумати про те, що "нове" ключове слово насправді виконує в 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;
}

Тепер, маючи ці знання про те, що "нове" ключове слово насправді має у JavaScript, створення служби в AngularJS повинно бути легше зрозуміти.

Найбільше, що потрібно зрозуміти при створенні сервісу, - це знати, що Служби інкримінуються за допомогою "нового" ключового слова. Поєднуючи ці знання з нашими прикладами вище, тепер ви повинні визнати, що ви будете додавати свої властивості та методи безпосередньо до "цього", яке потім буде повернуто з самої Служби. Давайте подивимося на це у дії.

На відміну від того, що ми спочатку робили з прикладом Фабрики, нам не потрібно створювати об'єкт, а потім повернути цей об'єкт, оскільки, як згадувалося багато разів раніше, ми використовували "нове" ключове слово таким чином, що інтерпретатор створить цей об'єкт, надасть йому делегування це прототип, а потім поверніть його нам, якщо нам не доведеться виконувати цю роботу.

По-перше, створіть нашу "приватну" та допоміжну функцію. Це має виглядати дуже добре, оскільки ми робили те саме саме з нашою фабрикою. Я не буду пояснювати, що тут кожен рядок робить, тому що я зробив це на фабричному прикладі, якщо ви збентежені, перегляньте фабричний приклад.

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;
  }
});

Тепер ми додамо всі наші методи, які будуть доступні в нашому контролері на "це".

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 та call-itunes будуть доступні в будь-якому контролері, яким ми передаємо 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) Постачальник

Найбільше, що потрібно пам'ятати про постачальників, - це те, що вони є єдиною службою, яку ви можете передати у прикладну програму 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, і тому їх можна змінити, перш ніж вони стануть доступними деінде більше (також показано вище). Важливо зазначити, що ці змінні потрібно долучити до "цього" ключового слова. У нашому прикладі, лише "thingFromConfig" буде доступний для зміни в app.config. Третій розділ (показаний нижче) - це всі змінні / функції, які будуть доступні у вашому контролері при передачі в службу "myProvider" у цей конкретний контролер.

Під час створення служби з Провайдером єдиними властивостями / методами, які будуть доступні у вашому контролері, є ті властивості / методи, які повертаються з функції $ get (). Нижче наведений код призводить до того, що $ повернеться на "цей" (який, як ми знаємо, в кінцевому підсумку буде повернуто з цієї функції). Тепер ця функція $ 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
    }
  }

Тепер повний код постачальника виглядає так

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;
});

Як згадувалося раніше, цільним моментом створення сервісу з Провайдером є можливість змінювати деякі змінні через функцію 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, це буде "Ця пропозиція була встановлена ​​...".


619
2017-12-24 13:15



Єдина частина, яка відсутня у цій відмінній характеристиці, - це відносні переваги використання сервісу над фабрикою; що чітко пояснюється в прийнятій відповіді Ліор - infinity
FWIW (може бути, не так багато), ось блогер, який вирішує проблему з Angular, і не любить providerProvider codeofrob.com/entries/you-have-ruined-javascript.html - barlop
Пункція "JavaScript-гуру" була хитри. : D Я думаю, що ця відповідь дуже сильно розчищає. Прекрасно написано. - amarmishra
Ваш TLDR потребує TLDR. - JensB
@ JensB tl; dr - Learn React. - Tyler McGinnis


Всі послуги є синглетони; вони отримують екземпляр раз на один додаток. Вони можуть бути будь-якого типу, будь то примітивний, об'єкт буквальний, функція або навіть екземпляр спеціального типу.

The value, factory, service, constant, і provider методи є всіма постачальниками. Вони навчають Інжектора, як інсценировать Служби.

Найбільш розмовним, але також найповнішим є Провайдер   рецепт The залишилися чотири типи рецептів - значення, фабрика, сервіс і   Постійна - є просто синтаксичним цукром на вершині рецепта постачальника.

  • The Рецепт цінності це найпростіший випадок, коли ви безпосередньо створюєте Службу самостійно та надайте екземплярне значення до інжектора.
  • The Заводський рецепт дає Injector заводську функцію, яку він викликає, коли вона потребує екземпляру служби. Коли називається, то фабрична функція створює і повертає службовий екземпляр. Залежності Сервісу вводяться як аргументи функцій. Отже, використовуючи цей рецепт, додається наступні здібності:
    • Можливість використання інших служб (є залежності)
    • Ініціалізація сервісу
    • Затримка / лінька ініціалізація
  • The Сервісний рецепт майже така ж, як рецепт заводу, але тут Injector викликає a конструктор з новим оператором замість фабричної функції.
  • The Рецепт постачальника звичайно перегріти. Він додає ще один шар неорієнтованості, дозволяючи налаштувати створення фабрики.

Ви повинні використовувати рецепт постачальника лише тоді, коли ви хочете виставити API   для широкої конфігурації програми, яка повинна бути зроблена до   програма запускається Зазвичай це цікаво тільки для багаторазового використання   послуги, поведінка яких може знадобитися трохи відрізнятися між   заявки.


506
2018-02-01 12:58



Так сервіс і фабрика по суті однакові? Використання одного з іншого забезпечує не що інше, як альтернативний синтаксис? - Matt
@ Матт, так, сервіс - стислі способи, коли у вас вже є ваша власна функція, яку ви хочете виставити як службу. З документів: myApp.factory ('unicornLauncher', ["apiToken", функція (apiToken) {повернути новий UnicornLauncher (apiToken);}]); vs: myApp.service ('unicornLauncher', [apiToken, UnicornLauncher]); - janek
@joshperry Як новачок, я протримав різницю між сервісом і фабрикою. Я згоден, це найкраща відповідь! Я б розумів, що сервіс є класом обслуговування (наприклад, класом encoder / decoder), який може мати деякі приватні властивості. І фабрика надає безліч допоміжних методів. - stanleyxu2005
Приклади інших відповідей вище не в змозі дуже чітко пояснити основні відмінності б / в послуг та постачальників, які вводяться в той час, коли ці рецепти інсценированы. - Ashish Singh


Розуміння заводу, сервісу та постачальника AngularJS

Всі вони використовуються для обміну одноразових об'єктів багаторазового використання. Це допомагає поділитися багаторазовим кодом у вашому додатку / різних компонентах / модулях.

З документів Сервіс / Фабрика:

  • Lazily інстанція - Кутовий лише іменує службу / фабрику, коли від нього залежить компонент програми.
  • Сінглетони - кожен компонент   залежно від служби отримує посилання на єдиний екземпляр   породжена сервісною фабрикою.

Фабрика

Фабрика - це функція, за допомогою якої ви можете маніпулювати / додавати логіку перед створенням об'єкта, тоді знову повертається щойно створений об'єкт.

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

Використання

Це може бути лише сукупність функцій, подібних до класу. Отже, це може бути показано в різних контролерах, коли ви вставляєте його всередину функцій контролера / фабрики / директиви. Вона екземплярована лише один раз на додаток.

Сервіс

Просто поглянувши на сервіси, подумайте про прототип масиву. Сервіс - це функція, яка створює новий об'єкт за допомогою "нового" ключового слова. Ви можете додати властивості та функції до сервісного об'єкту за допомогою thisключове слово На відміну від фабрики, він нічого не повертає (він повертає об'єкт, що містить методи / властивості).

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

Використання

Використовуйте його, коли потрібно поділитися одним об'єктом у всій програмі. Наприклад, автентифіковані дані користувача, спільні методи / дані, корисні функції тощо.

Постачальник

Провайдер використовується для створення настроюваного сервісного об'єкта. Ви можете налаштувати параметри сервісу за допомогою функції налаштування. Він повертає значення за допомогою $get() функція The $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.
        };
    };
});

Використання

Коли вам потрібно надати модульну конфігурацію об'єкту обслуговування, перш ніж зробити його доступним, наприклад. Припустимо, ви хочете встановити URL-адресу API на основі вашого середовища, як dev, stage або prod

ПРИМІТКА 

Тільки провайдер буде доступний в фазі конфігурації кутових, в той час як   Сервіс та фабрика не є.

Сподіваюся, що це допомогло зрозуміти ваше розуміння Завод, Сервіс та Постачальник.


221
2017-11-14 06:25



Що робити, якщо я хотів би мати службу з певним інтерфейсом, але має дві різні реалізації та ін'єкційний кожен в контролер, але прив'язаний до різних станів за допомогою ui-маршрутизатора? наприклад, робити віддалені дзвінки в одному стані, але писати в локальне сховище, а не в іншому. Документи постачальника говорять про використання only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications, це не звучить, чи не так? - qix


Для мене одкровення прийшло, коли я зрозумів, що всі вони працюють однаково: виконуючи щось один раз, зберігаючи ціну, яку вони отримують, а потім кашляти це ж збережене значення коли згадується ін'єкція залежності.

Скажімо, у нас є:

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

Різниця між трьома полями полягає в тому, що:

  1. aЗбережене значення приходить від запуску fn.
  2. bЗбережено значення збереженого значення newing fn.
  3. cЙого збережене значення походить від першого отримання екземпляра newing fn, а потім запустіть a $get метод екземпляра.

А це означає, що у AngularJS є щось схоже на об'єкт кешу, значення якого для кожної ін'єкції призначається лише один раз, коли їх вводили вперше, а де:

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

Ось чому ми використовуємо this в службах і визначити a this.$get у постачальниках.


190
2017-07-22 11:39



Мені також найбільше подобається ця відповідь. Справа в тому, щоб забезпечити доступ до об'єкта, коли це необхідно, через DI. Як правило, ти все добре factoryс Єдина причина serviceІснують такі мови, як CoffeeScript, TypeScript, ES6 тощо, тому ви можете використовувати їх синтаксис класів. Тобі потрібно providers, якщо ваш модуль використовується в кількох програмах з різними налаштуваннями за допомогою app.config(). Якщо ваша послуга є чистим синглом або здатна створювати екземпляри щось, залежить лише від вашої реалізації. - Andreas Linnert


Сервіс провайдера проти фабрики:

Я намагаюся простежити це. Все про базову концепцію JavaScript.

Перш за все, давайте поговоримо послуги в AngularJS!

Що таке Служба: У AngularJS Сервіс це не що інше як об'єкт JavaScript, який може зберігати деякі корисні методи або властивості. Цей єдиний об'єкт створено на основі ngApp (кутовий додаток), і він ділиться між усіма контролерами в межах поточного додатку. Коли Angularjs створює екземпляр об'єкта служби, він реєструє цей об'єкт служби унікальним іменем служби. Тому кожного разу, коли нам потрібен екземпляр служби, Angular шукає реєстр для цього імені служби, і він повертає посилання на об'єкт сервісу. Такі, що ми можемо викликати метод, властивості доступу тощо на об'єкті сервісу. Можливо, ви маєте питання, чи можна також поставити властивості, методи на об'єкт контролерів обсягу! Так чому вам потрібен сервісний об'єкт? Відповіді: послуги поділяються між різними контролерами. Якщо ви помістіть деякі властивості / методи в об'єкт обсягу контролера, він буде доступний лише для поточної області. Але коли ви визначаєте методи, властивості службового об'єкта, вони будуть доступні у всьому світі і можуть бути доступні в будь-якому обладнанні контролера шляхом ін'єкції цієї служби.

Отже, якщо існує три регулятора, нехай це буде controllerA, controllerB і controllerC, всі будуть поділяти один і той же екземпляр служби.

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

Як створити службу?

AngularJS забезпечує різні способи реєстрації послуги. Тут ми зосередимося на трьох способах заводських (..), сервісних (..), провайдерів (..);

Використовуйте цю посилання для посилання на код

Функція заводу:

Ми можемо визначити заводську функцію, як показано нижче.

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

AngularJS забезпечує 'фабрика (' 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 намагається інсталювати об'єкт сервісу. На цей раз кутовий використовує "нове" ключове слово та викликає функцію конструктора щось на зразок нижче.

var serviceInstance = new fnServiceConstructor();

У функції конструктора ми можемо використовувати "це" ключове слово для додавання властивостей / методів до сервісного об'єкта. приклад:

//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);
  };
});

Функція постачальника:

Функція постачальника () - це ще один спосіб створення послуг. Нехай нам цікаво створити сервіс, який просто покаже користувачеві вітальне повідомлення. Але ми також хочемо забезпечити таку функціональність, щоб користувач міг встановити своє вітальне повідомлення. У технічних умовах ми хочемо створити настроювані сервіси. Як ми можемо це зробити? Там повинен бути спосіб, щоб додаток міг передавати свої власні привітання повідомлення, і Angularjs зробить його доступним для функції фабрики / конструктора, які створюють наш екземпляр служби. У такому випадку функція provider () виконує роботу. Використовуючи функцію provider (), ми можемо створити налаштовувані послуги.

Ми можемо створити налаштовувані служби, використовуючи синтаксис постачальника, як зазначено нижче.

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

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

Як синтаксис постачальника працює внутрішньо?

Об'єкт 1.Provider створюється за допомогою функції конструктора, яку ми визначили в нашій функції провайдера.

var serviceProvider = new serviceProviderConstructor();

2. Функція, яку ми передали у app.config (), виконана. Це називається фаза конфігурації, і тут ми маємо можливість налаштувати наш сервіс.

configureService(serviceProvider);

3. Останній сервісний екземпляр створюється за допомогою виклику $ get method serviceProvider.

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 ?');
  }
);

Робоча демонстрація

Підсумок:


Фабрика використовуйте фабричну функцію, яка повертає екземпляр служби. serviceInstance = fnFactory ();

Сервіс використовуйте функцію конструктора та Angular викликайте цю функцію конструктора за допомогою "нового" ключового слова для створення сервісного екземпляра. serviceInstance = новий fnServiceConstructor ();

Постачальник визначає функцію providerConstructor, цей провайдерConstructor визначає фабричну функцію $ отримати . Кутові виклики $ get () для створення сервісного об'єкта. Синтаксис постачальника має додаткову перевагу в налаштуванні службового об'єкта, перш ніж він буде отриманий в інстанціях. serviceInstance = $ get ();


133
2017-11-19 13:36