AngularJS: Сервіс проти постачальника проти фабрики


3319

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


244
Я виявив, що всі терміни Angular лякають початківців. Ми почали з цього шаблону, який було трохи легше для розуміння нашим програмістам під час вивчення Angular demisx.github.io/angularjs/2014/09/14/… . Сподіваюся, це допоможе і вашій команді.
demisx

7
На мою думку, найкращий спосіб зрозуміти різницю - це використання власної документації Angular : docs.angularjs.org/guide/providers, це надзвичайно добре пояснено та використовується своєрідний приклад, який допоможе вам зрозуміти це.
Рафаель Мерлін

3
@Blaise Дякую! На мій коментар у дописі я це навмисно не залишив, оскільки 99% випадків використання з мого досвіду можна успішно вирішити service.factory. Не хотіли більше ускладнювати цю тему.
демікс

3
Я вважаю цю дискусію також дуже корисною stackoverflow.com/questions/18939709/…
Anand Gupta

3
Ось кілька хороших відповідей про теservices,якfactoriesі якprovidersпрацює.
Mistalis

Відповіді:


2866

Із списку розсилки AngularJS я отримав дивовижну нитку, яка пояснює службу проти фабрики проти постачальника та використання їх для ін'єкцій. Складання відповідей:

Послуги

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

Заводи

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

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

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

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

Дивіться тут за наданий код.

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

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

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

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

58
Дивіться також stackoverflow.com/a/13763886/215945, де обговорюються відмінності між сервісом та заводом.
Марк Райкок

3
У редагуванні 611 я додав використання кутових констант і значень. Для демонстрації відмінностей інших вже показаних. jsbin.com/ohamub/611/edit
Нік

17
Хоча служба викликається шляхом створення екземпляра функції. Він фактично створюється лише один раз на інжекторі, що робить його подібним до однотонного. docs.angularjs.org/guide/dev_guide.services.creating_services
angelokh

33
Цей приклад може бути неймовірним, якби він використовував чіткий практичний приклад. Я гублюся , намагаючись зрозуміти, в чому сенс речей , як toEqualі greeter.Greetє. Чому б не використати щось трохи більш реальне і відносне?
Кайл Пеннелл

5
Використання функції очаквайте () - поганий вибір, щоб щось пояснити. Використовуйте реальний світовий код наступного разу.
Крейг

812

Демо JS Fiddle

Приклад "Привіт світ" з 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>


2
Чи не thisзмінюється контекст у $getфункції? - ви більше не посилаєтесь на спеціалізованого постачальника в цій функції.
Нейт-Вілкінс

12
@Nate: thisфактично не змінює контекст, тому що те, що викликається, є new Provider()$ get (), куди Providerпередається функція app.provider. Тобто, що $get()викликається як метод на побудованому Provider, так thisбуде посилатися на Providerприклад, який пропонує.
Брендон

1
@Brandon Ой гаразд, це киндоф акуратно тоді. Плутати на перший погляд - дякую за роз’яснення!
Нейт-Вілкінс

3
Чому я отримую, Unknown provider: helloWorldProvider <- helloWorldколи це працює локально? Коментуючи це, така ж помилка для інших 2-х прикладів. Чи є якась прихована конфігурація провайдера? (Angular 1.0.8) - Знайдено: stackoverflow.com/questions/12339272/…
Антуан

4
Це причина, чому @Antoine отримує помилку "Невідомо надавати: helloWorldProvider", оскільки у коді .config ви використовуєте "helloWorldProvider", але коли ви визначаєте провайдера в myApp.provider ("helloWorld", функція ()), ви використовуєте 'Привіт Світ'? Іншими словами, у своєму конфігураційному коді, як кутовий знати, що ви посилаєтесь на постачальника helloWorld? Спасибі
jmtoung

645

TL; DR

1) Коли ви використовуєте Factory, ви створюєте об'єкт, додаєте до нього властивості та повертаєте цей самий об’єкт. Коли ви передасте цю фабрику у свій контролер, ці властивості об’єкта тепер будуть доступні в цьому контролері через вашу фабрику.

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



