Những thứ mà người Viking có thể được tiêm vào những người khác trong Angular.js?


142

Tôi có một chút khó khăn để hiểu Dependency Injection in Angular. Vì vậy, câu hỏi của tôi là, bất cứ ai cũng có thể giải thích loại "loại" nào, như Bộ điều khiển, Nhà máy, Nhà cung cấp, v.v. chúng ta có thể tiêm vào người khác, bao gồm các trường hợp khác có cùng "loại" không?

Những gì tôi thực sự tìm kiếm là bảng này chứa đầy y / n. Đối với các ô có cùng hàng / cột, điều đó có nghĩa là chèn giá trị của một "loại" vào một ô khác có cùng "loại"

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

Câu trả lời:


391

Thay vào đó chỉ cần điền vào bảng với "có" và "không" mà không có lời giải thích, tôi sẽ đi vào chi tiết hơn một chút.

[Lưu ý, được thêm vào sau khi hoàn thành: điều này cuối cùng đã ... khá lâu hơn tôi mong đợi. Có một tl; dr ở phía dưới, nhưng tôi hy vọng điều này chứng minh thông tin.]

[Câu trả lời này cũng đã được thêm vào wiki AngularJS: Hiểu về tiêm phụ thuộc ]


Nhà cung cấp ( $provide)

Các $providedịch vụ có trách nhiệm nói với góc làm thế nào để tạo ra những điều tiêm mới; những thứ này được gọi là dịch vụ . Dịch vụ được xác định bởi những thứ được gọi là nhà cung cấp , đó là những gì bạn đang tạo khi bạn sử dụng $provide. Việc xác định nhà cung cấp được thực hiện thông qua providerphương thức trên $providedịch vụ và bạn có thể nắm giữ $providedịch vụ bằng cách yêu cầu dịch vụ đó được đưa vào configchức năng của ứng dụng . Một ví dụ có thể là một cái gì đó như thế này:

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

Ở đây chúng tôi đã xác định một nhà cung cấp mới cho một dịch vụ được gọi là greeting; chúng ta có thể đưa một biến có tên greetingvào bất kỳ chức năng có thể tiêm nào (như bộ điều khiển, nhiều hơn về sau) và Angular sẽ gọi $gethàm của nhà cung cấp để trả về một phiên bản mới của dịch vụ. Trong trường hợp này, thứ sẽ được thêm vào là một hàm lấy nametham số và alertsa thông báo dựa trên tên. Chúng tôi có thể sử dụng nó như thế này:

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

Bây giờ đây là mẹo. factory, servicevaluetất cả chỉ là các phím tắt để xác định các phần khác nhau của nhà cung cấp - nghĩa là, họ cung cấp một phương tiện để xác định nhà cung cấp mà không phải nhập tất cả nội dung đó. Ví dụ: bạn có thể viết chính xác cùng một nhà cung cấp như thế này:

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

Điều quan trọng là phải hiểu, vì vậy tôi sẽ viết lại: dưới mui xe, AngularJS đang gọi chính xác mã mà chúng tôi đã viết ở trên ( $provide.providerphiên bản) cho chúng tôi. Có nghĩa đen, 100% không có sự khác biệt trong hai phiên bản. valuehoạt động theo cùng một cách - nếu bất cứ điều gì chúng ta sẽ trả về từ $gethàm của chúng ta (còn gọi là factoryhàm của chúng ta ) luôn giống hệt nhau, chúng ta có thể viết mã thậm chí ít hơn bằng cách sử dụng value. Ví dụ: vì chúng tôi luôn trả lại cùng chức năng cho greetingdịch vụ của mình, chúng tôi cũng có thể sử dụng valueđể xác định chức năng đó:

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

Một lần nữa, điều này giống hệt 100% với hai phương thức khác mà chúng ta đã sử dụng để xác định hàm này - đó chỉ là một cách để lưu một số cách gõ.

Bây giờ bạn có thể nhận thấy điều phiền toái app.config(function($provide) { ... })này tôi đã sử dụng. Do việc xác định các nhà cung cấp mới (thông qua bất kỳ phương thức đã cho nào ở trên) rất phổ biến, AngularJS trưng ra các $providerphương thức trực tiếp trên đối tượng mô-đun, để tiết kiệm hơn nữa việc gõ:

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

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

Tất cả đều làm điều tương tự như các app.config(...)phiên bản dài hơn mà chúng ta đã sử dụng trước đây.

Thuốc tiêm mà tôi đã bỏ qua cho đến nay là constant. Bây giờ, thật dễ dàng để nói rằng nó hoạt động như thế nào value. Chúng ta sẽ thấy có một sự khác biệt sau.

Để xem xét , tất cả các đoạn mã này đang thực hiện cùng một điều chính xác :

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

Vòi phun ( $injector)

