AngularJS: บริการกับผู้ให้บริการเทียบกับโรงงาน


3319

อะไรคือความแตกต่างระหว่างService, ProviderและFactoryใน AngularJS?


244
ฉันพบว่าคำศัพท์เชิงมุมทั้งหมดน่ากลัวสำหรับผู้เริ่มต้น เราเริ่มต้นด้วย cheatsheet นี้ว่าเป็นเพียงเล็กน้อยที่ง่ายขึ้นสำหรับโปรแกรมเมอร์ของเราที่จะเข้าใจขณะที่เรียนรู้เชิงมุมdemisx.github.io/angularjs/2014/09/14/... หวังว่านี่จะช่วยทีมของคุณด้วย
demisx

7
ในความคิดของฉันวิธีที่ดีที่สุดในการเข้าใจความแตกต่างคือการใช้เอกสารของ Angular: docs.angularjs.org/guide/providersมันอธิบายได้ดีมากและใช้ตัวอย่างที่แปลกประหลาดเพื่อช่วยให้คุณเข้าใจ
Rafael Merlin

3
@Blaise ขอบคุณ! ต่อความคิดเห็นของฉันในโพสต์ที่ฉันทิ้งมันออกจงใจตั้งแต่ 99% service.factoryของกรณีการใช้งานจากประสบการณ์ของผมสามารถจัดการได้ประสบความสำเร็จผ่านทาง ไม่ต้องการทำให้เรื่องนี้ซับซ้อนขึ้นอีก
demisx

3
ฉันพบว่าการสนทนานี้มีประโยชน์มากเช่นกันstackoverflow.com/questions/18939709/…
Anand Gupta

3
นี่คือบางส่วนคำตอบที่ดีเกี่ยวกับวิธีการservices,factoriesและprovidersผลงาน
Mistalis

คำตอบ:


2866

จากรายชื่อผู้รับจดหมายของ AngularJS ฉันได้รับหัวข้อที่น่าทึ่งซึ่งอธิบายถึงการบริการ vs โรงงานและผู้ให้บริการและการใช้งานการฉีดของพวกเขา รวบรวมคำตอบ:

บริการ

ไวยากรณ์: module.service( 'serviceName', function );
ผลลัพธ์: เมื่อประกาศ serviceName เป็นอาร์กิวเมนต์แบบฉีดคุณจะได้รับอินสแตนซ์ของฟังก์ชัน new FunctionYouPassedToService()กล่าวอีกนัยหนึ่ง

โรงงาน

ไวยากรณ์: module.factory( 'factoryName', function );
ผล: เมื่อประกาศ factoryName เป็นอาร์กิวเมนต์ฉีดที่คุณจะได้รับค่าที่ถูกส่งกลับโดยการเรียกใช้ฟังก์ชั่นการอ้างอิงที่ส่งผ่านไป module.factory

ผู้ให้บริการ

ไวยากรณ์: module.provider( 'providerName', function );
ผล: เมื่อประกาศ providername เป็นอาร์กิวเมนต์ฉีดที่คุณจะได้รับ (new ProviderFunction()).$get()ฟังก์ชั่นคอนสตรัคได้รับการยกตัวอย่างก่อนที่วิธีการ $ get เรียกว่า - ProviderFunctionคือการอ้างอิงฟังก์ชั่นที่ส่งผ่านไปยัง module.provider

ผู้ให้บริการมีข้อได้เปรียบที่สามารถกำหนดค่าได้ในช่วงการกำหนดค่าโมดูล

ดูที่นี่สำหรับรหัสที่ให้

นี่คือคำอธิบายเพิ่มเติมที่ยอดเยี่ยมโดย Misko:

provide.value('a', 123);

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

ในกรณีนี้หัวฉีดก็แค่คืนค่าตามที่เป็น แต่ถ้าคุณต้องการคำนวณค่าล่ะ? จากนั้นใช้โรงงาน

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

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

ดังนั้นfactoryหน้าที่ที่รับผิดชอบในการสร้างคุณค่า ขอให้สังเกตว่าฟังก์ชั่นจากโรงงานสามารถขอการพึ่งพาอื่น ๆ

แต่ถ้าคุณต้องการ OO มากกว่านี้และมีคลาสที่เรียกว่า Greeter

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

จากนั้นเพื่อยกตัวอย่างคุณจะต้องเขียน

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

ถ้าอย่างนั้นเราก็ขอ 'รู้ตัว' ในตัวควบคุมแบบนี้

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ซึ่งอธิบายถึงความแตกต่างระหว่างบริการและโรงงาน
Mark Rajcok

3
ในการแก้ไข 611 ฉันเพิ่มการใช้ค่าคงที่เชิงมุมและค่า เพื่อแสดงให้เห็นถึงความแตกต่างของอีกฝ่ายที่แสดงไว้แล้ว jsbin.com/ohamub/611/edit
Nick

17
แม้ว่าจะมีการเรียกใช้บริการโดยการสร้างอินสแตนซ์ของฟังก์ชั่น มันถูกสร้างขึ้นจริงเพียงครั้งเดียวต่อหัวฉีดซึ่งทำให้มันเหมือนซิงเกิล docs.angularjs.org/guide/dev_guide.services.creating_services
angelokh

33
ตัวอย่างนี้อาจไม่น่าเชื่อถ้ามันใช้เป็นตัวอย่างที่ชัดเจน ฉันหลงทางพยายามคิดออกว่าอะไรคือสิ่งที่เป็นtoEqualและgreeter.Greetเป็น ทำไมไม่ใช้สิ่งที่เป็นจริงและน่าเชื่อถือมากกว่านี้อีกเล็กน้อย
Kyle Pennell

5
การใช้ฟังก์ชั่น expect () เป็นตัวเลือกที่ดีในการอธิบายบางอย่าง ใช้รหัสโลกแห่งความจริงในครั้งต่อไป
Craig

812

การสาธิต JS Fiddle

ตัวอย่าง "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ฟังก์ชันหรือไม่ - คุณไม่ได้อ้างถึงผู้ให้บริการที่สร้างอินสแตนซ์ในฟังก์ชันนั้นอีกต่อไป
Nate-Wilkins

12
@Nate: thisไม่ได้เปลี่ยนบริบทจริงเพราะสิ่งที่ถูกเรียกว่าเป็นnew Provider(). $ ได้รับ () ซึ่งเป็นฟังก์ชั่นที่ถูกส่งผ่านไปยังProvider app.providerกล่าว$get()คือจะเรียกว่าเป็นวิธีการในการสร้างProviderดังนั้นthisจะหมายถึงProviderเป็นตัวอย่างที่แสดงให้เห็น
แบรนดอน

1
@Brandon Ohh เรียบร้อยแล้วใจดี สับสนได้อย่างรวดเร็วก่อน - ขอบคุณสำหรับการชี้แจง!
Nate-Wilkins

3
เหตุใดฉันจึงได้รับUnknown provider: helloWorldProvider <- helloWorldเมื่อเรียกใช้ภายในเครื่อง แสดงความคิดเห็นข้อผิดพลาดเดียวกันสำหรับอีก 2 ตัวอย่าง มีการกำหนดค่าผู้ให้บริการที่ซ่อนอยู่บ้างไหม? (เชิงมุม 1.0.8) - พบ: stackoverflow.com/questions/12339272/…
แอนทอน

