"Benda" apa yang bisa disuntikkan ke orang lain di Angular.js?


142

Saya mengalami sedikit kesulitan memahami Dependency Injection di Angular. Jadi pertanyaan saya adalah, adakah yang bisa menjelaskan "jenis" mana, seperti Pengendali, Pabrik, Penyedia, dll yang dapat kita suntikkan ke orang lain, termasuk contoh lain dari "jenis" yang sama?

Apa yang sebenarnya saya cari adalah tabel ini diisi dengan y / n. Untuk sel dengan baris / kolom yang sama, itu berarti menyuntikkan nilai dari satu "jenis" ke yang lain dengan "jenis" yang sama

+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Can we inject? | Constant | Controller | Directive | Factory | Filter | Provider | Service | Value |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Constant       |          |            |           |         |        |          |         |       |
| Controller     |          |            |           |         |        |          |         |       |
| Directive      |          |            |           |         |        |          |         |       |
| Factory        |          |            |           |         |        |          |         |       |
| Filter         |          |            |           |         |        |          |         |       |
| Provider       |          |            |           |         |        |          |         |       |
| Service        |          |            |           |         |        |          |         |       |
| Value          |          |            |           |         |        |          |         |       |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+

Jawaban:


391

Alih-alih itu hanya mengisi tabel dengan "ya" dan "tidak" tanpa penjelasan, saya akan membahas sedikit lebih detail.

[Catatan, ditambahkan setelah selesai: ini akhirnya ... sedikit lebih lama dari yang saya harapkan. Ada tl; dr di bagian bawah, tapi saya harap ini membuktikan informasi.]

[Jawaban ini juga telah ditambahkan ke wiki AngularJS: Understanding Dependency Injection ]


Penyedia ( $provide)

The $providelayanan bertanggung jawab untuk memberitahu sudut cara membuat hal-hal yang disuntikkan baru; hal-hal ini disebut layanan . Layanan ditentukan oleh hal-hal yang disebut penyedia , yang merupakan apa yang Anda buat saat Anda gunakan $provide. Menentukan penyedia dilakukan melalui providermetode pada $providelayanan, dan Anda dapat memperoleh $providelayanan dengan memintanya untuk disuntikkan ke fungsi aplikasi config. Contohnya mungkin seperti ini:

app.config(function($provide) {
  $provide.provider('greeting', function() {
    this.$get = function() {
      return function(name) {
        alert("Hello, " + name);
      };
    };
  });
});

Di sini kami telah menetapkan penyedia baru untuk layanan yang disebut greeting; kita bisa menyuntikkan variabel yang dinamai greetingke fungsi injeksi apa saja (seperti pengendali, lebih lanjut tentang itu nanti) dan Angular akan memanggil fungsi penyedia $getuntuk mengembalikan contoh layanan yang baru. Dalam hal ini, hal yang akan disuntikkan adalah fungsi yang mengambil nameparameter dan menyimpan alertpesan berdasarkan namanya. Kami mungkin menggunakannya seperti ini:

app.controller('MainController', function($scope, greeting) {
  $scope.onClick = function() {
    greeting('Ford Prefect');
  };
});

Nah, inilah triknya. factory,, servicedan valuesemuanya hanya jalan pintas untuk menentukan berbagai bagian penyedia - yaitu, mereka menyediakan sarana untuk menentukan penyedia tanpa harus mengetikkan semua hal itu. Misalnya, Anda dapat menulis penyedia yang sama persis seperti ini:

app.config(function($provide) {
  $provide.factory('greeting', function() {
    return function(name) {
      alert("Hello, " + name);
    };
  });
});

Ini penting untuk dipahami, jadi saya akan ulangi: di bawah tenda, AngularJS memanggil kode yang sama persis yang kita tulis di atas ( $provide.providerversi) untuk kita. Secara harfiah, 100% tidak ada perbedaan dalam dua versi. valuebekerja dengan cara yang sama - jika apa pun yang kita akan kembali dari $getfungsi kita (alias factoryfungsi kita ) selalu persis sama, kita dapat menulis lebih sedikit menggunakan kode value. Misalnya, karena kami selalu mengembalikan fungsi yang sama untuk greetinglayanan kami , kami juga dapat menggunakannya valueuntuk mendefinisikannya:

app.config(function($provide) {
  $provide.value('greeting', function(name) {
    alert("Hello, " + name);
  });
});

Sekali lagi, ini 100% identik dengan dua metode lain yang kami gunakan untuk mendefinisikan fungsi ini - ini hanya cara untuk menghemat pengetikan.

Sekarang Anda mungkin memperhatikan hal menjengkelkan app.config(function($provide) { ... })yang telah saya gunakan. Karena mendefinisikan penyedia baru (melalui salah satu metode yang diberikan di atas) sangat umum, AngularJS memaparkan $providermetode langsung pada objek modul, untuk menyimpan lebih banyak mengetik:

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

myMod.provider("greeting", ...);
myMod.factory("greeting", ...);
myMod.value("greeting", ...);

Ini semua melakukan hal yang sama dengan versi yang lebih verbose yang app.config(...)kami gunakan sebelumnya.

Injeksi yang saya lewati sejauh ini adalah constant. Untuk saat ini, cukup mudah untuk mengatakan bahwa itu berfungsi seperti itu value. Kita akan melihat ada satu perbedaan nanti.

Untuk meninjau , semua bagian kode ini melakukan hal yang persis sama:

myMod.provider('greeting', function() {
  this.$get = function() {
    return function(name) {
      alert("Hello, " + name);
    };
  };
});

myMod.factory('greeting', function() {
  return function(name) {
    alert("Hello, " + name);
  };
});

myMod.value('greeting', function(name) {
  alert("Hello, " + name);
});

Injector ( $injector)

Injektor bertanggung jawab untuk benar-benar membuat contoh layanan kami menggunakan kode yang kami berikan melalui $provide(tidak ada permainan kata pun). Setiap kali Anda menulis fungsi yang mengambil argumen yang disuntikkan, Anda melihat injektor sedang bekerja. Setiap aplikasi AngularJS memiliki satu $injectoryang akan dibuat ketika aplikasi pertama kali dimulai; Anda bisa memperolehnya dengan menyuntikkan $injectorke fungsi injeksi apa pun (ya, $injectortahu cara menyuntikkan sendiri!)

Setelah memilikinya $injector, Anda bisa mendapatkan instance dari layanan yang ditentukan dengan memanggilnya getdengan nama layanan. Sebagai contoh,

var greeting = $injector.get('greeting');
greeting('Ford Prefect');

Injektor juga bertanggung jawab untuk menyuntikkan layanan ke dalam fungsi; misalnya, Anda dapat secara ajaib menyuntikkan layanan ke fungsi apa pun yang Anda miliki menggunakan metode injektor invoke;

var myFunction = function(greeting) {
  greeting('Ford Prefect');
};
$injector.invoke(myFunction);

Perlu dicatat bahwa injektor hanya akan membuat instance layanan satu kali . Kemudian cache apa pun penyedia kembali dengan nama layanan; kali berikutnya Anda meminta layanan, Anda akan benar-benar mendapatkan objek yang sama persis.

Jadi, untuk menjawab pertanyaan Anda, Anda bisa menyuntikkan layanan ke fungsi apa pun yang dipanggil$injector.invoke . Ini termasuk

  • fungsi definisi pengontrol
  • fungsi definisi direktif
  • fungsi definisi filter
  • yang $getmetode penyedia (alias factoryfungsi definisi)

Karena constants dan values selalu mengembalikan nilai statis, mereka tidak dipanggil melalui injektor, dan dengan demikian Anda tidak dapat menyuntikkan mereka dengan apa pun.

Mengkonfigurasi Penyedia