Non TL; DR

1) Заводські
фабрики - найпопулярніший спосіб створення та налаштування послуги. Дійсно не набагато більше, ніж те, що сказав TL; DR. Ви просто створюєте об'єкт, додаєте до нього властивості, а потім повертаєте той самий об’єкт. Потім, коли ви передасте завод у свій контролер, ці властивості об’єкта тепер будуть доступні в цьому контролері через вашу фабрику. Більш обширний приклад наведено нижче.

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

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

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

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 - це функція, яка створить та поверне нашу iTunes дружню URL-адресу.

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

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

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

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

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

Спочатку давайте створимо наш Конструктор.

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

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

Тепер додамо метод до прототипу нашого 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» у нашому прикладі, ми можемо викликати метод (sayName) на «tyler» так само, як якщо б це був об’єкт - це тому, що це так. Отже, спочатку ми знаємо, що наш конструктор Person повертає об'єкт, можемо ми бачити це в коді чи ні. По-друге, ми знаємо, що оскільки наша функція 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 та 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) Постачальник

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

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

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

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, це буде "Це речення було встановлено ...".


11
Єдина частина, якої не вистачає в цій чудовій програмі, - відносні переваги використання послуги над фабрикою; що чітко пояснено у прийнятій Ліором відповіді
нескінченність

2
FWIW (можливо , НЕ так багато), ось блогер , який полемізує з кутовими, і не любить providerProvider codeofrob.com/entries/you-have-ruined-javascript.html
barlop

3
«Гуру JavaScript» хитромудрий. : DI думаю, що ця відповідь дуже очищає речі. Чудово написано.
amarmishra

4
Ваш TLDR потребує TLDR.
JensB

3
@JensB tl; dr - Learn React.
Тайлер МакГінніс

512

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

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

Найбільш багатослівний, але і найбільш повний - це рецепт постачальника. В інших чотирьох типів рецептів - Value, фабрика, Обслуговування і Constant - це просто синтаксичний цукор поверх рецепта постачальника .

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

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

  • Constant рецепт просто як рецепт Значення крім цього дозволяє визначити послуги, які доступні в конфігурації фази. Швидше ніж служби, створені за рецептом значення. На відміну від Значень, їх не можна декорувати за допомогою decorator.
Дивіться документацію провайдера .


2
Тож сервіс та фабрика по суті однакові? Використання одного з іншого не забезпечує нічого іншого, крім альтернативного синтаксису?
Метт

2
@Matt, так, сервіс - це стислий спосіб, коли у вас вже є своя функція, яку ви хочете викрити як послугу. З docs: myApp.factory ('unicornLauncher', ["apiToken", функція (apiToken) {повернути новий UnicornLauncher (apiToken);}]); vs: myApp.service ('unicornLauncher', ["apiToken", UnicornLauncher]);
янек

5
@joshperry Як новачок, я деякий час переглядав різницю між сервісом та фабрикою. Я згоден, це найкраща відповідь коли-небудь! Я розумію службу як клас обслуговування (наприклад, клас кодера / декодера), який може мати деякі приватні властивості. А фабрика пропонує набір асистентних методів без громадянства.
stanleyxu2005

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

223

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

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

Від служби / фабрики Документів :

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

Заводська

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

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

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

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

ПРИМІТКА

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

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


1
Що б я зробив, якби хотів мати сервіс із певним інтерфейсом, але мати дві різні реалізації та вводити кожну в контролер, але прив’язаний до різних станів за допомогою 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

191

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

Скажіть, у нас є:

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провайдери.


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

137

Постачальник послуг проти постачальника:

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

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

Що таке сервіс: в AngularJS, сервісце не що інше, як однотонний JavaScript-об'єкт, який може зберігати корисні методи чи властивості. Цей одиночний об'єкт створений за принципом ngApp (Angular app) і його поділяють між усіма контролерами в поточному додатку. Коли 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 забезпечує метод 'factory (' serviceName ', fnFactory)', який бере два параметри, serviceName та функцію JavaScript. Angular створює екземпляр служби за допомогою виклику функції fnFactory () , наведеної нижче.