4
นี่คือเหตุผลที่ @Antoine ได้รับข้อผิดพลาด "ไม่ระบุให้: helloWorldProvider" เพราะในรหัส. config คุณใช้ 'helloWorldProvider' แต่เมื่อคุณกำหนดผู้ให้บริการใน myApp.provider ('helloWorld', function ()) คุณใช้ 'สวัสดีชาวโลก'? กล่าวอีกนัยหนึ่งในรหัสการกำหนดค่าของคุณคุณรู้ได้อย่างไรว่าคุณอ้างอิงผู้ให้บริการ 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 จะยกตัวอย่างทันทีโดยใช้คำหลัก 'ใหม่' ด้วยเหตุนี้คุณจะเพิ่มคุณสมบัติให้กับ '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) Factory Factory
เป็นวิธีที่นิยมที่สุดในการสร้างและกำหนดค่าบริการ ไม่มีอะไรมากไปกว่าสิ่งที่ TL; DR กล่าว คุณเพิ่งสร้างวัตถุเพิ่มคุณสมบัติเข้าไปแล้วส่งคืนวัตถุเดียวกันนั้น จากนั้นเมื่อคุณส่งโรงงานไปยังคอนโทรลเลอร์ของคุณคุณสมบัติเหล่านั้นบนวัตถุจะมีอยู่ในคอนโทรลเลอร์นั้นผ่านโรงงานของคุณ ตัวอย่างที่กว้างขวางยิ่งขึ้นอยู่ด้านล่าง

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

ตอนนี้คุณสมบัติอะไรก็ตามที่เราแนบกับ 'บริการ' จะมีให้เราเมื่อเราส่ง 'myFactory' เข้าไปในตัวควบคุมของเรา

ตอนนี้เราจะเพิ่มตัวแปร 'ส่วนตัว' ลงในฟังก์ชันการโทรกลับของเรา สิ่งเหล่านี้จะไม่สามารถเข้าถึงได้โดยตรงจากคอนโทรลเลอร์ แต่ในที่สุดเราจะตั้งค่าเมธอด getter / setter ใน 'บริการ' เพื่อให้สามารถเปลี่ยนตัวแปร 'ส่วนตัว' เหล่านี้เมื่อจำเป็น

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 หลักที่ iTunes API ต้องการ
  • _artist เป็นศิลปินที่เราต้องการค้นหา
  • _finalUrl เป็น URL สุดท้ายและสร้างขึ้นอย่างสมบูรณ์ซึ่งเราจะทำการโทรไปที่ iTunes
  • makeUrl เป็นฟังก์ชั่นที่จะสร้างและส่งคืน URL ที่เป็นมิตรกับ iTunes ของเรา

ตอนนี้ผู้ช่วย / ตัวแปรส่วนตัวและฟังก์ชั่นของเราพร้อมใช้งานแล้วให้เพิ่มคุณสมบัติบางอย่างลงในออบเจ็กต์ 'บริการ' สิ่งที่เราใส่ใน 'บริการ' สามารถนำมาใช้โดยตรงภายในตัวควบคุมใดก็ตามที่เราผ่าน 'myFactory' เข้าไป

เราจะสร้างเมธอด setArtist และ getArtist ที่เพียงแค่คืนค่าหรือตั้งค่าศิลปิน เรากำลังจะสร้างวิธีการที่จะเรียก iTunes API ด้วย URL ที่สร้างขึ้นของเรา วิธีนี้จะคืนสัญญาที่จะตอบสนองเมื่อข้อมูลได้กลับมาจาก iTunes API หากคุณไม่เคยมีประสบการณ์การใช้คำสัญญาใน AngularJS มากนักฉันขอแนะนำให้ดำน้ำลึก

ด้านล่างsetArtistยอมรับศิลปินและอนุญาตให้คุณตั้งค่าศิลปิน getArtistส่งคืนศิลปิน callItunesสายแรก makeUrl () เพื่อสร้าง URL ที่เราจะใช้กับคำขอ $ http ของเรา จากนั้นจะตั้งค่าวัตถุสัญญาทำให้คำขอ $ http เป็น URL สุดท้ายของเราจากนั้นเนื่องจาก $ http ส่งคืนสัญญาเราจึงสามารถโทรหา. สำเร็จหรือ .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) บริการ
บางทีสิ่งที่สำคัญที่สุดที่ควรทราบเมื่อต้องจัดการกับการสร้างบริการคือการสร้างอินสแตนซ์ด้วยคำหลัก 'ใหม่' สำหรับคุณ gurus JavaScript คุณควรให้คำแนะนำอย่างละเอียดเกี่ยวกับลักษณะของรหัส สำหรับผู้ที่มีพื้นหลังที่ จำกัด ใน JavaScript หรือสำหรับผู้ที่ไม่คุ้นเคยกับสิ่งที่คำหลัก 'ใหม่' จริงลองมาตรวจทานพื้นฐาน JavaScript ที่จะช่วยเราในการทำความเข้าใจลักษณะของบริการ

หากต้องการดูการเปลี่ยนแปลงที่เกิดขึ้นเมื่อคุณเรียกใช้ฟังก์ชันด้วยคำหลัก 'ใหม่' ให้สร้างฟังก์ชันและเรียกใช้ด้วยคำหลัก 'ใหม่' จากนั้นให้แสดงสิ่งที่ล่ามทำเมื่อเห็นคำหลัก 'ใหม่' ผลลัพธ์ที่ได้จะเหมือนกันทั้งคู่

ก่อนอื่นมาสร้างตัวสร้างของเรา

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

นี่คือฟังก์ชันตัวสร้าง JavaScript ทั่วไป ตอนนี้เมื่อใดก็ตามที่เราเรียกใช้ฟังก์ชั่นบุคคลโดยใช้คำหลัก 'ใหม่' นี้จะถูกผูกไว้กับวัตถุที่สร้างขึ้นใหม่

ตอนนี้เราจะเพิ่มวิธีการลงบนต้นแบบของบุคคลของเราเพื่อให้สามารถใช้ได้กับทุก ๆ ตัวอย่างของ 'ชั้นเรียน' ของบุคคล

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

ตอนนี้เนื่องจากเราใส่ฟังก์ชั่น sayName ลงบนต้นแบบทุกอินสแตนซ์ของ Person จะสามารถเรียกใช้ฟังก์ชัน sayName เพื่อแจ้งเตือนการสั่งซื้อชื่อของอินสแตนซ์นั้น

ตอนนี้เรามีฟังก์ชั่นตัวสร้างบุคคลของเราและฟังก์ชั่น sayName ของเราบนต้นแบบแล้วลองสร้างตัวอย่างของบุคคลจากนั้นเรียกใช้ฟังก์ชั่น sayName

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

ดังนั้นทุกรหัสเข้าด้วยกันสำหรับการสร้างตัวสร้างบุคคลเพิ่มฟังก์ชั่นของมันเป็นต้นแบบสร้างอินสแตนซ์ของบุคคลแล้วเรียกฟังก์ชั่นบนต้นแบบของมันมีลักษณะเช่นนี้

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

