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


3322

Каковы различия между Service, Providerи Factoryв AngularJS?


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

7
На мой взгляд, лучший способ понять разницу - это использовать собственную документацию Angular: docs.angularjs.org/guide/providers это очень хорошо объяснено и использует специфический пример, чтобы помочь вам понять это.
Рафаэль Мерлин

3
@ Blaise Спасибо! Согласно моему комментарию в посте, я умышленно исключил его, поскольку 99% случаев использования из моего опыта могут быть успешно обработаны с помощью service.factory. Не хотел усложнять эту тему дальше.
demisx

3
Я нахожу это обсуждение также очень полезным stackoverflow.com/questions/18939709/…
Ананд Гупта

3
Вот несколько хороших ответов о томservices,какfactoriesи какprovidersработает.
Мисталис

Ответы:


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

Но что, если вы хотите быть более интересным и иметь класс под названием 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 Demo

Пример "Hello world" с 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
@ Брэндон Ох, ладно, тогда вроде как. Смущает на первый взгляд - спасибо за разъяснения!
Нейт-Уилкинс,

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

4
Это причина, по которой @Antoine получает ошибку «Неизвестный провайдер: helloWorldProvider», потому что в вашем коде .config вы используете «helloWorldProvider», но когда вы определяете провайдера в myApp.provider («helloWorld», function ()), вы используете 'Привет мир'? Другими словами, как Angular узнает, что вы обращаетесь к провайдеру 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 создает его за кулисами с ключевым словом «new». Из-за этого вы добавите свойства в «this», и сервис вернет «this». Когда вы передаете услугу в свой контроллер, эти свойства в «this» теперь будут доступны на этом контроллере через ваш сервис.

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

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



3) Поставщики - это единственная услуга, которую вы можете передать в функцию .config (). Используйте провайдера, если вы хотите предоставить конфигурацию всего модуля для объекта службы, прежде чем сделать его доступным.

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

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

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



Не TL; DR

1)
Фабрики - это самый популярный способ создания и настройки сервиса. Там действительно не намного больше, чем то, что сказал TL; DR. Вы просто создаете объект, добавляете к нему свойства, а затем возвращаете тот же объект. Затем, когда вы передадите фабрику в свой контроллер, эти свойства объекта теперь будут доступны в этом контроллере через вашу фабрику. Более подробный пример приведен ниже.

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

Здесь вы заметите, что мы не привязываем эти переменные / функцию к 'service'. Мы просто создаем их, чтобы потом использовать или модифицировать их.

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

Теперь, когда наши вспомогательные / закрытые переменные и функции на месте, давайте добавим некоторые свойства к объекту 'service'. Все, что мы ставим на «сервис», можно напрямую использовать внутри любого контроллера, в который мы передаем «myFactory».

Мы собираемся создать методы setArtist и getArtist, которые просто возвращают или устанавливают художника. Мы также собираемся создать метод, который будет вызывать iTunes API с нашим созданным 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». Затем мы устанавливаем свойства нашего объекта $ scope с данными из myFactory. Единственный сложный код выше, если вы никогда не имели дело с обещаниями раньше. Поскольку callItunes возвращает обещание, мы можем использовать метод .then () и устанавливать $ scope.data.artistData только после того, как наше обещание будет выполнено с данными iTunes. Вы заметите, что наш контроллер очень «тонкий» (это хорошая практика кодирования). Вся наша логика и постоянные данные находятся в нашем сервисе, а не в нашем контроллере.

2) Сервис
Возможно, самая важная вещь, которую нужно знать при создании Сервиса, это то, что он создается с ключевым словом «new». Для вас, гуру JavaScript, это должно дать вам подсказку о природе кода. Для тех из вас, кто имеет ограниченный опыт работы с JavaScript, или тех, кто не слишком знаком с тем, что на самом деле делает ключевое слово «new», давайте рассмотрим некоторые основы JavaScript, которые в конечном итоге помогут нам понять природу Сервиса.