var serviceInstace = fnFactory();

Функція, що передається, може визначити об'єкт і повернути його. AngularJS просто зберігає цей об'єкт посилання на змінну, яка передається як перший аргумент. Все, що повернуто з fnFactory, буде прив'язане до службиInstance. Замість повернення об'єкта ми також можемо повернути функцію, значення тощо. Що б ми не повернули, вони будуть доступні для сервісного екземпляра.

Приклад:

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

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

Функція провайдера:

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

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

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

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

Як внутрішньо працює синтаксис провайдера?

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

var serviceProvider = new serviceProviderConstructor();

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

configureService(serviceProvider);

3.Насправді екземпляр служби створюється за допомогою виклику $ get методу 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 ();

Сервіс використовує функцію конструктора і Кутовий виклик цієї функції конструктора, використовуючи ключове слово 'new' для створення екземпляра служби. serviceInstance = новий fnServiceConstructor ();

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


87

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

введіть тут опис зображення

Ось стаття, з якої складається це зображення:

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


63

Заводська

Ви надаєте 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;
});

Сервіс

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

Приклад:

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

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

Ви надаєте 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');
});

56

Я помітив щось цікаве, граючи з провайдерами.

Видимість ін'єкційних препаратів для постачальників різна, ніж для служб та заводів. Якщо ви оголосите AngularJS "постійною" (наприклад, myApp.constant('a', 'Robert');), ви можете ввести його в сервіси, фабрики та постачальники.

Але якщо ви оголосите AngularJS "значенням" (напр., myApp.value('b', {name: 'Jones'});), Ви можете ввести його в сервіси та на фабрики, а НЕ у функцію створення постачальника. Однак ви можете ввести його у $getфункцію, яку ви визначите для свого постачальника. Про це йдеться в документації AngularJS, але це легко пропустити. Ви можете знайти його на сторінці% provide у розділах про значення та постійні методи.

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>

45

Це дуже заплутана частина для новачків, і я намагався пояснити це простими словами

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

У сервісі ми створюємо назви функцій як властивості з цим об’єктом.

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

Провайдер AngularJS: мета цього знову однакова, однак Провайдер дає висновок про функцію $ get.

Визначення та використання сервісу, фабрики та постачальника пояснюється на веб- сайті http://www.dotnetfunda.com/articles/show/3156/difference-bet between-angularjs-service-factory-and-provider


2
Фабрика та постачальники також є одиночним об'єктом? Будь-який сканріо, де фабрикам рекомендується надавати послуги?
Суніл Гарг

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

Найпростіший спосіб мислення такий:

  • Служба є однотонним об'єктом об'єкта. Використовуйте сервіси, якщо ви хочете надати єдиний об'єкт для свого коду.
  • Фабрика - клас. Використовуйте фабрики, якщо ви хочете надати спеціальні класи для свого коду (неможливо зробити з сервісами, оскільки вони вже створені).

Приклад заводського класу наведено у коментарях, а також у різниці постачальників.


як послуга може бути одиночною, якщо вона оновлюється при кожному використанні? я можу
Джо

Служба лише один раз інстанціюється під час вирішення залежності, і тоді, коли ви попросите послуги від інжектора, ви отримуєте завжди один і той же екземпляр. Це можна легко перевірити тут: jsfiddle.net/l0co/sovtu55t/1 , будь ласка, запустіть його за допомогою консолі. На консолі показано, що служба створюється лише один раз.
Лукаш Франковський

о Я бачу. я очікував, що зможу буквально new MyService()чи щось :)
joe

33

Моє уточнення з цього приводу:

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

Хоча глобальні змінні не рекомендуються, реальне використання цих глобальних змінних полягає у забезпеченні введення залежності , передаючи змінну відповідному контролеру.