ตอนนี้เรามาดูสิ่งที่เกิดขึ้นจริงเมื่อคุณใช้คำหลัก 'ใหม่' ใน JavaScript สิ่งแรกที่คุณควรสังเกตคือหลังจากใช้ 'ใหม่' ในตัวอย่างของเราเราสามารถเรียกเมธอด (sayName) บน 'tyler' เหมือนกับว่ามันเป็นวัตถุ - นั่นเป็นเพราะมันเป็น ดังนั้นก่อนอื่นเรารู้ว่าตัวสร้างบุคคลของเราส่งคืนวัตถุไม่ว่าเราจะเห็นว่าในโค้ดหรือไม่ ประการที่สองเรารู้ว่าเนื่องจากฟังก์ชั่น sayName ของเราตั้งอยู่บนต้นแบบและไม่ได้อยู่บนอินสแตนซ์ Person โดยตรงวัตถุที่ฟังก์ชัน Person กำลังส่งคืนจะต้องมอบสิทธิ์ให้กับต้นแบบในการค้นหาที่ล้มเหลว ในแง่ง่ายขึ้นเมื่อเราเรียก tyler.sayName () ล่ามบอกว่า“ ตกลงฉันจะดูที่วัตถุ 'tyler' ที่เราเพิ่งสร้างขึ้นค้นหาฟังก์ชัน sayName แล้วเรียกมัน เดี๋ยวก่อนฉันไม่เห็นที่นี่ - ทั้งหมดที่ฉันเห็นคือชื่อและอายุ ให้ฉันตรวจสอบต้นแบบ ใช่ดูเหมือนว่ามันอยู่บนต้นแบบให้ฉันเรียกมันว่า”

ด้านล่างคือรหัสสำหรับวิธีที่คุณสามารถคิดเกี่ยวกับสิ่งที่คำหลัก 'ใหม่' ทำงานจริงใน JavaScript มันเป็นตัวอย่างรหัสของย่อหน้าข้างต้น ฉันใส่ 'มุมมองล่าม' หรือวิธีที่ล่ามเห็นโค้ดในบันทึกย่อ

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

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

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

  //return this;
}

ตอนนี้มีความรู้ในสิ่งที่คำหลัก 'ใหม่' นี้จริงใน JavaScript การสร้างบริการใน AngularJS ควรจะเข้าใจง่ายขึ้น

สิ่งที่ใหญ่ที่สุดที่ควรทำความเข้าใจเมื่อสร้างบริการคือการรู้ว่าบริการได้รับการยกตัวอย่างด้วยคำหลัก 'ใหม่' เมื่อรวมความรู้ดังกล่าวกับตัวอย่างของเราข้างต้นแล้วคุณควรตระหนักว่าคุณกำลังแนบคุณสมบัติและวิธีการของคุณโดยตรงกับ 'นี้' ซึ่งจะส่งคืนจากบริการ ลองดูที่การทำงานนี้

ซึ่งแตกต่างจากที่เราทำกับตัวอย่างโรงงานในตอนแรกเราไม่จำเป็นต้องสร้างวัตถุจากนั้นคืนค่าวัตถุนั้นเพราะอย่างที่เคยกล่าวไว้หลายครั้งก่อนหน้านี้เราใช้คำหลัก 'ใหม่' เพื่อที่ล่ามจะสร้างวัตถุนั้น มันเป็นต้นแบบแล้วส่งคืนให้เราโดยที่เราไม่ต้องทำงาน

ก่อนอื่นมาสร้างฟังก์ชั่น 'ส่วนตัว' และตัวช่วยของเรา สิ่งนี้ควรดูคุ้นเคยเพราะเราทำสิ่งเดียวกันกับโรงงานของเรา ฉันจะไม่อธิบายว่าแต่ละบรรทัดทำอะไรที่นี่เพราะฉันทำอย่างนั้นในตัวอย่างจากโรงงานถ้าคุณงงให้อ่านตัวอย่างจากโรงงานอีกครั้ง

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

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

ตอนนี้เราจะแนบวิธีการทั้งหมดของเราที่จะมีอยู่ในตัวควบคุมของเรากับ 'นี้'

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

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

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

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

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

});

ตอนนี้เหมือนในโรงงานของเรา setArtist, getArtist และ 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.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' ลงในคอนโทรลเลอร์เฉพาะนั้น

เมื่อสร้างบริการกับผู้ให้บริการคุณสมบัติ / วิธีการเดียวที่จะมีในตัวควบคุมของคุณคือคุณสมบัติ / วิธีการเหล่านั้นซึ่งส่งคืนจากฟังก์ชัน $ get () รหัสด้านล่างทำให้ $ รับ 'นี้' (ซึ่งเรารู้ว่าในที่สุดจะถูกส่งกลับจากฟังก์ชั่นนั้น) ตอนนี้ฟังก์ชั่น $ get จะส่งคืนเมธอด / คุณสมบัติทั้งหมดที่เราต้องการให้มีอยู่ในคอนโทรลเลอร์ นี่คือตัวอย่างรหัส

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

ตอนนี้รหัสผู้ให้บริการแบบเต็มจะมีลักษณะเช่นนี้

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

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

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

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

ตอนนี้เหมือนในโรงงานและบริการของเรา setArtist, getArtist และ callItunes จะพร้อมใช้งานในตัวควบคุมใดก็ตามที่เราส่ง myProvider เข้าไป นี่คือตัวควบคุม myProvider (ซึ่งเกือบจะเหมือนกับตัวควบคุมโรงงาน / บริการของเรา)

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

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

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

ดังที่ได้กล่าวไว้ก่อนหน้านี้จุดทั้งหมดของการสร้างบริการกับผู้ให้บริการคือการสามารถเปลี่ยนแปลงตัวแปรบางอย่างผ่านฟังก์ชั่น app.config ก่อนที่วัตถุสุดท้ายจะถูกส่งผ่านไปยังส่วนที่เหลือของแอปพลิเคชัน ลองดูตัวอย่างของสิ่งนั้น

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

ตอนนี้คุณสามารถดูว่า 'thingFromConfig' เป็นสตริงว่างในผู้ให้บริการของเราได้อย่างไร แต่เมื่อสิ่งนั้นปรากฏใน DOM จะเป็น 'ประโยคนี้ถูกตั้งค่า ... '


11
ส่วนเดียวที่ขาดหายไปในการเขียนที่ยอดเยี่ยมนี้คือข้อดีข้อได้เปรียบของการใช้บริการผ่านโรงงาน ซึ่งมีการอธิบายอย่างชัดเจนในคำตอบที่ยอมรับโดย Lior
infinity

2
FWIW (อาจจะไม่มาก) นี่คือบล็อกเกอร์ที่มีปัญหากับ Angular และไม่ชอบผู้ให้บริการ codeofrob.com/entries/you-have-ruined-javascript.html
barlop

3
Punchline 'JavaScript gurus' มีเลศนัย : DI คิดว่าคำตอบนี้จะล้างสิ่งต่าง ๆ อย่างมาก เขียนได้อย่างยอดเยี่ยม
amarmishra

4
TLDR ของคุณต้องการ TLDR
JensB

3
@JensB tl; dr - เรียนรู้ตอบสนอง
Tyler McGinnis

512

บริการทั้งหมดอยู่singletons ; พวกเขาจะได้รับอินสแตนซ์หนึ่งครั้งต่อแอป พวกเขาสามารถเป็นประเภทใด ๆไม่ว่าจะเป็นดั้งเดิมวัตถุตัวอักษรฟังก์ชั่นหรือแม้กระทั่งตัวอย่างของประเภทที่กำหนดเอง