Anda mungkin bertanya-tanya mengapa ada orang yang repot-repot untuk mendirikan sebuah penyedia penuh dengan providemetode jika factory, value, dll jauh lebih mudah. Jawabannya adalah bahwa penyedia mengizinkan banyak konfigurasi. Kami telah menyebutkan bahwa ketika Anda membuat layanan melalui penyedia (atau salah satu pintasan yang diberikan Angular), Anda membuat penyedia baru yang menentukan bagaimana layanan itu dibangun. Yang tidak saya sebutkan adalah bahwa penyedia ini dapat disuntikkan ke configbagian aplikasi Anda sehingga Anda dapat berinteraksi dengan mereka!

Pertama, Angular menjalankan aplikasi Anda dalam dua fase - fase configdan run. The configfase, seperti yang kita lihat, adalah di mana Anda dapat mengatur penyedia apapun yang diperlukan. Ini juga tempat arahan, pengontrol, filter, dan sejenisnya diatur. The runfase, seperti yang Anda duga, adalah di mana sudut sebenarnya mengkompilasi DOM dan dijalankan aplikasi Anda.

Anda dapat menambahkan kode tambahan untuk dijalankan dalam fase ini dengan myMod.configdan myMod.runfungsi - masing-masing mengambil fungsi untuk dijalankan selama fase tertentu. Seperti yang kita lihat di bagian pertama, fungsi-fungsi ini dapat disuntikkan - kami menyuntikkan layanan bawaan dalam $providesampel kode pertama kami. Namun, yang perlu dicatat adalah bahwa selama configfase ini, hanya penyedia yang dapat disuntikkan (dengan pengecualian layanan dalam AUTOmodul - $providedan $injector).

Misalnya, yang berikut ini tidak diizinkan :

myMod.config(function(greeting) {
  // WON'T WORK -- greeting is an *instance* of a service.
  // Only providers for services can be injected in config blocks.
});

Apa yang Anda lakukan memiliki akses ke apapun penyedia layanan yang telah Anda buat:

myMod.config(function(greetingProvider) {
  // a-ok!
});

Ada satu pengecualian penting: constants, karena mereka tidak dapat diubah, diizinkan untuk disuntikkan di dalam configblok (ini adalah bagaimana mereka berbeda dari values). Mereka diakses dengan nama mereka sendiri (tidak Providerperlu suffix).

Setiap kali Anda menentukan penyedia untuk layanan, penyedia itu dinamai serviceProvider, di mana serviceadalah nama layanan. Sekarang kita dapat menggunakan kekuatan penyedia melakukan beberapa hal yang lebih rumit!

myMod.provider('greeting', function() {
  var text = 'Hello, ';

  this.setText = function(value) {
    text = value;
  };

  this.$get = function() {
    return function(name) {
      alert(text + name);
    };
  };
});

myMod.config(function(greetingProvider) {
  greetingProvider.setText("Howdy there, ");
});

myMod.run(function(greeting) {
  greeting('Ford Prefect');
});

Sekarang kami memiliki fungsi pada penyedia kami yang disebut setTextdapat kami gunakan untuk menyesuaikan alert; kita bisa mendapatkan akses ke penyedia ini dalam satu configblok untuk memanggil metode ini dan menyesuaikan layanan. Ketika kami akhirnya menjalankan aplikasi kami, kami dapat mengambil greetinglayanan, dan mencobanya untuk melihat bahwa kustomisasi kami mulai berlaku.

Karena ini adalah contoh yang lebih kompleks, inilah demonstrasi yang berfungsi: http://jsfiddle.net/BinaryMuse/9GjYg/

Pengendali ( $controller)

Fungsi-fungsi pengontrol dapat disuntikkan ke dalam, tetapi pengontrol itu sendiri tidak dapat disuntikkan ke hal-hal lain. Itu karena pengendali tidak dibuat melalui penyedia. Sebaliknya, ada layanan Angular built-in yang disebut $controlleryang bertanggung jawab untuk mengatur pengendali Anda. Saat Anda menelepon myMod.controller(...), Anda sebenarnya sedang mengakses penyedia layanan ini , seperti di bagian terakhir.

Misalnya, saat Anda mendefinisikan pengontrol seperti ini:

myMod.controller('MainController', function($scope) {
  // ...
});

Apa yang sebenarnya Anda lakukan adalah ini:

myMod.config(function($controllerProvider) {
  $controllerProvider.register('MainController', function($scope) {
    // ...
  });
});

Kemudian, ketika Angular perlu membuat turunan dari pengontrol Anda, ia menggunakan $controllerlayanan (yang pada gilirannya menggunakan $injectoruntuk memanggil fungsi pengontrol Anda sehingga ketergantungannya juga disuntikkan).

Filter dan Arahan

filterdan directivebekerja dengan cara yang persis sama seperti controller; filtermenggunakan layanan yang disebut $filterdan penyedianya $filterProvider, sementara directivemenggunakan layanan yang disebut $compiledan penyedianya $compileProvider. Beberapa tautan:

Seperti contoh lainnya, myMod.filterdan myMod.directivemerupakan pintasan untuk mengonfigurasi layanan ini.


tl; dr

Jadi, untuk meringkas, fungsi apa pun yang dipanggil $injector.invoke dapat disuntikkan . Ini termasuk, dari bagan Anda (tetapi tidak terbatas pada):

  • pengontrol
  • pengarahan
  • pabrik
  • Saring
  • provider $get(saat mendefinisikan provider sebagai objek)
  • fungsi penyedia (ketika mendefinisikan penyedia sebagai fungsi konstruktor)
  • layanan

Penyedia menciptakan layanan baru yang dapat disuntikkan ke dalam berbagai hal . Ini termasuk:

  • konstan
  • pabrik
  • pemberi
  • layanan
  • nilai

Yang mengatakan, layanan built-in seperti $controllerdan $filter dapat disuntikkan, dan Anda dapat menggunakan layanan tersebut untuk mendapatkan filter dan pengendali baru yang Anda tetapkan dengan metode-metode tersebut (meskipun hal-hal yang Anda tentukan sendiri tidak dapat disuntikkan ke dalam berbagai hal).

Selain itu, fungsi apa pun yang disuntikkan injektor dapat disuntikkan dengan layanan yang disediakan penyedia apa pun - tidak ada batasan (selain dari configdan runperbedaan yang tercantum di sini).


6
Wow! terima kasih telah meluangkan waktu untuk menjawab secara mendetail! Saya sudah membaca ini dua kali, dan saya pikir saya sudah mengerti sedikit. Akan mempelajarinya dan tautan yang Anda berikan secara rinci hari ini. Dan +1 lainnya untuk kucing. :)
user1527166

18
Salah satu jawaban SO yang paling berguna dan terperinci yang pernah saya temui - terima kasih!
Godders

11
Jawaban ini mendefinisikan tingkat baru yang mengagumkan. Hal-hal yang menerangi.
Ngure Nyaga

4
Sejauh ini sumber daya terbaik yang saya temui untuk AngularJS. Terima kasih.
code90

5
Secara harfiah, bagian terbaik dari dokumentasi AngularJS yang pernah saya lihat. Jalan untuk pergi!
Iain Duncan

13

Poin yang dibuat BinaryMuse dalam jawabannya yang menakjubkan tentang penyedia, pabrik, dan layanan semuanya menjadi hal yang sama sangat penting.

Di bawah ini adalah gambar yang menurut saya dapat menggambarkan maksudnya secara visual:

AngularJS mereka semua hanyalah penyedia
(sumber: simplygoodcode.com )


7

Jawaban yang bagus dari Michelle. Saya hanya ingin menunjukkan bahwa arahan dapat disuntikkan. Jika Anda memiliki arahan bernama myThing, Anda dapat menyuntikkannya dengan myThingDirective: Ini adalah contoh yang dibuat-buat .

Contoh di atas tidak terlalu praktis, namun kemampuan untuk menyuntikkan arahan berguna ketika Anda ingin menghias arahan itu .


Tampaknya contoh kedua untuk menghias arahan itu tidak berfungsi sejak Angular 1.4. (lihat komentar Juan Biscaia di sana)
Vadorequest
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.