Існує багато рівнів ускладнень при створенні значень для "глобальних змінних":

  1. Постійний
    Це визначає фактичну константу, яку не слід змінювати протягом усієї програми, як константи в інших мовах (те, чого не вистачає JavaScript).
  2. Значення
    Це значення, що може змінюватися, або об'єкт, і він служить деякою глобальною змінною, яку навіть можна вводити під час створення інших служб або заводів (див. Далі про це). Однак воно повинно бути " буквальним значенням ", що означає, що потрібно виписати фактичне значення і не може використовувати жодну логіку обчислень або програмування (іншими словами 39 або myText або {prop: "value"} в порядку, але 2 +2 немає).
  3. Фабрика
    Більш загальне значення, яке можна обчислити відразу. Він працює, передаючи функцію AngularJS з логікою, необхідною для обчислення значення, і AngularJS виконує його, і зберігає повернене значення у названій змінній.
    Зауважте, що можливо повернути об’єкт (у такому випадку він буде функціонувати аналогічно сервісу ) або функцію (яка буде збережена у змінній як функція зворотного виклику).
  4. Сервіс
    Сервіс - це більш скорочена версія фабрики, яка діє лише тоді, коли значення є об'єктом, і вона дозволяє записувати будь-яку логіку безпосередньо у функції (як би це був конструктор), а також декларувати та отримувати доступ властивості об'єкта за допомогою цього ключового слова.
  5. Провайдер
    На відміну від послуги, яка є спрощеною версією заводу , постачальник послуг є більш складним, але більш гнучким способом ініціалізації "глобальних" змінних, при цьому найбільшою гнучкістю є можливість встановлення значень з app.config.
    Це працює як використання комбінації послуг та постачальника , передаючи провайдеру функцію, яка має властивості, оголошені за допомогою цього ключового слова, які можна використовувати з app.config.
    Тоді йому потрібно мати окрему функцію $ .get, яка виконується AngularJS після встановлення вищевказаних властивостей через app.configфайл, і ця $ .get функція поводиться так само, як і заводська вище, оскільки його повернене значення використовується для ініціалізації "глобальних" змінних.

26

Моє розуміння дуже просте нижче.

Фабрика: Ви просто створюєте об'єкт всередині фабрики і повертаєте його.

Сервіс:

У вас просто є стандартна функція, яка використовує це ключове слово для визначення функції.

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

Є $getвизначений вами об’єкт, і він може бути використаний для отримання об'єкта, який повертає дані.


Ви не переплутали завод і сервіс? Служби створюються там, де завод повертається.
Флавіен Волкен

Коли ви оголошуєте ім'я служби ін'єкційним аргументом, вам буде наданий екземпляр функції. Іншими словами, нова функціяYouPassedToService (). Цей примірник об'єкта стає об’єктом обслуговування, який AngularJS реєструє та ін'єктує пізніше іншим службам / контролерам, якщо потрібно. // factory При визначенні фабричного імені як аргументу, що ін'єктується, вам буде надано значення, яке повертається, викликаючи посилання функції, передане на module.factory.
sajan

Гаразд, так ... у кутовій фабриці є сингтон, де "послуга" - це фактично фабрика (у загальних рисах дизайну)
Флавіен Волкен

25

Резюме з кутових документів :

  • Існує п’ять типів рецептів, які визначають, як створювати об’єкти: значення , фабрика , послуга , постачальник і постійний .
  • Фабрика та сервіс - це найчастіше використовувані рецепти. Єдина відмінність між ними полягає в тому, що рецепт Сервісу працює краще для об'єктів нестандартного типу, тоді як заводський може виробляти примітиви та функції JavaScript.
  • Provider рецепт є основною тип рецепт і всі інші просто синтаксичний цукор на ньому.
  • Постачальник - найскладніший тип рецепту. Він вам не потрібен, якщо ви не будуєте багаторазовий фрагмент коду, який потребує глобальної конфігурації.

введіть тут опис зображення


Найкращі відповіді від SO:

https://stackoverflow.com/a/26924234/165673 (<- ДОБРЕ) https://stackoverflow.com/a/27263882/165673
https://stackoverflow.com/a/16566144/165673


20