Чтобы действительно увидеть изменения, которые происходят при вызове функции с ключевым словом «new», давайте создадим функцию и вызовем ее с ключевым словом «new», а затем покажем, что делает интерпретатор, когда видит ключевое слово «new». Конечные результаты будут одинаковыми.

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

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

Это типичная функция конструктора JavaScript. Теперь всякий раз, когда мы вызываем функцию Person с помощью ключевого слова new, this будет привязано к вновь созданному объекту.

Теперь давайте добавим метод в прототип нашего Person, чтобы он был доступен для каждого экземпляра нашего класса Person.

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

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

Теперь, когда у нас есть функция конструктора Person и наша функция sayName в ее прототипе, давайте создадим экземпляр Person, а затем вызовем функцию sayName.

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

Таким образом, весь код для создания конструктора Person, добавления функции к его прототипу, создания экземпляра Person и последующего вызова функции для его прототипа выглядит следующим образом.

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

Теперь давайте посмотрим, что на самом деле происходит, когда вы используете ключевое слово «new» в 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 должно быть проще для понимания.

Самая важная вещь, которую нужно понять при создании Сервиса, это знание того, что Сервисы создаются с помощью ключевого слова «new». Объединяя эти знания с нашими примерами выше, вы теперь должны понимать, что вы будете привязывать свои свойства и методы непосредственно к «this», которое затем будет возвращено из самой Службы. Давайте посмотрим на это в действии.

В отличие от того, что мы изначально делали с примером Factory, нам не нужно создавать объект, а затем возвращать этот объект, потому что, как упоминалось много раз ранее, мы использовали ключевое слово «new», поэтому интерпретатор создаст этот объект, предоставив ему делегирование это прототип, а затем верните его нам без необходимости выполнять работу.

Прежде всего, давайте создадим нашу «приватную» и вспомогательную функцию. Это должно выглядеть очень знакомо, так как мы сделали то же самое с нашим заводом. Я не буду объяснять, что здесь делает каждая строка, потому что я сделал это в заводском примере, если вы не уверены, перечитайте заводской пример.

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

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

Теперь мы прикрепим все наши методы, которые будут доступны в нашем контроллере, к «this».

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

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

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

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

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

});

Теперь, как и на нашей фабрике, setArtist, getArtist и callItunes будут доступны в любом контроллере, в который мы передаем myService. Вот контроллер myService (который почти такой же, как наш заводской контроллер).

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

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

Как я уже упоминал ранее, когда вы действительно понимаете, что делает «новое», Сервисы практически идентичны фабрикам в AngularJS.

3) Провайдер

Самое важное, что нужно помнить о провайдерах, это то, что они являются единственной службой, которую вы можете передать в часть app.config вашего приложения. Это имеет огромное значение, если вам нужно изменить какую-то часть вашего сервисного объекта, прежде чем он станет доступен везде в вашем приложении. Хотя это очень похоже на Услуги / Фабрики, есть несколько различий, которые мы обсудим.

Сначала мы настраиваем нашего провайдера аналогично тому, как мы работали с нашим сервисом и фабрикой. Переменные ниже являются нашей «приватной» и вспомогательной функцией.

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

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

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

* Опять же, если какая-либо часть приведенного выше кода вводит в заблуждение, ознакомьтесь с разделом «Фабрика», где я объясняю, для чего все это более подробно.

Вы можете думать о провайдерах как о трех разделах. Первый раздел - это «частные» переменные / функции, которые будут изменены / установлены позже (показано выше). Второй раздел - это переменные / функции, которые будут доступны в вашей функции app.config и, следовательно, доступны для изменения до того, как они станут доступны где-либо еще (также показано выше). Важно отметить, что эти переменные должны быть присоединены к ключевому слову this. В нашем примере, только 'thingFromConfig' будет доступен для изменения в app.config. Третий раздел (показанный ниже) - это все переменные / функции, которые будут доступны в вашем контроллере, когда вы передадите службу myProvider в этот конкретный контроллер.