value, factory, service, constantและproviderวิธีการเป็นผู้ให้บริการทั้งหมด พวกเขาสอน Injector ถึงวิธีการยกตัวอย่างบริการ

verbose ที่สุด แต่ยังครอบคลุมมากที่สุดคือสูตรผู้ให้บริการ เหลืออีกสี่ประเภทสูตร - ราคา, โรงงาน, บริการและคงที่ - เป็นเพียงน้ำตาลประโยคด้านบนของสูตรผู้ให้บริการ

  • สูตรราคาเป็นกรณีที่ง่ายที่คุณยกตัวอย่างบริการด้วยตัวเองและให้ค่า instantiatedจะหัวฉีด
  • สูตรโรงงานให้หัวฉีดฟังก์ชั่นโรงงานที่เรียกเมื่อต้องการยกตัวอย่างบริการ เมื่อเรียกใช้ฟังก์ชันโรงงานจะสร้างและส่งคืนอินสแตนซ์บริการ การอ้างอิงของบริการจะถูกฉีดเป็นอาร์กิวเมนต์ของฟังก์ชัน ดังนั้นการใช้สูตรนี้เพิ่มความสามารถดังต่อไปนี้:
    • ความสามารถในการใช้บริการอื่น ๆ (มีการอ้างอิง)
    • การเริ่มต้นบริการ
    • การเริ่มต้นล่าช้า / สันหลังยาว
  • สูตรบริการเกือบจะเป็นเช่นเดียวกับสูตรโรงงาน แต่นี่หัวฉีดจะเรียกconstructorกับผู้ประกอบการใหม่แทนการทำงานของโรงงาน
  • สูตรของผู้ให้บริการมักจะoverkill มันเพิ่มเลเยอร์ทางอ้อมอีกหนึ่งชั้นโดยอนุญาตให้คุณกำหนดค่าการสร้างโรงงาน

    คุณควรใช้สูตรผู้ให้บริการเฉพาะเมื่อคุณต้องการเปิดเผย API สำหรับการกำหนดค่าทั่วทั้งแอปพลิเคชันที่ต้องทำก่อนที่แอปพลิเคชันจะเริ่มต้น นี่เป็นเรื่องที่น่าสนใจสำหรับบริการที่สามารถนำกลับมาใช้ใหม่ได้ซึ่งพฤติกรรมอาจต้องแตกต่างกันเล็กน้อยระหว่างแอปพลิเคชัน

  • สูตรคงเป็นเช่นเดียวกับสูตรราคายกเว้นว่ามันจะช่วยให้คุณกำหนดบริการที่มีอยู่ในการตั้งค่าขั้นตอน เร็วกว่าบริการที่สร้างขึ้นโดยใช้สูตรมูลค่า decoratorซึ่งแตกต่างจากค่าที่พวกเขาไม่สามารถที่จะตกแต่งโดยใช้
ดูเอกสารที่ผู้ให้บริการ


2
ดังนั้นการบริการและโรงงานก็เหมือนกันใช่ไหม? การใช้ข้อใดข้อหนึ่งให้อะไรนอกจากไวยากรณ์ทางเลือกหรือไม่
Matt

2
@ แมทใช่บริการเป็นวิธีที่รัดกุมเมื่อคุณมีฟังก์ชั่นของคุณเองซึ่งคุณต้องการแสดงเป็นบริการ จาก docs: myApp.factory ('unicornLauncher', ["apiToken", ฟังก์ชัน (apiToken) {ส่งคืน UnicornLauncher ใหม่ (apiToken);}]); vs: myApp.service ('unicornLauncher', ["apiToken", UnicornLauncher]);
janek

5
@ Joshersry ในฐานะมือใหม่ฉันได้ทำความเข้าใจกับความแตกต่างระหว่างการบริการและโรงงานมาระยะหนึ่งแล้ว ฉันเห็นด้วยว่านี่เป็นคำตอบที่ดีที่สุด! ฉันเข้าใจบริการเป็นคลาสบริการ (เช่นคลาสของตัวเข้ารหัส / ตัวถอดรหัส) ซึ่งอาจมีคุณสมบัติส่วนตัวบางอย่าง และโรงงานจัดทำชุดวิธีการไร้สัญชาติ
stanleyxu2005

3
ตัวอย่างของ Yaa ในคำตอบอื่น ๆ ข้างต้นล้มเหลวในการอธิบายอย่างชัดเจนถึงความแตกต่างที่สำคัญของบริการ b / w และผู้ให้บริการซึ่งเป็นสิ่งที่ถูกฉีดในเวลาที่สูตรเหล่านี้มีอินสแตนซ์
Ashish Singh

223

ทำความเข้าใจกับโรงงาน AngularJS การบริการและผู้ให้บริการ

ทั้งหมดนี้ใช้เพื่อแชร์วัตถุซิงเกิลตันที่สามารถใช้ซ้ำได้ ช่วยในการแบ่งปันรหัสที่ใช้ซ้ำได้ในแอพของคุณ / ส่วนประกอบ / โมดูลต่างๆ

จากบริการเอกสาร/ โรงงาน :

  • Lazily instantiated - 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;
});

การใช้

มันอาจเป็นเพียงชุดของฟังก์ชั่นเช่นคลาส ดังนั้นมันสามารถสร้างอินสแตนซ์ในตัวควบคุมที่แตกต่างกันเมื่อคุณฉีดเข้าไปในฟังก์ชั่นคอนโทรลเลอร์ / โรงงาน / คำสั่งของคุณ มันเป็นอินสแตนซ์เพียงครั้งเดียวต่อแอป

บริการ

เพียงแค่มองไปที่บริการต่างๆให้นึกถึงอาเรย์ต้นแบบ บริการคือฟังก์ชั่นที่ทำให้วัตถุใหม่โดยใช้คำสำคัญ 'ใหม่' คุณสามารถเพิ่มคุณสมบัติและฟังก์ชั่นให้กับวัตถุบริการโดยใช้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-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ดังนั้นจึงเป็นไปไม่ได้ใช่มั้ย
qix

191

สำหรับฉันการเปิดเผยมาถึงเมื่อฉันตระหนักว่าพวกเขาทั้งหมดทำงานในลักษณะเดียวกัน: โดยการทำงานบางอย่างเพียงครั้งเดียวเก็บค่าที่ได้รับจากนั้นก็ไอค่าที่เก็บไว้เดิมนั้นเมื่ออ้างอิงผ่านการฉีดพึ่งพาฉีดพึ่งพา

บอกว่าเรามี:

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

ความแตกต่างระหว่างสามคือ:

  1. aค่า 's fnเก็บไว้มาจากการทำงาน
  2. bค่า 's เก็บไว้มาจากไอเอ็นจีnewfn
  3. cค่า 's เก็บไว้มาจากครั้งแรกที่ได้รับตัวอย่างจากnewไอเอ็นจี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()หากบริการของคุณเป็นซิงเกิลบริสุทธิ์หรือสามารถสร้างอินสแตนซ์ของบางสิ่งบางอย่างขึ้นอยู่กับการใช้งานของคุณ
Andreas Linnert

137

บริการเทียบกับผู้ให้บริการและโรงงาน:

ฉันพยายามทำให้มันง่าย ทุกอย่างเกี่ยวกับแนวคิดพื้นฐานเกี่ยวกับ JavaScript

ก่อนอื่นเรามาพูดคุยเกี่ยวกับบริการใน AngularJS!