Всі хороші відповіді вже. Я хотів би додати ще кілька балів на Сервіс та Завод . Поряд із різницею між сервісом / заводом. Також у вас можуть виникнути такі питання, як:

  1. Чи варто користуватися сервісом чи фабрикою? Яка різниця?
  2. Вони так само чи мають однакову поведінку?

Почнемо з різниці між Сервісом та Заводом:

  1. Обидва є одиночними : Кожен раз, коли кутовий виявить їх як залежність вперше, він створює єдиний екземпляр служби / фабрики. Після створення екземпляра той самий екземпляр використовується назавжди.

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

Послуги:

Сервіс - це функція конструктора, і Angular буде створювати його, викликаючи новий yourServiceName() . Це означає пару речей.

  1. Функції та змінні екземплярів будуть властивостями this .
  2. Вам не потрібно повертати значення. Коли кутовий дзвінок new yourServiceName(), він отримаєthis об’єкт із усіма властивостями, які ви наділили на нього.

Приклад зразка:

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

Коли Angular вводить цю MyServiceпослугу в контролер, який залежить від неї, цей контролер отримає aMyService яку він може викликати функції, наприклад, MyService.aServiceMethod ().

Будьте обережні 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пов'язаний з, nullі ви отримаєте помилки. Кращий спосіб був би $http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper)). Незалежно від того, чи вирішили ви використовувати це у своїх методах обслуговування чи ні, будьте уважні, як ви їх називаєте.

Повернення значення зService :

Через те, як працюють конструктори JavaScript, якщо ви повернете комплексне значення (i.e., an Object)з constructorфункції, абонент отримає цей Об'єкт замість цього екземпляра.

Це означає , що ви можете в основному копіпаст приклад фабрики знизу, які замінять factoryз service, і він буде працювати:

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

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

Отже, коли Angular побудує вашу службу за допомогою нового MyService (), він отримає цей об’єкт api замість екземпляра MyService.