Người tiêm có trách nhiệm thực sự tạo ra các phiên bản dịch vụ của chúng tôi bằng cách sử dụng mã chúng tôi cung cấp thông qua $provide(không có ý định chơi chữ). Bất cứ khi nào bạn viết một hàm lấy các đối số được chèn, bạn sẽ thấy trình tiêm tại nơi làm việc. Mỗi ứng dụng AngularJS có một ứng dụng $injectorđược tạo khi ứng dụng bắt đầu; bạn có thể nắm giữ nó bằng cách tiêm $injectorvào bất kỳ chức năng tiêm nào (vâng, $injectorbiết cách tự tiêm!)

Khi bạn đã có $injector, bạn có thể lấy một phiên bản của một dịch vụ được xác định bằng cách gọi getnó với tên của dịch vụ. Ví dụ,

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

Người tiêm cũng có trách nhiệm tiêm dịch vụ vào các chức năng; ví dụ, bạn có thể tiêm dịch vụ một cách kỳ diệu vào bất kỳ chức năng nào bạn có bằng invokephương pháp của người tiêm ;

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

Cần lưu ý rằng người tiêm sẽ chỉ tạo một thể hiện của dịch vụ một lần . Sau đó, nó lưu trữ bất cứ thứ gì nhà cung cấp trả về bằng tên của dịch vụ; lần sau khi bạn yêu cầu dịch vụ, bạn sẽ thực sự có được cùng một đối tượng.

Vì vậy, để trả lời câu hỏi của bạn, bạn có thể đưa dịch vụ vào bất kỳ chức năng nào được gọi với$injector.invoke . Điêu nay bao gôm

  • chức năng định nghĩa bộ điều khiển
  • chức năng định nghĩa chỉ thị
  • chức năng định nghĩa bộ lọc
  • các $getphương thức của nhà cung cấp (còn gọi là các factoryhàm định nghĩa)

constants và values luôn trả về một giá trị tĩnh, chúng không được gọi thông qua bộ tiêm, và do đó bạn không thể tiêm chúng với bất cứ thứ gì.

Cấu hình nhà cung cấp

Bạn có thể tự hỏi tại sao người ta bận tâm để thiết lập một nhà cung cấp chính thức với các providephương pháp nếu factory, valuevv rất dễ dàng hơn nhiều. Câu trả lời là các nhà cung cấp cho phép rất nhiều cấu hình. Chúng tôi đã đề cập rằng khi bạn tạo một dịch vụ thông qua nhà cung cấp (hoặc bất kỳ phím tắt nào mà Angular cung cấp cho bạn), bạn sẽ tạo một nhà cung cấp mới xác định cách thức dịch vụ đó được xây dựng. Điều tôi không đề cập là các nhà cung cấp này có thể được đưa vào configcác phần trong ứng dụng của bạn để bạn có thể tương tác với họ!

Đầu tiên, Angular chạy ứng dụng của bạn theo hai giai đoạn - giai đoạn configrungiai đoạn. Các configgiai đoạn, như chúng ta đã thấy, là nơi bạn có thể thiết lập bất kỳ nhà cung cấp khi cần thiết. Đây cũng là nơi chỉ thị, bộ điều khiển, bộ lọc và những thứ tương tự được thiết lập. Các rungiai đoạn, như bạn có thể đoán, là nơi góc thực sự biên dịch DOM của bạn và khởi động ứng dụng của bạn.

Bạn có thể thêm mã bổ sung để được chạy trong các giai đoạn này với các hàm myMod.configmyMod.runhàm - mỗi hàm có một chức năng để chạy trong giai đoạn cụ thể đó. Như chúng ta đã thấy trong phần đầu tiên, các hàm này có thể được tiêm - chúng ta đã thêm $providedịch vụ tích hợp trong mẫu mã đầu tiên của mình. Tuy nhiên, điều đáng chú ý là trong configgiai đoạn này, chỉ các nhà cung cấp mới có thể được tiêm (ngoại trừ các dịch vụ trong mô- AUTOđun-- $provide$injector).

Ví dụ: những điều sau đây không được phép :

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

Những gì bạn làm có quyền truy cập vào bất kỳ nhà cung cấp cho dịch vụ mà bạn đã thực hiện:

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

Có một ngoại lệ quan trọng: constants, vì chúng không thể thay đổi, được phép tiêm vào bên trong configcác khối (đây là cách chúng khác với values). Họ được truy cập bằng tên của họ một mình (không có Providerhậu tố cần thiết).

Bất cứ khi nào bạn xác định nhà cung cấp dịch vụ, nhà cung cấp đó sẽ được đặt tên serviceProvider, servicetên của dịch vụ đó ở đâu. Bây giờ chúng ta có thể sử dụng sức mạnh của các nhà cung cấp làm một số thứ phức tạp hơn!

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

Bây giờ chúng tôi có một chức năng trên nhà cung cấp của chúng tôi được gọi là setTextchúng tôi có thể sử dụng để tùy chỉnh alert; chúng tôi có thể truy cập vào nhà cung cấp này trong một configkhối để gọi phương thức này và tùy chỉnh dịch vụ. Cuối cùng khi chúng tôi chạy ứng dụng của mình, chúng tôi có thể lấy greetingdịch vụ và dùng thử để thấy rằng tùy chỉnh của chúng tôi có hiệu lực.