บริการคืออะไร: ใน AngularJS บริการเป็นอะไรนอกจากวัตถุ JavaScript เดี่ยวซึ่งสามารถเก็บวิธีการหรือคุณสมบัติที่มีประโยชน์บางอย่าง วัตถุแบบซิงเกิลนี้ถูกสร้างขึ้นตามพื้นฐาน ngApp (แอพเชิงมุม) และมันถูกใช้ร่วมกันระหว่างตัวควบคุมทั้งหมดภายในแอปปัจจุบัน เมื่อ Angularjs สร้างอินสแตนซ์ของออบเจ็กต์บริการมันลงทะเบียนออบเจ็กต์บริการนี้ด้วยชื่อบริการที่ไม่ซ้ำกัน ดังนั้นทุกครั้งที่เราต้องการอินสแตนซ์ของบริการ Angular ค้นหารีจิสทรีสำหรับชื่อบริการนี้และส่งกลับการอ้างอิงไปยังวัตถุบริการ เช่นที่เราสามารถเรียกใช้วิธีการเข้าถึงคุณสมบัติอื่น ๆ บนวัตถุบริการ คุณอาจมีคำถามว่าคุณสามารถใส่คุณสมบัติวิธีการในขอบเขตวัตถุของตัวควบคุม! ดังนั้นทำไมคุณต้องการบริการวัตถุ รู้รอบคือ: บริการต่าง ๆ ถูกใช้ร่วมกันในขอบเขตของคอนโทรลเลอร์ หากคุณวางคุณสมบัติ / วิธีการบางอย่างไว้ในวัตถุขอบเขตของคอนโทรลเลอร์จะสามารถใช้ได้กับขอบเขตปัจจุบันเท่านั้น

ดังนั้นหากมีสามขอบเขตคอนโทรลเลอร์ให้เป็นคอนโทรลเลอร์ A, คอนโทรลเลอร์ B และคอนโทรลเลอร์ C ทั้งหมดจะใช้อินสแตนซ์บริการเดียวกัน

<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 พยายามสร้างอินสแตนซ์ของวัตถุบริการ เวลานี้ใช้คำสำคัญ 'ใหม่' และเรียกใช้ฟังก์ชันตัวสร้าง

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 จะทำให้สามารถใช้งานได้ในฟังก์ชั่นโรงงาน / คอนสตรัคเตอร์ซึ่งสร้างอินสแตนซ์บริการของเรา ในฟังก์ชันตัวให้บริการเคส () ดังกล่าวทำงาน ใช้ผู้ให้บริการ () ฟังก์ชั่นเราสามารถสร้างบริการที่กำหนด

เราสามารถสร้างบริการที่กำหนดค่าได้โดยใช้ไวยากรณ์ผู้ให้บริการตามที่ระบุด้านล่าง