Це поведінка для будь-яких складних значень (об'єктів, функцій), але не для примітивних типів.

Заводи:

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

angular.factory('MyFactory', function($http) {
  var api = {};

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

  return api;
});

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

Відповіді на вище 1 та 2 питання:

Здебільшого просто дотримуйтесь використання фабрик для всього. Їх поведінку легше зрозуміти. Немає вибору робити, повертати значення чи ні, і, крім того, помилок не буде введено, якщо ви зробите не так.

Я все ще називаю їх «послугами», коли говорю про введення їх як залежності.

Поведінка служби / заводу дуже схожа, і деякі люди скажуть, що будь-яка з них нормальна. Це дещо правда, але мені легше слідувати порадам керівництва зі стилю Джона Папи і просто дотримуватися фабрик. **


16

Додатковим уточненням є те, що фабрики можуть створювати функції / примітиви, тоді як служби не можуть. Перевірте цей jsFiddle на основі Epokk: http://jsfiddle.net/skeller88/PxdSP/1351/ .

Фабрика повертає функцію, яку можна викликати:

myApp.factory('helloWorldFromFactory', function() {
  return function() {
    return "Hello, World!";
  };
});

Фабрика також може повернути об'єкт методом, до якого можна викликати:

myApp.factory('helloWorldFromFactory', function() {
  return {
    sayHello: function() {
      return "Hello, World!";
    }
  };
});

Служба повертає об'єкт методом, до якого можна викликати:

myApp.service('helloWorldFromService', function() {
  this.sayHello = function() {
     return "Hello, World!";
  };
});

Детальніше дивіться у публікації, яку я написав про різницю: http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/


16

Вже є хороші відповіді, але я просто хочу поділитися цим.

Перш за все: Провайдер - це спосіб / рецепт створення service(одиночного об’єкта), який передбачає ввести $ injector (як AngulaJS іде про шаблон IoC).

І вартість, фабрика, обслуговування та постійна (4 способи) - синтаксичний цукор над Provider шлях / recepie.

Є Service vs Factoryчастина була висвітлена: https://www.youtube.com/watch?v=BLzNCkPn3ao

Служба - це newфактично ключове слово, яке, як ми знаємо, робить 4 речі:

  1. створює абсолютно новий об’єкт
  2. пов'язує це зі своїм prototype об’єктом
  3. з'єднує context доthis
  4. і повертається this

І Factory - це Factory Pattern - містить функції, що повертають такі об'єкти, як ця Служба.

  1. можливість користуватися іншими послугами (мати залежності)
  2. ініціалізація послуги
  3. затримка / лінива ініціалізація

І це просте / коротке відео: охоплює також Постачальника : https://www.youtube.com/watch?v=HvTZbQ_hUZY (там ви можете побачити, як вони переходять від заводу до постачальника)

Рецепт постачальника використовується в основному в конфігурації програми, перш ніж програма повністю запуститься / ініціалізується.


14

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

  • Інжектор використовує рецепти для створення двох типів об'єктів: служб та об'єктів спеціального призначення
  • Існує п’ять типів рецептів, які визначають, як створювати об’єкти: значення, фабрика, послуга, постачальник і постійний.
  • Фабрика та сервіс - це найчастіше використовувані рецепти. Єдина відмінність між ними полягає в тому, що рецепт Сервісу працює краще для об'єктів користувальницького типу, тоді як Фабрика може виробляти примітиви та функції JavaScript.
  • Рецепт постачальника є основним типом рецепту, а всі інші - це лише синтаксичний цукор.
  • Постачальник - найскладніший тип рецепту. Він вам не потрібен, якщо ви не будуєте багаторазовий фрагмент коду, який потребує глобальної конфігурації.
  • Усі об'єкти спеціального призначення, крім контролера, визначаються за допомогою заводських рецептів.

введіть тут опис зображення

І для початківців розуміють: - Це може бути неправильним випадком використання, але на високому рівні це те, що використовується для цих трьох.

  1. Якщо ви хочете використовувати в кутовому модулі, функцію конфігурації слід створити як провайдера

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

  1. Інтеграція виклику Ajax або інтеграція третьої сторони повинна бути послугою .
  2. Для маніпуляцій із даними створіть його як заводський

Для основних сценаріїв завод та сервіс поводяться однаково.


13

Ось декілька кодів бройлерів, які я придумав як шаблон-код для фабрики об'єктів в AngularjS. Я використовував Автомобіль / CarFactory як приклад для ілюстрації. Створює простий код реалізації в контролері.

     <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>

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

    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

;


12

Використовуючи в якості довідки цю сторінку та документацію (яка, схоже, значно покращилася з моменту, коли я останній раз переглядала), я зібрав наступну реальну (-іш) світову демонстрацію, яка використовує 4 з 5 ароматів постачальника; Цінність, постійний, заводський і повний видувний постачальник.

HTML:

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

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


12

Ця відповідь стосується теми / питання

як Factory, Service та Constant - це лише синтаксичний цукор поверх рецепта постачальника?

АБО

наскільки фабрика, сервіс та постачальники рівнозначні зсередини

в основному те, що відбувається

Коли ви робите factory()це, ви встановлюєте, що ви functionнадали у другому аргументі постачальнику, $getі повертаєте його ( provider(name, {$get:factoryFn })), все, що ви отримуєте, providerале немає іншого властивості / методу, крім$get цього provider(значить, ви не можете налаштувати це)

Вихідний код заводу

function factory(name, factoryFn, enforce) {
    return provider(name, {
      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
    });
};

Здійснюючи service()це, ви повертаєте фабрику () з functionін'єкцією constructor(повертайте екземпляр конструктора, який ви надали у вашій службі) і повертаєте його

Вихідний код послуги

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

Таким чином, в основному в обох випадках ви, зрештою, отримуєте провайдерів $ встановити свою функцію, яку ви надали, але ви можете дати що-небудь додатково, ніж $ get, як ви могли спочатку надати у постачальника () для конфігураційного блоку


11

Я знаю багато відмінних відповідей, але мені потрібно поділитися своїм досвідом використання
1. serviceдля більшості випадків за замовчуванням
2., які 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){ ...
    }       
}
})();

та використовуючи:

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;
... 

10