При создании службы с помощью Provider в вашем контроллере будут доступны только те свойства / методы, которые возвращаются из функции $ get (). Приведенный ниже код помещает $ get в this (которое, как мы знаем, будет возвращено из этой функции). Теперь эта функция $ get возвращает все методы / свойства, которые мы хотим быть доступными в контроллере. Вот пример кода.

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

Теперь полный код провайдера выглядит следующим образом

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

Теперь, как и на нашей фабрике, Service, setArtist, getArtist и callItunes будут доступны в любом контроллере, в который мы передаем myProvider. Вот контроллер myProvider (который почти такой же, как наш заводской / сервисный контроллер).

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

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

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

Как упоминалось ранее, весь смысл создания службы с помощью Provider заключается в возможности изменять некоторые переменные с помощью функции app.config до того, как конечный объект будет передан остальной части приложения. Давайте посмотрим пример этого.

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

Теперь вы можете видеть, как 'thingFromConfig' является пустой строкой в ​​нашем провайдере, но когда это появится в DOM, это будет "Это предложение было установлено ...".


11
Единственная часть, которая отсутствует в этой превосходной статье, - это относительные преимущества использования сервиса над фабрикой; что ясно объясняется в принятом ответе Лиора
бесконечность

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

3
Изюминка «JavaScript-гуру» была хитрой. : D Думаю, этот ответ очень многое прояснит. Великолепно написано.
Амармишра

4
Вашему TLDR нужен TLDR.
JensB

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

512

Все Сервисы являются одиночными ; они создаются один раз за приложение. Они могут быть любого типа , будь то примитив, литерал объекта, функция или даже экземпляр пользовательского типа.

value, factory, service, constant, И providerметоды все провайдеры. Они учат Инжектора, как создавать Услуги.

Наиболее многословным, но и наиболее полным является рецепт провайдера. В остальных четырех типов рецептов - Value, фабрика, Обслуживание и Constant - это просто синтаксический сахар поверх рецепта поставщика .

  • Рецепт Value является наиболее простым случаем, когда вы создаете экземпляр сервиса самостоятельно и предоставить конкретизированное значение для инжектора.
  • Рецепт Фабрика дает Инжектор функцию заводскую , что он вызывает , когда необходимо создать экземпляр службы. При вызове фабричная функция создает и возвращает экземпляр службы. Зависимости Сервиса вводятся как аргументы функций. Поэтому использование этого рецепта добавляет следующие способности:
    • Возможность использования других сервисов (есть зависимости)
    • Служба инициализации
    • Отложенная / ленивая инициализация
  • Рецепт Сервиса почти такие же , как рецепт фабрики, но здесь Injector вызывает конструктор с новым оператором вместо функции фабрики.
  • Рецепт поставщика , как правило , избыточен . Он добавляет еще один уровень косвенности, позволяя настроить создание фабрики.

    Вы должны использовать рецепт провайдера только в том случае, если вы хотите предоставить API для конфигурации всего приложения, которую необходимо выполнить до запуска приложения. Обычно это интересно только для многоразовых сервисов, чье поведение может немного отличаться в разных приложениях.

  • Constant рецепт просто как рецепт Значение кроме этого позволяет определить услуги, которые доступны в конфигурации фазы. Раньше, чем сервисы, созданные с использованием рецепта Value. В отличие от значений, они не могут быть оформлены с использованием decorator.
Смотрите документацию поставщика .


2
Значит сервис и фабрика по сути одинаковы? Использование одного другого обеспечивает ничего, кроме альтернативного синтаксиса?
Мэтт

2
@ Матт, да, сервис - это лаконичный способ, когда у вас уже есть собственная функция, которую вы хотите представить как сервис. Из документов: myApp.factory ('unicornLauncher', ["apiToken", function (apiToken) {вернуть новый UnicornLauncher (apiToken);}]); vs: myApp.service ('unicornLauncher', ["apiToken", UnicornLauncher]);
Янв