/*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 ();

บริการใช้ฟังก์ชันตัวสร้างและ Angular เรียกใช้ฟังก์ชันตัวสร้างนี้โดยใช้คำหลัก 'ใหม่' สำหรับการสร้างอินสแตนซ์ของบริการ serviceInstance = ใหม่ fnServiceConstructor ();

ผู้ให้บริการกำหนดฟังก์ชั่น providerConstructor ฟังก์ชัน providerConstructor นี้กำหนดฟังก์ชั่นโรงงาน$ ได้รับ Angular call $ get () เพื่อสร้างออบเจ็กต์บริการ ไวยากรณ์ของผู้ให้บริการมีข้อได้เปรียบเพิ่มเติมของการกำหนดค่าบริการวัตถุก่อนที่จะได้รับอินสแตนซ์ serviceInstance = $ รับ ();


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 จะเรียกใหม่เพื่อยกตัวอย่าง เป็นอินสแตนซ์ที่ 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

ฉันสังเกตเห็นสิ่งที่น่าสนใจเมื่อเล่นกับผู้ให้บริการ

การมองเห็นของ injectables จะแตกต่างกันสำหรับผู้ให้บริการกว่าสำหรับบริการและโรงงาน ถ้าคุณประกาศ AngularJS "คงที่" (ตัวอย่างเช่นmyApp.constant('a', 'Robert'); ) คุณสามารถฉีดเข้าไปในบริการโรงงานและผู้ให้บริการ

แต่ถ้าคุณประกาศ "ค่า" AngularJS (ตัวอย่างเช่น. myApp.value('b', {name: 'Jones'});) คุณสามารถฉีดเข้าไปในบริการและโรงงาน แต่ไม่เข้าไปในฟังก์ชันการสร้างผู้ให้บริการ อย่างไรก็ตามคุณสามารถฉีดเข้าไปใน$getฟังก์ชันที่คุณกำหนดไว้สำหรับผู้ให้บริการของคุณ สิ่งนี้ถูกกล่าวถึงในเอกสารของ AngularJS แต่พลาดง่าย คุณสามารถค้นหาได้ในหน้า% ให้ในส่วนที่เกี่ยวกับค่าและวิธีการคงที่

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

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

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

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

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

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

45

นี่เป็นส่วนที่สับสนมากสำหรับมือใหม่และฉันพยายามอธิบายให้ชัดเจนด้วยคำพูดง่ายๆ

AngularJS Service:ใช้สำหรับแชร์ฟังก์ชั่นยูทิลิตี้พร้อมการอ้างอิงบริการในคอนโทรลเลอร์ บริการเป็นแบบซิงเกิลดังนั้นสำหรับหนึ่งบริการจะมีการสร้างอินสแตนซ์เดียวเท่านั้นในเบราว์เซอร์และใช้การอ้างอิงเดียวกันทั่วทั้งหน้า

ในการให้บริการที่เราสร้างชื่อฟังก์ชั่นเป็นทรัพย์สินที่มีนี้วัตถุ

โรงงาน AngularJS:วัตถุประสงค์ของโรงงานก็เหมือนกันกับบริการอย่างไรก็ตามในกรณีนี้เราสร้างวัตถุใหม่และเพิ่มฟังก์ชั่นเป็นคุณสมบัติของวัตถุนี้และในตอนท้ายเราจะส่งคืนวัตถุนี้

AngularJS Provider:จุดประสงค์นี้เหมือนเดิมอีกครั้ง แต่ Provider ให้ผลลัพธ์เป็นฟังก์ชัน $ get

การกำหนดและการใช้บริการโรงงานและผู้ให้บริการมีการอธิบายที่http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider


2
โรงงานและผู้ให้บริการยังเป็นวัตถุเดี่ยวหรือไม่ มี scanrio ไหนบ้างที่แนะนำโรงงานให้บริการ?
Sungarg

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โปรดเรียกใช้ด้วยคอนโซล คอนโซลแสดงว่าบริการนั้นเริ่มต้นเพียงครั้งเดียว
Lukasz Frankowski

อ้อเข้าใจแล้ว. ผมคาดหวังว่าจะสามารถที่จะตามตัวอักษร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วัตถุที่คุณกำหนดและสามารถใช้เพื่อรับวัตถุที่ส่งคืนข้อมูล


คุณไม่ได้ผสมโรงงานและบริการหรือไม่? บริการสร้างที่ส่งคืนโรงงาน
Flavien Volken

เมื่อคุณประกาศชื่อบริการเป็นอาร์กิวเมนต์แบบฉีดคุณจะได้รับอินสแตนซ์ของฟังก์ชัน ในคำอื่น ๆ ใหม่ FunctionYouPassedToService () อินสแตนซ์ของวัตถุนี้กลายเป็นวัตถุบริการที่ AngularJS ลงทะเบียนและฉีดเข้าไปในภายหลังเพื่อบริการ / ตัวควบคุมอื่น ๆ หากจำเป็น // factory เมื่อคุณประกาศชื่อโรงงานเป็นอาร์กิวเมนต์แบบฉีดคุณจะได้รับค่าที่ส่งคืนโดยเรียกใช้การอ้างอิงฟังก์ชันที่ส่งผ่านไปยัง module.factory
sajan

เอาล่ะ ... ในมุมโรงงานเป็นซิงเกิลที่ "บริการ" เป็นจริงโรงงาน (ในแง่รูปแบบการออกแบบร่วมกัน)
Flavien VOLKEN

25

สรุปจากเอกสารเชิงมุม :

  • : มีห้าประเภทสูตรที่กำหนดวิธีการที่จะสร้างวัตถุมีค่า , โรงงาน , บริการ , ผู้ให้บริการและคงที่คงที่
  • โรงงานและบริการเป็นสูตรที่ใช้กันมากที่สุด ข้อแตกต่างระหว่างพวกเขาเพียงอย่างเดียวคือสูตรการให้บริการทำงานได้ดีขึ้นสำหรับวัตถุประเภทที่กำหนดเองในขณะที่โรงงานสามารถผลิตดั้งเดิมและฟังก์ชั่น JavaScript
  • ผู้ให้บริการสูตรเป็นประเภทหลักสูตรและอื่น ๆ ทั้งหมดเป็นเพียงน้ำตาล syntactic มัน
  • ผู้ให้บริการเป็นสูตรอาหารที่ซับซ้อนที่สุด คุณไม่จำเป็นต้องใช้มันนอกจากว่าคุณกำลังสร้างชิ้นส่วนของรหัสที่สามารถใช้ซ้ำได้ซึ่งต้องการการกำหนดค่าระดับโลก

ป้อนคำอธิบายรูปภาพที่นี่


คำตอบที่ดีที่สุดจาก SO:

https://stackoverflow.com/a/26924234/165673 (<- ดีมาก) https://stackoverflow.com/a/27263882/165673
https://stackoverflow.com/a/16566144/165673


20

คำตอบที่ดีทั้งหมดอยู่แล้ว ผมอยากจะเพิ่มจุดอีกไม่กี่บนบริการและโรงงาน พร้อมกับความแตกต่างระหว่างบริการ / โรงงาน และสามารถมีคำถามเช่น:

  1. ฉันควรใช้บริการหรือโรงงาน ความแตกต่างคืออะไร?
  2. พวกเขาทำเหมือนกันหรือมีพฤติกรรมเหมือนกันหรือไม่

เริ่มต้นด้วยความแตกต่างระหว่างการบริการและโรงงาน:

  1. ทั้งสองเป็น Singletons : เมื่อใดก็ตามที่ Angular พบว่าเป็นการพึ่งพาครั้งแรกมันจะสร้างอินสแตนซ์ของการบริการ / โรงงานเพียงครั้งเดียว เมื่อสร้างอินสแตนซ์แล้วอินสแตนซ์เดียวกันจะถูกใช้ตลอดไป

  2. สามารถใช้ในการจำลองวัตถุที่มีพฤติกรรม : พวกเขาทั้งสองสามารถมีวิธีการตัวแปรสถานะภายในและอื่น ๆ แม้ว่าวิธีที่คุณเขียนรหัสนั้นจะแตกต่างกัน

บริการ:

yourServiceName()บริการเป็นฟังก์ชั่นคอนสตรัคและเชิงมุมจะยกตัวอย่างโดยเรียกใหม่ นี่หมายถึงสองสิ่ง

  1. thisฟังก์ชั่นและตัวแปรเช่นจะมีคุณสมบัติของ
  2. คุณไม่จำเป็นต้องส่งคืนค่า เมื่อ Angular call 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ฟังก์ชันผู้เรียกจะได้รับ Object นั้นแทนที่จะเป็นอินสแตนซ์นี้

ซึ่งหมายความว่าคุณสามารถคัดลอกตัวอย่างจากโรงงานโดยทั่วไปจากด้านล่างแทนที่factoryด้วยserviceและมันจะทำงาน:

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

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

ดังนั้นเมื่อ Angular สร้างบริการของคุณด้วย MyService ใหม่ () มันจะได้รับออบเจ็กต์ api นั้นแทน MyService instance

นี่คือพฤติกรรมสำหรับค่าที่ซับซ้อนใด ๆ (วัตถุฟังก์ชั่น) แต่ไม่ได้สำหรับประเภทดั้งเดิม

โรงงาน:

โรงงานคือฟังก์ชั่นเก่าแบบธรรมดาที่คืนค่า ค่าส่งคืนคือสิ่งที่ได้รับการฉีดเข้าไปในสิ่งที่ขึ้นอยู่กับโรงงาน รูปแบบโรงงานทั่วไปใน Angular คือการส่งคืนวัตถุที่มีฟังก์ชั่นเป็นคุณสมบัติเช่นนี้

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

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

  return api;
});

ค่าที่ฉีดสำหรับการพึ่งพาจากโรงงานคือค่าส่งคืนของโรงงานและไม่จำเป็นต้องเป็นวัตถุ มันอาจเป็นฟังก์ชั่น

คำตอบสำหรับคำถาม 1 และ 2 ข้างต้น:

ส่วนใหญ่เพียงใช้การใช้โรงงานเพื่อทุกสิ่ง พฤติกรรมของพวกเขานั้นง่ายต่อการเข้าใจ ไม่มีทางเลือกที่จะทำเกี่ยวกับว่าจะคืนค่าหรือไม่และยิ่งกว่านั้นไม่มีข้อบกพร่องที่จะแนะนำถ้าคุณทำสิ่งผิด

ฉันยังคงอ้างถึงพวกเขาว่า "บริการ" เมื่อฉันพูดถึงการฉีดพวกเขาเป็นการอ้างอิงแม้ว่า

พฤติกรรมบริการ / โรงงานคล้ายกันมากและบางคนจะบอกว่าอย่างใดอย่างหนึ่งก็ดี ค่อนข้างจะเป็นเรื่องจริง แต่ฉันคิดว่ามันง่ายกว่าที่จะทำตามคำแนะนำของไกด์นำเที่ยวของ John Papa และติดกับโรงงาน **


16

ชี้แจงเพิ่มเติมคือโรงงานสามารถสร้างฟังก์ชั่น / ดั้งเดิมในขณะที่บริการไม่สามารถ ตรวจสอบjsFiddleนี้ตาม Epokk's: http://jsfiddle.net/skeller88/PxdSP/1351/ 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 วิธี) - น้ำตาล syntactic มากกว่าทางผู้ให้บริการ / ผู้รับ

มีService vs Factoryบางส่วนได้รับการคุ้มครอง: https://www.youtube.com/watch?v=BLzNCkPn3ao

บริการเป็นเรื่องเกี่ยวกับnewคำสำคัญจริง ๆ ซึ่งเรารู้ว่าทำสิ่งที่ 4:

  1. สร้างวัตถุใหม่
  2. เชื่อมโยงไปยังprototypeวัตถุ
  3. เชื่อมต่อcontextกับthis
  4. และผลตอบแทน this

และFactoryเป็นข้อมูลเกี่ยวกับ Factory Pattern - ประกอบด้วยฟังก์ชั่นที่คืนค่าออบเจกต์เช่น Service

  1. ความสามารถในการใช้บริการอื่น ๆ (มีการอ้างอิง)
  2. การเริ่มต้นบริการ
  3. การเริ่มต้นล่าช้า / สันหลังยาว

และวิดีโอแบบสั้น / สั้นนี้: ครอบคลุมผู้ให้บริการด้วย : https://www.youtube.com/watch?v=HvTZbQ_hUZY (ที่นั่นคุณจะเห็นว่าพวกเขาไปจากโรงงานสู่ผู้ให้บริการได้อย่างไร)

สูตรผู้ให้บริการส่วนใหญ่จะใช้ในการกำหนดค่าแอปก่อนที่แอปจะเริ่มต้น / เริ่มต้นอย่างสมบูรณ์


14

หลังจากอ่านโพสต์ทั้งหมดมันสร้างความสับสนให้ฉันมากขึ้น .. แต่ก็ยังเป็นข้อมูลที่มีค่าทั้งหมด .. ในที่สุดฉันก็พบตารางต่อไปนี้ซึ่งจะให้ข้อมูลกับการเปรียบเทียบแบบง่าย ๆ

  • หัวฉีดใช้สูตรในการสร้างวัตถุสองประเภท: บริการและวัตถุที่มีวัตถุประสงค์พิเศษ
  • มีห้าประเภทสูตรที่กำหนดวิธีการสร้างวัตถุ: ค่าโรงงานบริการผู้ให้บริการและค่าคงที่
  • โรงงานและบริการเป็นสูตรที่ใช้กันมากที่สุด ข้อแตกต่างระหว่างพวกเขาเพียงอย่างเดียวคือสูตรการให้บริการทำงานได้ดีขึ้นสำหรับวัตถุประเภทที่กำหนดเองในขณะที่โรงงานสามารถผลิตดั้งเดิมและฟังก์ชั่น JavaScript
  • ผู้ให้บริการสูตรเป็นประเภทหลักสูตรและอื่น ๆ ทั้งหมดเป็นเพียงน้ำตาล syntactic มัน
  • ผู้ให้บริการเป็นสูตรอาหารที่ซับซ้อนที่สุด คุณไม่จำเป็นต้องใช้มันนอกจากว่าคุณกำลังสร้างชิ้นส่วนของรหัสที่สามารถใช้ซ้ำได้ซึ่งต้องการการกำหนดค่าระดับโลก
  • วัตถุวัตถุประสงค์พิเศษทั้งหมดยกเว้นตัวควบคุมถูกกำหนดผ่านสูตรโรงงาน

ป้อนคำอธิบายรูปภาพที่นี่

และสำหรับผู้เริ่มต้นเข้าใจ: -นี่อาจไม่ถูกต้องกรณีใช้ แต่ในระดับสูงนี่คือสิ่งที่เราใช้สำหรับทั้งสาม

  1. หากคุณต้องการใช้ในฟังก์ชั่นการกำหนดค่าโมดูลเชิงมุมควรสร้างเป็นผู้ให้

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

  1. โทรอาแจ็กซ์หรือการผสานรวมความต้องการของบุคคลที่สามที่จะให้บริการ
  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()มัน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);
    }]);
};

ดังนั้นโดยพื้นฐานแล้วในทั้งสองกรณีคุณได้รับผู้ให้บริการ $ ได้รับการตั้งค่าให้กับฟังก์ชั่นที่คุณให้ไว้ แต่คุณสามารถให้อะไรมากกว่า $ ได้ตามที่คุณสามารถให้ในผู้ให้บริการ () สำหรับ config block


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 Custom Services โดยใช้วิธีการจากโรงงานบริการและผู้ให้บริการ

ฉันพบวิดีโอนี้ซึ่งอธิบายเกี่ยวกับวิธีการบริการและผู้ให้บริการอย่างชัดเจนสำหรับการพัฒนา AngularJS Custom Services:

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

รหัสที่มา: http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service

รหัสที่โพสต์ที่นี่จะถูกคัดลอกมาจากแหล่งข้อมูลข้างต้นโดยตรงเพื่อประโยชน์ของผู้อ่าน

รหัสสำหรับบริการที่กำหนดเองที่ใช้ "โรงงาน" มีดังต่อไปนี้ (ซึ่งไปพร้อมกับเวอร์ชันการซิงค์และ async พร้อมกับการเรียกใช้บริการ 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;
  }
]);

รหัสสำหรับวิธีการ "บริการ" สำหรับ Custom Services (ค่อนข้างคล้ายกับ 'โรงงาน' แต่แตกต่างจากมุมมองของไวยากรณ์):

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

}]);

รหัสสำหรับวิธีการ "ผู้ให้บริการ" สำหรับ Custom Services (จำเป็นถ้าคุณต้องการพัฒนาบริการที่สามารถกำหนดค่าได้):

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

ในที่สุด UI ที่ทำงานกับบริการใด ๆ ข้างต้น:

<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 ของ Yaakov )

บริการ :

ไวยากรณ์:

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. Lazilyอินสแตนซ์: ถ้ามันไม่ถูกฉีดมันจะไม่ถูกอินสแตนซ์เคย ดังนั้นในการใช้มันจะต้องทำการฉีดเข้ากับโมดูล
  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. ผู้ให้บริการเป็นวิธีที่ยืดหยุ่นที่สุดในการสร้างบริการในเชิงมุม
  2. ไม่เพียง แต่เราสามารถสร้างโรงงานที่สามารถกำหนดค่าได้แบบไดนามิก แต่ในเวลาที่ใช้โรงงานด้วยวิธีการของผู้ให้บริการเราสามารถกำหนดค่าโรงงานได้เองเพียงครั้งเดียวที่การบูตแอปพลิเคชันทั้งหมดของเรา
  3. จากนั้นโรงงานสามารถใช้งานได้ตลอดทั้งแอปพลิเคชันด้วยการตั้งค่าแบบกำหนดเอง กล่าวอีกนัยหนึ่งเราสามารถกำหนดค่าโรงงานนี้ก่อนเริ่มแอปพลิเคชัน ในความเป็นจริงในเอกสารเชิงมุมมันบอกว่าวิธีการให้บริการเป็นสิ่งที่จริงได้รับการดำเนินการอยู่เบื้องหลังเมื่อเรากำหนดค่าบริการของเรามีอย่างใดอย่างหนึ่ง.serviceหรือ.factoryวิธีการ
  4. $getเป็นฟังก์ชั่นที่ติดโดยตรงกับผู้ให้บริการเช่นที่ ฟังก์ชั่นที่เป็นโรงงานฟังก์ชั่น ในคำอื่น ๆ ก็เช่นเดียวกับคนที่เราใช้ในการให้ไปที่.factoryวิธีการ ในฟังก์ชันนั้นเราสร้างบริการของเราเอง นี้$getคุณสมบัติที่ฟังก์ชั่นเป็นสิ่งที่ทำให้ผู้ให้บริการผู้ให้บริการ AngularJS คาดว่าผู้ให้บริการจะมีคุณสมบัติ $ get ซึ่งมีค่าเป็นฟังก์ชั่นที่ Angular จะถือว่าเป็นฟังก์ชันโรงงาน แต่สิ่งที่ทำให้การตั้งค่าผู้ให้บริการทั้งหมดนี้พิเศษมากคือความจริงที่ว่าเราสามารถให้configวัตถุบางอย่างภายในผู้ให้บริการและโดยปกติจะมาพร้อมกับค่าเริ่มต้นที่เราสามารถเขียนทับในขั้นตอนต่อมาซึ่งเราสามารถกำหนดค่าแอปพลิเคชันทั้งหมด

7

โรงงาน:โรงงานที่คุณสร้างวัตถุภายในโรงงานจริง ๆ แล้วส่งคืน
บริการ:บริการที่คุณเพิ่งมีฟังก์ชั่นมาตรฐานที่ใช้คำหลักนี้เพื่อกำหนดฟังก์ชั่น
ผู้ให้บริการ:ผู้ให้บริการที่มี $ ให้คุณกำหนดและมันสามารถใช้ในการรับวัตถุที่ส่งกลับข้อมูล


7

โดยพื้นฐานแล้วผู้ให้บริการโรงงานและบริการคือบริการทั้งหมด Factory เป็นกรณีพิเศษของบริการเมื่อทุกสิ่งที่คุณต้องการคือฟังก์ชั่น $ get () ช่วยให้คุณเขียนด้วยรหัสน้อยลง

ความแตกต่างที่สำคัญระหว่างบริการโรงงานและผู้ให้บริการคือความซับซ้อน บริการเป็นรูปแบบที่ง่ายที่สุดโรงงานมีความแข็งแกร่งขึ้นเล็กน้อยและผู้ให้บริการสามารถกำหนดค่าได้ในขณะทำงาน

นี่เป็นบทสรุปของการใช้เมื่อใด:

Factory : ค่าที่คุณให้ต้องถูกคำนวณตามข้อมูลอื่น

บริการ : คุณกำลังส่งคืนวัตถุด้วยวิธีการ

ผู้ให้บริการ : คุณต้องการที่จะสามารถกำหนดค่าในระหว่างขั้นตอนการกำหนดค่าวัตถุที่จะถูกสร้างขึ้นก่อนที่จะสร้างขึ้น ใช้ผู้ให้บริการส่วนใหญ่ในการกำหนดค่าแอปก่อนที่แอปจะเริ่มต้นได้อย่างสมบูรณ์


ERM ราคาโรงงานบริการและค่าคงที่ - เป็นเพียงน้ำตาลซินแทคติคที่อยู่ด้านบนของสูตรผู้ให้บริการ Angularjs docs - ผู้ให้บริการ
Sudarshan_SMD

ใช่ฉันเห็นด้วยตอนนี้กับ angular 4 เราไม่มีอาการปวดหัวนี้อีกแล้ว
eGhoul

4

1. บริการเป็นวัตถุเดี่ยวที่สร้างขึ้นเมื่อจำเป็นและไม่เคยทำความสะอาดจนกว่าจะสิ้นสุดวงจรชีวิตของแอปพลิเคชัน (เมื่อปิดเบราว์เซอร์) ตัวควบคุมจะถูกทำลายและทำความสะอาดเมื่อไม่ต้องการอีกต่อไป

2. วิธีที่ง่ายที่สุดในการสร้างบริการคือใช้วิธีการจากโรงงาน () วิธีการจากโรงงาน () ช่วยให้เราสามารถกำหนดบริการโดยส่งคืนวัตถุที่มีฟังก์ชั่นบริการและข้อมูลการบริการ ฟังก์ชั่นการให้คำจำกัดความบริการเป็นที่ที่เราให้บริการแบบฉีดเช่น $ http และ $ q Ex:

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. บริการ () วิธีในทางตรงกันข้ามช่วยให้เราสามารถสร้างบริการโดยการกำหนดฟังก์ชั่นการสร้าง เราสามารถใช้วัตถุต้นแบบเพื่อกำหนดบริการของเราแทนที่จะเป็นวัตถุจาวาสคริปต์แบบดิบ เช่นเดียวกับวิธี factory () เราจะตั้งค่า injectables ในนิยามฟังก์ชั่น
  2. วิธีระดับต่ำสุดในการสร้างบริการคือการใช้วิธีการให้ () นี่เป็นวิธีเดียวในการสร้างบริการที่เราสามารถกำหนดค่าโดยใช้ฟังก์ชัน. config () ซึ่งแตกต่างจากวิธีก่อนหน้านี้เราจะตั้งค่า injectables ในที่กำหนดไว้นี้ $ get () นิยามฟังก์ชั่น

-3

น้ำตาลซินแทคติคคือความแตกต่างวากยสัมพันธ์น้ำตาลคือความแตกต่างผู้ให้บริการเท่านั้นที่จำเป็น หรือในคำอื่น ๆ ผู้ให้บริการเท่านั้นเป็นเชิงมุมจริงคนอื่น ๆ ทั้งหมดจะได้รับ (เพื่อลดรหัส) มีเวอร์ชันง่าย ๆ เช่นกันเรียกว่า Value () ซึ่งคืนค่าเพียงไม่มีการคำนวณหรือฟังก์ชัน แม้ค่ามาจากผู้ให้บริการ!

เหตุใดจึงเกิดภาวะแทรกซ้อนเช่นนี้ทำไมเราไม่สามารถใช้ผู้ให้บริการและลืมทุกอย่างได้ มันควรจะช่วยให้เราเขียนโค้ดได้ง่ายและสื่อสารได้ดีขึ้น และการตอบกลับด้วยลิ้นจะยิ่งซับซ้อนมากขึ้นเท่าไหร่การขายเฟรมเวิร์กก็จะยิ่งดีขึ้นเท่านั้น


  • ผู้ให้บริการที่สามารถส่งคืน value = Value
  • ผู้ให้บริการที่สามารถยกตัวอย่างและส่งคืน = Factory (+ Value)
  • ผู้ให้บริการที่สามารถยกตัวอย่าง + ทำบางสิ่ง = บริการ (+ โรงงาน, + มูลค่า)
  • ผู้ให้บริการ = ต้องมีสถานที่ให้บริการที่เรียกว่า $ get (+ Factory, + Service, + Value)

การฉีดเชิงมุมให้คำแนะนำแรกแก่เราในการบรรลุข้อสรุปนี้

"$ injector ใช้เพื่อดึงข้อมูลอินสแตนซ์ของวัตถุตามที่กำหนดโดย ผู้ให้บริการ " ไม่ใช่บริการไม่ใช่จากโรงงาน แต่เป็นผู้ให้บริการ

และคำตอบที่ดีกว่านี้ก็คือ: "บริการ Angular ถูกสร้างขึ้นโดยโรงงานบริการโรงงานบริการเหล่านี้เป็นฟังก์ชั่นซึ่งในทางกลับกันถูกสร้างขึ้นโดยผู้ให้บริการผู้ให้บริการเป็นฟังก์ชั่นคอนสตรัคเตอร์ เรียกว่า $ get ซึ่งเก็บฟังก์ชั่นบริการโรงงาน "

ดังนั้นผู้ให้บริการหลักและหัวฉีดและทั้งหมดจะอยู่ในสถานที่ :) และมันน่าสนใจใน typescript เมื่อ $ get สามารถนำไปใช้ในผู้ให้บริการโดยรับมรดกจาก IServiceProvider

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.