Трохи запізнюємось на вечірку. Але я подумав, що це корисніше для тих, хто хотів би дізнатися (чи мати чіткість) щодо розробки користувальницьких служб Angular JS за допомогою заводських, службових та постачальницьких методологій.

Я натрапив на це відео, яке чітко пояснює фабричні, службові та провайдерські методології розробки користувальницьких послуг AngularJS:

https://www.youtube.com/watch?v=oUXku28ex-M

Вихідний код: http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service

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

Код для користувальницької служби на основі "фабрики" такий (що йде з версіями синхронізації та асинхронізації разом з викликом служби http):

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

Код методології "служби" для користувацьких служб (це досить схоже на "заводський", але відрізняється від точки зору синтаксису):

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

}]);

Код методології "постачальника" для користувацьких послуг (це необхідно, якщо ви хочете розробити сервіс, який можна було б налаштувати):

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

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

<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>


10

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

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

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

9

Давайте обговоримо три способи керування бізнес-логікою в AngularJS простим способом: ( натхненний курсом Якокова Coursera AngularJS )

ОБСЛУГОВУВАННЯ :

Синтаксис:

app.js

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

index.html

<div ng-controller = "ServiceExampleController as serviceExample">
   {{serviceExample.data}}
</div>

Особливості обслуговування:

  1. Ліниво миттєвий : Якщо його не ввести, він не буде інстанційним ніколи. Тому для його використання доведеться ввести його в модуль.
  2. Синглтон : Якщо ввести кілька модулів, всі матимуть доступ лише до одного конкретного екземпляра. Ось чому дуже зручно ділитися даними на різних контролерах.

ЗАВОД

Спочатку давайте подивимось на синтаксис:

app.js :

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

Тепер використовуємо зазначені вище в контролері:

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

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

Особливості заводу:

  1. Дотримується заводської схеми дизайну. Фабрика - це центральне місце, яке виробляє нові об'єкти чи функції.
  2. Випускає не лише одиночні, але й настроювані послуги.
  3. .service()Метод є заводом , який завжди справляє один і той же тип сервісу, який одноелементен, і без якого - або простого способу налаштувати його поведінку. Цей .service()метод зазвичай використовується як ярлик для чогось, що не вимагає жодної конфігурації.

ПОСТАВНИК

Давайте ще раз спочатку розглянемо синтаксис:

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

}

Особливості Постачальника:

  1. Провайдер - це найбільш гнучкий метод створення послуг в Angular.
  2. Ми не тільки можемо створити фабрику, яка динамічно налаштовується, але під час використання фабрики за допомогою методу постачальника ми змогли налаштувати завод лише один раз під час завантаження всієї нашої програми.
  3. Потім фабрику можна використовувати у всій програмі зі спеціальними налаштуваннями. Іншими словами, ми можемо налаштувати цю фабрику до запуску програми. Насправді в кутовій документації зазначається, що метод постачальника - це те, що насправді виконується за кадром, коли ми налаштовуємо наші сервіси за допомогою методів .serviceабо .factory.
  4. Це $getфункція, яка безпосередньо приєднана до екземпляра постачальника. Ця функція є заводською функцією. Іншими словами, це так само , як той , який ми використовуємо , щоб забезпечити до .factoryметоду. У цій функції ми створюємо власну службу. Ця $getвластивість, яка є функцією, - це те, що робить провайдера постачальником . AngularJS розраховує, що постачальник має властивість $ get, значенням якої є функція, яку Angular буде розглядати як заводську функцію. Але те, що робить цілу настройку всього провайдера дуже особливою, це той факт, що ми можемо надати деякий configоб’єкт всередині постачальника послуг, і це, як правило, поставляється із за замовчуванням, яке ми можемо згодом перезаписати на етапі, де ми можемо налаштувати всю програму.

7

Фабрика: Фабрика, на якій ви фактично створюєте об'єкт всередині фабрики та повертаєте його.
послуга: Служба, у якої ви просто маєте стандартну функцію, яка використовує це ключове слово для визначення функції.
провайдер: У постачальника є визначене вами $ get, яке може бути використане для отримання об'єкта, який повертає дані.