5
@joshperry Как новичок, я некоторое время гуглял разницу между сервисом и фабрикой. Я согласен, что это лучший ответ! Я бы понял службу как класс службы (например, класс кодировщика / декодера), который может иметь некоторые частные свойства. И фабрика предоставляет набор вспомогательных методов без сохранения состояния.
stanleyxu2005

3
Примеры Yaa в других ответах, приведенных выше, не очень четко объясняют основное различие между ч / б услугами и провайдерами, которое и вводится в то время, когда создаются эти рецепты.
Ашиш Сингх

223

Понимание AngularJS Factory, Сервис и Поставщик

Все они используются для совместного использования одноразовых объектов. Это помогает обмениваться повторно используемым кодом через ваше приложение / различные компоненты / модули.

Из Документов Сервис / Фабрика :

  • Ленивый экземпляр - 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

НОТА

Только поставщик будет доступен в фазе конфигурации angular, а сервис и фабрика - нет.

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


1
Что бы я сделал, если бы я хотел иметь службу с определенным интерфейсом, но иметь две разные реализации и внедрять каждую в контроллер, но привязывать к различным состояниям с помощью ui-router? например, делать удаленные вызовы в одном состоянии, но вместо этого записывать в локальное хранилище. Документы провайдера говорят, чтобы использовать 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, так что не кажется возможным, верно?
9

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 и т. Д., Поэтому вы можете использовать синтаксис их классов. Вам нужны providers, только если ваш модуль используется в нескольких приложениях с разными настройками при помощи app.config(). Если ваш сервис является чисто одноэлементным или способен создавать экземпляры чего-либо, зависит только от вашей реализации.
Андреас Линнерт

137

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

Я пытаюсь сделать это простым. Это все о базовой концепции JavaScript.

Прежде всего, давайте поговорим об услугах в AngularJS!

Что такое Сервис: В AngularJS, Сервисэто не что иное, как одиночный объект JavaScript, который может хранить некоторые полезные методы или свойства. Этот одноэлементный объект создается на основе ngApp (приложение Angular) и используется всеми контроллерами в текущем приложении. Когда 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, будет связано с serviceInstance. Вместо того, чтобы возвращать объект, мы также можем возвращать функцию, значения и т. Д. Что бы мы ни возвращали, они будут доступны экземпляру службы.

Пример:

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

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

Сервисная функция:

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

Это еще один способ, мы можем зарегистрировать услугу. Единственное отличие состоит в том, как AngularJS пытается создать экземпляр объекта службы. На этот раз angular использует ключевое слово «new» и вызывает функцию конструктора, как показано ниже.

var serviceInstance = new fnServiceConstructor();

В функции конструктора мы можем использовать ключевое слово this для добавления свойств / методов к объекту службы. пример:

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

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

Функция Provider () - это еще один способ создания сервисов. Пусть нам интересно создать сервис, который просто отображает какое-то приветственное сообщение пользователю. Но мы также хотим предоставить такую ​​функциональность, чтобы пользователь мог установить собственное приветственное сообщение. В техническом плане мы хотим создавать настраиваемые сервисы. Как мы можем это сделать ? Должен быть способ, чтобы приложение могло передавать свои пользовательские приветственные сообщения, а 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 метода 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 вызывает эту функцию конструктора, используя ключевое слово «new» для создания экземпляра сервиса. serviceInstance = new fnServiceConstructor ();

Provider определяет функцию providerConstructor, эта функция providerConstructor определяет фабричную функцию $ get . Angular вызывает $ get () для создания объекта службы. Синтаксис провайдера имеет дополнительное преимущество в настройке объекта службы перед его созданием. serviceInstance = $ get ();


87

Как правильно отметили несколько человек, фабрика, поставщик, сервис и даже значение и константа являются версиями одного и того же. Вы можете разбить более общее providerна всех из них. Вот так:

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

Вот статья, из которой это изображение:

http://www.simplygoodcode.com/2015/11/the-difference-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 вызовет new, чтобы создать ее экземпляр. Это экземпляр, который создает AngularJS, который будет кэшироваться и вставляться при запросе сервиса. Поскольку new использовался для создания экземпляра службы, ключевое слово this является действительным и относится к экземпляру.

Пример:

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

Применение:

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

поставщик

Вы даете 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 в разделах о методах value и constant.

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: используется для совместного использования служебных функций со ссылкой на службу в контроллере. Служба является одноэлементной по своей природе, поэтому для одного сервиса в браузере создается только один экземпляр, и одна и та же ссылка используется на всей странице.

В сервисе мы создаем имена функций как свойства этого объекта.

AngularJS Factory: назначение Factory также совпадает с Service, однако в этом случае мы создаем новый объект и добавляем функции в качестве свойств этого объекта, а в конце возвращаем этот объект.

Поставщик AngularJS: цель этого снова та же, однако поставщик предоставляет вывод своей функции $ get.

Определение и использование Сервиса, Фабрики и Поставщика объясняются по адресу http://www.dotnetfunda.com/articles/show/3156/difference-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()или что-то :)
Джо

33

Мои разъяснения по этому вопросу:

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

Хотя глобальные переменные не рекомендуются, реальное использование этих глобальных переменных заключается в обеспечении внедрения зависимостей путем передачи переменной соответствующему контроллеру.

Существует много уровней сложности при создании значений для «глобальных переменных»:

  1. Константа
    Это определяет фактическую константу, которая не должна изменяться в течение всего приложения, так же как и константы в других языках (чего нет в JavaScript).
  2. Значение
    Это изменяемое значение или объект, и он служит некоторой глобальной переменной, которую можно даже внедрить при создании других служб или фабрик (см. Далее). Тем не менее, это должно быть « буквальное значение », что означает, что нужно выписать фактическое значение и не может использовать какую-либо логику вычислений или программирования (другими словами, 39 или myText или {prop: «value»} в порядке, но 2 +2 нет).
  3. Фабрика
    Более общее значение, которое можно вычислить сразу. Он работает путем передачи функции в AngularJS с логикой, необходимой для вычисления значения, и AngularJS выполняет ее, и сохраняет возвращаемое значение в именованной переменной.
    Обратите внимание, что можно вернуть объект (в этом случае он будет функционировать аналогично службе ) или функцию (которая будет сохранена в переменной как функция обратного вызова).
  4. Служба
    Служба - это более урезанная версия фабрики, которая действует только тогда, когда значение является объектом, и она позволяет писать любую логику непосредственно в функции (как если бы она была конструктором), а также объявлять и получать доступ свойства объекта с использованием этого ключевого слова.
  5. Провайдер
    В отличие от сервиса, который является упрощенной версией фабрики , провайдер является более сложным, но более гибким способом инициализации «глобальных» переменных, при этом наибольшей гибкостью является возможность установки значений из app.config.
    Это работает как использование комбинации службы и поставщика , передавая поставщику функцию, свойства которой объявлены с использованием ключевого слова this , которое можно использовать из app.config.
    Затем он должен иметь отдельную функцию $ .get, которая выполняется AngularJS после установки указанных выше свойств через app.configфайл, и эта функция $ .get ведет себя так же, как фабрика выше, тем, что его возвращаемое значение используется для инициализации «глобальных» переменных.

26

Мое понимание очень просто ниже.

Фабрика: вы просто создаете объект внутри фабрики и возвращаете его.

Обслуживание:

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

Поставщик:

Существует $getобъект, который вы определяете, и его можно использовать для получения объекта, который возвращает данные.


Разве вы не перепутали Фабрику и Сервис? Служба создает, где фабрика возвращается.
Флавиен Волкен

Когда вы объявляете имя службы как вводимый аргумент, вам будет предоставлен экземпляр функции. Другими словами, новый FunctionYouPassedToService (). Этот экземпляр объекта становится сервисным объектом, который AngularJS регистрирует и затем внедряет в другие сервисы / контроллеры, если это необходимо. // factory Когда вы объявляете factoryname как вводимый аргумент, вам будет предоставлено значение, возвращаемое путем вызова ссылки на функцию, переданной в module.factory.
Саджан