Vì đây là một ví dụ phức tạp hơn, đây là một minh chứng hoạt động: http://jsfiddle.net/BinaryMuse/9GjYg/

Bộ điều khiển ( $controller)

Các chức năng của bộ điều khiển có thể được đưa vào, nhưng bản thân các bộ điều khiển không thể được tiêm vào những thứ khác. Đó là bởi vì bộ điều khiển không được tạo thông qua nhà cung cấp. Thay vào đó, có một dịch vụ Angular tích hợp được gọi $controllerlà chịu trách nhiệm thiết lập bộ điều khiển của bạn. Khi bạn gọi myMod.controller(...), bạn thực sự đang truy cập nhà cung cấp dịch vụ này , giống như trong phần trước.

Ví dụ: khi bạn xác định bộ điều khiển như thế này:

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

Những gì bạn đang thực sự làm là đây:

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

Sau này, khi Angular cần tạo một thể hiện của bộ điều khiển của bạn, nó sẽ sử dụng $controllerdịch vụ (lần lượt sử dụng $injectorchức năng để gọi hàm điều khiển của bạn để nó cũng được thêm vào các phụ thuộc của nó).

Bộ lọc và Chỉ thị

filterdirectivelàm việc chính xác theo cách tương tự như controller; filtersử dụng một dịch vụ được gọi $filtervà nhà cung cấp của nó $filterProvider, trong khi directivesử dụng một dịch vụ được gọi $compilevà nhà cung cấp của nó $compileProvider. Một số liên kết:

Theo các ví dụ khác, myMod.filtermyMod.directivelà các phím tắt để cấu hình các dịch vụ này.


tl; dr

Vì vậy, để tóm tắt, bất kỳ chức năng nào được gọi với $injector.invoke có thể được đưa vào . Điều này bao gồm, từ biểu đồ của bạn (nhưng không giới hạn):

  • bộ điều khiển
  • chỉ thị
  • nhà máy
  • bộ lọc
  • nhà cung cấp $get(khi xác định nhà cung cấp là một đối tượng)
  • Hàm nhà cung cấp (khi xác định nhà cung cấp là hàm xây dựng)
  • dịch vụ

Nhà cung cấp tạo ra các dịch vụ mới có thể được tiêm vào mọi thứ . Điêu nay bao gôm:

  • không thay đổi
  • nhà máy
  • các nhà cung cấp
  • dịch vụ
  • giá trị

Điều đó nói rằng, các dịch vụ tích hợp như $controller$filter có thể được tiêm, và bạn có thể sử dụng các dịch vụ đó để nắm giữ các bộ lọc và bộ điều khiển mới mà bạn đã xác định bằng các phương thức đó (mặc dù chính những điều bạn xác định không thể tiêm vào vật).

Ngoài ra, bất kỳ chức năng nào được gọi là có thể được tiêm với bất kỳ dịch vụ nào do nhà cung cấp cung cấp - không có hạn chế nào (ngoài những khác biệt configrunđược liệt kê ở đây).


6
Ồ cảm ơn bạn đã dành thời gian để trả lời chi tiết như vậy! Tôi đã đọc điều này hai lần và tôi nghĩ rằng tôi đã hiểu khá nhiều. Gonna nghiên cứu nó và các liên kết bạn đã đưa ra chi tiết sau ngày hôm nay. Và +1 khác cho con mèo. :)
dùng1527166

18
Một trong những câu trả lời SO hữu ích và chi tiết nhất mà tôi đã gặp - cảm ơn!
Godder

11
Câu trả lời này xác định một cấp độ mới tuyệt vời. Công cụ chiếu sáng.
Ngure Nyaga

4
Cho đến nay, nguồn lực tốt nhất mà tôi đã tìm thấy cho AngularJS. Cảm ơn.
mã90

5
Nghĩa đen là phần tốt nhất của tài liệu AngularJS tôi đã thấy. Con đường để đi!
Iain Duncan

13

Điểm mà BinaryMuse đưa ra trong câu trả lời tuyệt vời của cô về các nhà cung cấp, nhà máy và dịch vụ đều là những điều cực kỳ quan trọng.

Dưới đây là một hình ảnh mà tôi nghĩ có thể minh họa trực quan quan điểm của cô ấy:

AngularJS tất cả họ chỉ là nhà cung cấp
(nguồn: Simplygoodcode.com )


7

Câu trả lời tuyệt vời của Michelle. Tôi chỉ muốn chỉ ra rằng các chỉ thị có thể được tiêm. Nếu bạn có một lệnh được đặt tên myThing, bạn có thể tiêm nó với myThingDirective: Đây là một ví dụ giả định .

Ví dụ trên không thực tế lắm, tuy nhiên khả năng tiêm chỉ thị rất hữu ích khi bạn muốn trang trí chỉ thị đó .


Có vẻ như ví dụ thứ hai để trang trí cho lệnh đó không hoạt động kể từ Angular 1.4. (xem bình luận của Juan Biscaia ở đó)
Vadorequest
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.