7

По суті, Постачальник, Фабрика та Сервіс - це всі Послуги. Фабрика - це особливий випадок Сервісу, коли все, що вам потрібно, - це функція $ get (), що дозволяє писати її з меншим кодом.

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

Ось коротка інформація про те, коли користуватися кожним:

Фабрика : вартість, яку ви надаєте, повинна бути обчислена на основі інших даних.

Сервіс : Ви повертаєте об'єкт методами.

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


ерм. Значення, Фабрика, Сервіс та Постійне - це лише синтаксичний цукор на вершині рецепта постачальника. Angularjs docs - провайдери
Sudarshan_SMD

так, я згоден, зараз з кутовим 4 у нас більше не виникає цього головного болю
eGhoul

4

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

2. Найпростіший спосіб створити послугу, використовуючи метод factory (). Фабричний () метод дозволяє нам визначити послугу, повернувши об’єкт, що містить сервісні функції та сервісні дані. Функція визначення послуги - це місце, де ми розміщуємо наші ін'єкційні послуги, такі як $ http та $ q. Наприклад:

angular.module('myApp.services')
.factory('User', function($http) { // injectables go here
var backendUrl = "http://localhost:3000"; var service = {
    // our factory definition
user: {},
setName: function(newName) {
      service.user['name'] = newName;
    },
setEmail: function(newEmail) { service.user['email'] = newEmail;
},
save: function() {
return $http.post(backendUrl + '/users', { user: service.user
}); }
};
return service; });

Використання заводу () у нашому додатку

Використовувати фабрику у нашому додатку просто, оскільки ми можемо просто вводити її там, де нам це потрібно під час роботи.

angular.module('myApp')
.controller('MainController', function($scope, User) {
  $scope.saveUser = User.save;
});
  1. Метод service (), з іншого боку, дозволяє нам створити сервіс шляхом визначення функції конструктора. Ми можемо використовувати прототипний об’єкт для визначення нашого сервісу замість необробленого об’єкта javascript. Подібно до методу factory (), ми також встановимо ін'єкційні елементи у визначенні функції.
  2. Спосіб створення служби найнижчого рівня - це використання методу Provid (). Це єдиний спосіб створити службу, яку ми можемо налаштувати за допомогою функції .config (). На відміну від попередніх методів, ми встановимо ін'єкційні елементи у визначене це визначення. $ Get () функція.

-3

Синтаксичний цукор - це різниця . Потрібен лише провайдер. Або іншими словами, лише провайдер - це справжній кут, всі інші - отримані (щоб зменшити код). Існує також проста версія, що називається Value (), яка повертає просто значення, без обчислення або функції. Навіть цінність отримується від провайдера!

То чому такі ускладнення, чому ми не можемо просто скористатися провайдером і забути все інше? Він повинен допомогти нам легко писати код і краще спілкуватися. І чим жорсткішою буде відповідь, тим складнішою вона стане, тим краще продаватимуться рамки.


  • Постачальник, який може повернути значення = Значення
  • Постачальник, який може просто інстанціювати та повертати = Factory (+ Значення)
  • Постачальник, який може створити інстанцію + зробити щось = послуга (+ завод, + вартість)
  • Постачальник послуг = повинен містити властивість під назвою $ get (+ Factory, + Service, + Value)

Кутова ін'єкція дає нам першу підказку для досягнення цього висновку.

"$ injector використовується для отримання екземплярів об'єктів, визначених постачальником ", не служба, не фабрика, а постачальник.

І кращою відповіддю буде така: "Кутовий сервіс створюється фабрикою послуг. Ці фабрики послуг - це функції, які, в свою чергу, створюються постачальником послуг. Постачальники послуг - це функції конструктора. При інстанції вони повинні містити властивість називається $ get, який утримує функцію фабрики послуг. "

Тож майстер-постачальник та інжектор і все стане на свої місця :). І в Typescript стає цікаво, коли $ get може бути реалізований у провайдера, успадкувавши від IServiceProvider.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.