Хорошо, так что ... в угловом виде фабрика - это синглтон, где "сервис" - это фактически фабрика (в общих чертах дизайна)
Флавиен Волкен,

25

Резюме из угловых документов :

  • Существует пять типов рецептов, которые определяют, как создавать объекты: Value , Factory , Service , Provider и Constant .
  • Фабрика и Сервис - наиболее часто используемые рецепты. Единственная разница между ними состоит в том, что рецепт Service лучше работает для объектов пользовательского типа, тогда как Factory может создавать примитивы и функции 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. Оба являются синглетонами : всякий раз, когда Angular находит их как зависимость в первый раз, он создает один экземпляр сервиса / фабрики. Как только экземпляр создан, тот же экземпляр используется навсегда.

  2. Может использоваться для моделирования объекта с поведением : у них могут быть методы, внутренние переменные состояния и т. Д. Хотя способ написания этого кода будет отличаться.

Сервисы:

Сервис - это функция конструктора, и Angular создаст его экземпляр, вызвав new yourServiceName(). Это означает пару вещей.

  1. Функции и переменные экземпляра будут свойствами this.
  2. Вам не нужно возвращать значение. Когда Angular вызывает new yourServiceName(), он получит thisобъект со всеми свойствами, которые вы на него наделили.

Пример примера:

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

Когда Angular внедряет этот MyServiceсервис в контроллер, который зависит от него, этот контроллер получит объект MyService, для которого он может вызывать функции, например 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 выше:

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

Однако я все еще называю их «услугами», когда говорю о внедрении их как зависимостей.

Поведение Service / Factory очень похоже, и некоторые люди скажут, что любой из них в порядке. Это несколько верно, но мне легче следовать советам руководства по стилю Джона Папы и просто придерживаться заводов. **


16

Дополнительным разъяснением является то, что фабрики могут создавать функции / примитивы, а службы - нет. Проверьте это jsFiddle на основе Epokk's: 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

Уже есть хорошие ответы, но я просто хочу поделиться этим.

Прежде всего: Provider - это способ / рецепт для создания service(одноэлементного объекта), который должен быть введен $ инжектором (как AngulaJS использует шаблон IoC).

И Value, Factory, Service и Constant (4 способа) - синтаксический сахар по сравнению с Provider way / 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

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

  • Инжектор использует рецепты для создания двух типов объектов: сервисов и объектов специального назначения.
  • Существует пять типов рецептов, которые определяют, как создавать объекты: Value, Factory, Service, Provider и Constant.
  • Фабрика и Сервис - наиболее часто используемые рецепты. Единственная разница между ними состоит в том, что рецепт Service лучше работает для объектов пользовательского типа, тогда как Factory может создавать примитивы и функции JavaScript.
  • Рецепт провайдера является основным типом рецепта, а все остальные - просто синтаксический сахар.
  • Поставщик - самый сложный тип рецепта. Вам это не нужно, если вы не создаете многократно используемый код, который нуждается в глобальной настройке.
  • Все объекты специального назначения, кроме Контроллера, определяются с помощью заводских рецептов.

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

И для начинающего понять: - Это может не исправить вариант использования, но на высоком уровне это то, что сценарий использования для этих трех.

  1. Если вы хотите использовать в модуле конфигурации угловой модуль должен быть создан в качестве поставщика

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

  1. Ajax-вызов или сторонние интеграции должны быть сервисными .
  2. Для манипулирования данными создайте его как фабрику

Для базовых сценариев фабрика и сервис ведут себя одинаково.


13

Вот пример кода, который я придумал как шаблон кода для фабрики объектов в AngularjS. Я использовал Car / 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

Используя в качестве ссылки эту страницу и документацию (которая, кажется, значительно улучшилась с тех пор, как я в последний раз смотрел), я собрал следующую (-ish) демонстрацию мира, в которой используются 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()вы возвращаете его, вы предоставляете factory () функцию, functionкоторая внедряет constructor(возвращает экземпляр конструктора, предоставленного вами в вашем сервисе) и возвращает его

Исходный код сервиса

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

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


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. Singleton : если внедрено в несколько модулей, все будут иметь доступ только к одному конкретному экземпляру. Именно поэтому очень удобно обмениваться данными между разными контроллерами.

ФАБРИКА

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

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

Фабрика: фабрика, на которой вы фактически создаете объект внутри фабрики и возвращаете его.
сервис: сервис, у вас просто есть стандартная функция, которая использует ключевое слово this для определения функции.
провайдер: в качестве провайдера вы определяете $ get, и его можно использовать для получения объекта, который возвращает данные.


7

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

Основными различиями между Сервисами, Фабриками и Поставщиками являются их сложности. Службы являются самой простой формой, фабрики немного более устойчивы, а провайдеры настраиваются во время выполнения.

Вот краткое изложение того, когда использовать каждый:

Фабрика : предоставляемое вами значение должно быть рассчитано на основе других данных.

Сервис : вы возвращаете объект с методами.

Поставщик : вы хотите иметь возможность настроить на этапе настройки объект, который будет создан до его создания. Используйте поставщика в основном в конфигурации приложения, прежде чем приложение будет полностью инициализировано.


эм. Value, Factory, Service и Constant - это просто синтаксический сахар на вершине рецепта поставщика. Документы Angularjs - провайдеры
Sudarshan_SMD

да, я согласен, теперь с угловым 4 у нас больше нет этой головной боли
eGhoul

4

1.Услуги - это одноэлементные объекты, которые создаются при необходимости и никогда не очищаются до конца жизненного цикла приложения (когда браузер закрыт). Контроллеры уничтожаются и очищаются, когда они больше не нужны.

2. Самый простой способ создать сервис - использовать метод factory (). Метод 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; });

Используя factory () в нашем приложении

В нашем приложении легко использовать фабрику, поскольку мы можем просто внедрить ее туда, где она нам нужна во время выполнения.

angular.module('myApp')
.controller('MainController', function($scope, User) {
  $scope.saveUser = User.save;
});
  1. Метод service (), с другой стороны, позволяет нам создавать сервис, определяя функцию конструктора. Мы можем использовать прототипный объект для определения нашего сервиса вместо необработанного объекта JavaScript. Подобно методу factory (), мы также установим инъецируемые элементы в определении функции.
  2. Самым низким способом создания сервиса является использование метода provide (). Это единственный способ создать службу, которую мы можем настроить с помощью функции .config (). В отличие от предыдущих методов, мы будем устанавливать инъецируемые объекты в определенном определении функции. $ Get ().

-3

Синтаксический сахар - это разница . Нужен только провайдер. Или, другими словами, реальным углом является только поставщик, все остальные являются производными (для уменьшения кода). Существует также простая версия, называемая Value (), которая возвращает только значение, без вычисления или функции. Четное значение получено от поставщика!

Так почему такие сложности, почему мы не можем просто использовать провайдера и забыть обо всем остальном? Предполагается, что это поможет нам легче писать код и лучше общаться. И ответ будет сложнее, чем сложнее он становится, тем лучше будет продажа фреймворка.


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

Угловая инъекция дает нам первый намек в достижении этого заключения.

«$ injector используется для извлечения экземпляров объекта, как определено поставщиком », не службой, не фабрикой, а провайдером.

И лучшим ответом было бы следующее: «Служба Angular создается фабрикой сервисов. Эти фабрики сервисов являются функциями, которые, в свою очередь, создаются поставщиком услуг. Поставщики услуг являются функциями конструктора. При реализации они должны содержать свойство называется $ get, который содержит функцию фабрики сервиса. "

Так что мастер провайдер и инжектор и все встанет на свои места :). И в Typescript становится интересным, когда $ get может быть реализован в провайдере путем наследования от IServiceProvider.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.