AngularJS: Cách liên kết chính xác với thuộc tính dịch vụ


162

Tôi đang tìm cách thực hành tốt nhất về cách liên kết với một tài sản dịch vụ trong AngularJS.

Tôi đã làm việc qua nhiều ví dụ để hiểu cách liên kết với các thuộc tính trong một dịch vụ được tạo bằng AngularJS.

Dưới đây tôi có hai ví dụ về cách liên kết với các thuộc tính trong một dịch vụ; cả hai đều làm việc. Ví dụ đầu tiên sử dụng các ràng buộc cơ bản và ví dụ thứ hai đã sử dụng $ scope. $ Watch để liên kết với các thuộc tính dịch vụ

Là một trong những ví dụ này được ưa thích khi liên kết với các thuộc tính trong một dịch vụ hoặc có một tùy chọn khác mà tôi không biết về điều đó sẽ được đề xuất không?

Tiền đề của những ví dụ này là dịch vụ nên cập nhật các thuộc tính của mình. Khi các thuộc tính dịch vụ được cập nhật, khung nhìn sẽ phản ánh những thay đổi này. Cả hai ví dụ này hoạt động thành công; Tôi tự hỏi nếu có một cách tốt hơn để làm điều đó.

Ràng buộc cơ bản

Các mã sau đây có thể được xem và chạy ở đây: http://plnkr.co/edit/d3c16z

<html>
<body ng-app="ServiceNotification" >

    <div ng-controller="TimerCtrl1" style="border-style:dotted"> 
        TimerCtrl1 <br/>
        Last Updated: {{timerData.lastUpdated}}<br/>
        Last Updated: {{timerData.calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.timerData = Timer.data;
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

Một cách khác tôi đã giải quyết ràng buộc với các thuộc tính dịch vụ là sử dụng $ scope. $ Watch trong bộ điều khiển.

$ phạm vi. $ xem

Các mã sau đây có thể được xem và chạy ở đây: http://plnkr.co/edit/dSBlC9

<html>
<body ng-app="ServiceNotification">
    <div style="border-style:dotted" ng-controller="TimerCtrl1">
        TimerCtrl1<br/>
        Last Updated: {{lastUpdated}}<br/>
        Last Updated: {{calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.$watch(function () { return Timer.data.lastUpdated; },
                function (value) {
                    console.log("In $watch - lastUpdated:" + value);
                    $scope.lastUpdated = value;
                }
            );

            $scope.$watch(function () { return Timer.data.calls; },
                function (value) {
                    console.log("In $watch - calls:" + value);
                    $scope.calls = value;
                }
            );
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

Tôi biết rằng tôi có thể sử dụng $ rootscope. $ Broadcast trong dịch vụ và $ root. $ On trong bộ điều khiển, nhưng trong các ví dụ khác mà tôi đã tạo sử dụng $ Broadcast / $ trong lần phát đầu tiên không bị bắt bởi bộ điều khiển, nhưng các cuộc gọi bổ sung được phát sóng được kích hoạt trong bộ điều khiển. Nếu bạn biết một cách để giải quyết vấn đề $ rootscope. $ Broadcast, vui lòng cung cấp câu trả lời.

Nhưng để nói lại những gì tôi đã đề cập trước đó, tôi muốn biết cách thực hành tốt nhất về cách liên kết với một thuộc tính dịch vụ.


Cập nhật

Câu hỏi này ban đầu được hỏi và trả lời vào tháng 4 năm 2013. Vào tháng 5 năm 2014, Gil Birman đã cung cấp một câu trả lời mới, mà tôi đã thay đổi thành câu trả lời đúng. Vì câu trả lời của Gil Birman có rất ít phiếu bầu, nên mối quan tâm của tôi là những người đọc câu hỏi này sẽ bỏ qua câu trả lời của anh ta để ủng hộ các câu trả lời khác có nhiều phiếu hơn. Trước khi bạn đưa ra quyết định về câu trả lời hay nhất, tôi đánh giá cao câu trả lời của Gil Birman.


Tôi nghĩ câu trả lời của Josh David Miller tốt hơn là của Gil Birman. Và sử dụng $ watch, $ watchgroup và $ watchCollection thậm chí có thể làm cho nó thậm chí còn tốt hơn. Tách các mối quan tâm là rất quan trọng trên các ứng dụng kích thước trung bình đến lớn.
Jonathan

@bardev Tôi nghĩ cả hai câu trả lời đều không hữu ích và các nhà phát triển mới sẽ hiểu họ hoàn toàn sai.
Zalaboza

Vấn đề bạn hỏi về liên quan đến hành vi biến JavaScript gốc của các đối tượng tham chiếu, tôi đã thêm một chút giải thích bên dưới
Zalaboza

Câu trả lời:


100

Xem xét một số ưu và nhược điểm của phương pháp thứ hai :

  • 0 {{lastUpdated}} thay vì {{timerData.lastUpdated}}, có thể dễ dàng như vậy {{timer.lastUpdated}}, điều mà tôi có thể tranh luận là dễ đọc hơn (nhưng đừng tranh luận ... Tôi đang đưa ra quan điểm trung lập này để bạn tự quyết định)

  • +1 Có thể thuận tiện khi bộ điều khiển hoạt động như một loại API cho đánh dấu sao cho nếu bằng cách nào đó cấu trúc của mô hình dữ liệu thay đổi, bạn có thể (về lý thuyết) cập nhật ánh xạ API của bộ điều khiển mà không cần chạm vào phần html.

  • -1 Tuy nhiên, lý thuyết không phải luôn luôn luyện tập và tôi thường thấy mình cần phải thay đổi đánh dấu Logic điều khiển khi thay đổi được kêu gọi, nào . Vì vậy, nỗ lực thêm của việc viết API phủ nhận lợi thế của nó.

  • -1 Hơn nữa, cách tiếp cận này không phải là rất KHÔ.

  • -1 Nếu bạn muốn liên kết dữ liệu với ng-modelmã của mình trở nên ít DRY hơn vì bạn phải đóng gói $scope.scalar_valueslại bộ điều khiển để thực hiện cuộc gọi REST mới.

  • -0.1 Có một hiệu suất nhỏ tạo ra người theo dõi thêm. Ngoài ra, nếu các thuộc tính dữ liệu được gắn vào mô hình không cần phải xem trong một bộ điều khiển cụ thể, chúng sẽ tạo thêm chi phí cho người theo dõi sâu.

  • -1 Điều gì xảy ra nếu nhiều bộ điều khiển cần cùng một mô hình dữ liệu? Điều đó có nghĩa là bạn có nhiều API để cập nhật với mỗi thay đổi mô hình.

$scope.timerData = Timer.data;đang bắt đầu nghe có vẻ rất hấp dẫn ngay bây giờ ... Chúng ta hãy đi sâu hơn một chút vào điểm cuối cùng đó ... Chúng ta đang nói về loại thay đổi mô hình nào? Một mô hình trên back-end (máy chủ)? Hay một mô hình được tạo ra và chỉ sống ở mặt trước? Trong cả hai trường hợp, về cơ bản, API ánh xạ dữ liệu thuộc về lớp dịch vụ mặt trước , (một nhà máy hoặc dịch vụ góc cạnh). (Lưu ý rằng ví dụ đầu tiên của bạn - sở thích của tôi-- không có API như vậy trong lớp dịch vụ , điều này cũng tốt vì nó đủ đơn giản để nó không cần đến nó.)

Tóm lại , mọi thứ không phải tách rời. Và theo như cách tách hoàn toàn đánh dấu khỏi mô hình dữ liệu, những nhược điểm lớn hơn những lợi thế.


Bộ điều khiển, nói chung không nên xả rác với $scope = injectable.data.scalar. Thay vào đó, chúng nên được rắc với $scope = injectable.data's, promise.then(..)' và $scope.complexClickAction = function() {..}'s

Là một cách tiếp cận khác để đạt được việc tách rời dữ liệu và do đó đóng gói khung nhìn, nơi duy nhất thực sự có ý nghĩa để tách rời chế độ xem khỏi mô hìnhbằng một lệnh . Nhưng ngay cả ở đó, không có $watchgiá trị vô hướng trong controllerhoặc linkhàm. Điều đó sẽ không tiết kiệm thời gian hoặc làm cho mã trở nên dễ bảo trì hơn hoặc không thể đọc được. Nó thậm chí sẽ không làm cho việc kiểm tra dễ dàng hơn vì các thử nghiệm mạnh mẽ ở góc thường thường kiểm tra DOM kết quả . Thay vào đó, trong một yêu cầu chỉ thị API dữ liệu của bạn ở dạng đối tượng và ưu tiên chỉ sử dụng các $watchers được tạo bởi ng-bind.


Ví dụ http://plnkr.co/edit/MVeU1GKRTN4bqA3h9Yio

<body ng-app="ServiceNotification">
    <div style="border-style:dotted" ng-controller="TimerCtrl1">
        TimerCtrl1<br/>
        Bad:<br/>
        Last Updated: {{lastUpdated}}<br/>
        Last Updated: {{calls}}<br/>
        Good:<br/>
        Last Updated: {{data.lastUpdated}}<br/>
        Last Updated: {{data.calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.data = Timer.data;
            $scope.lastUpdated = Timer.data.lastUpdated;
            $scope.calls = Timer.data.calls;
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 500);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>

CẬP NHẬT : Cuối cùng tôi đã quay lại câu hỏi này để thêm rằng tôi không nghĩ rằng một trong hai cách tiếp cận là "sai". Ban đầu tôi đã viết rằng câu trả lời của Josh David Miller là không chính xác, nhưng nhìn lại quan điểm của anh ta là hoàn toàn hợp lệ, đặc biệt là quan điểm của anh ta về việc tách các mối quan tâm.

Tách các mối quan tâm sang một bên (nhưng liên quan đến tiếp tuyến), có một lý do khác để sao chép phòng thủ mà tôi không xem xét. Câu hỏi này chủ yếu liên quan đến việc đọc dữ liệu trực tiếp từ một dịch vụ. Nhưng điều gì sẽ xảy ra nếu một nhà phát triển trong nhóm của bạn quyết định rằng bộ điều khiển cần chuyển đổi dữ liệu theo một cách tầm thường trước khi khung nhìn hiển thị nó? (Liệu các bộ điều khiển có nên chuyển đổi dữ liệu hay không là một cuộc thảo luận khác.) Nếu trước tiên cô ấy không tạo một bản sao của đối tượng, cô ấy có thể vô tình gây ra hồi quy trong một thành phần xem khác tiêu thụ cùng một dữ liệu.

Câu hỏi này thực sự nổi bật là những thiếu sót về kiến ​​trúc của ứng dụng góc điển hình (và thực sự là bất kỳ ứng dụng JavaScript nào): sự liên kết chặt chẽ giữa các mối quan tâm và tính biến đổi đối tượng. Gần đây tôi đã trở nên say mê với ứng dụng kiến ​​trúc với React cấu trúc dữ liệu bất biến. Làm như vậy sẽ giải quyết hai vấn đề sau đây một cách tuyệt vời:

  1. Tách biệt các mối quan tâm : Một thành phần tiêu thụ tất cả dữ liệu của nó thông qua các đạo cụ và không phụ thuộc nhiều vào các singletons toàn cầu (như dịch vụ Angular) và không biết gì về những gì đã xảy ra trên hệ thống phân cấp xem.

  2. Khả năng biến đổi: Tất cả các đạo cụ là bất biến giúp loại bỏ nguy cơ đột biến dữ liệu.

Angular 2.0 hiện đang trên đường vay mượn rất nhiều từ React để đạt được hai điểm trên.


1
Càng làm việc với AngularJS, tôi càng hiểu. Tôi tin rằng bộ điều khiển AngularJS nên đơn giản và mỏng nhất có thể. Bằng cách thêm đồng hồ $ trong bộ điều khiển, nó sẽ làm phức tạp logic. Bởi chỉ cần tham khảo giá trị trong dịch vụ, nó đơn giản hơn nhiều và dường như giống với cách của AngularJS. -
Mike Barlow - BarDev

3
"Mười điều răn" của AngularJS. Đó là tuyên bố cho một lý do.
pilau

Câu trả lời của Josh David Miller không phải là "TUYỆT VỜI". Có thời gian và địa điểm cho mọi việc.
JoshuaDavid

Tôi nghĩ bạn đã đúng, @FireCoding. Tôi đang lên kế hoạch cập nhật câu trả lời.
Gil Birman

@GilBirman câu trả lời tuyệt vời. Bạn có phiền khi viết và ví dụ cho chỉ thị? Để minh họa những gì bạn đã nói "nơi duy nhất thực sự hợp lý để tách rời chế độ xem khỏi mô hình là bằng một lệnh. [...] Thay vào đó, trong một lệnh yêu cầu API dữ liệu của bạn ở dạng đối tượng và ưu tiên sử dụng chỉ $ những người theo dõi được tạo ra bởi ng-bind. "
klode

78

Từ quan điểm của tôi, $watchsẽ là cách thực hành tốt nhất.

Bạn thực sự có thể đơn giản hóa ví dụ của bạn một chút:

function TimerCtrl1($scope, Timer) {
  $scope.$watch( function () { return Timer.data; }, function (data) {
    $scope.lastUpdated = data.lastUpdated;
    $scope.calls = data.calls;
  }, true);
}

Đó là tất cả những gì bạn cần.

Vì các thuộc tính được cập nhật đồng thời, bạn chỉ cần một chiếc đồng hồ. Ngoài ra, vì chúng đến từ một vật thể khá nhỏ, tôi đã thay đổi nó để chỉ xem Timer.datatài sản. Tham số cuối cùng được thông qua để $watchbảo nó kiểm tra sự bằng nhau sâu sắc thay vì chỉ đảm bảo rằng tham chiếu giống nhau.


Để cung cấp một bối cảnh nhỏ, lý do tôi muốn phương pháp này đặt giá trị dịch vụ trực tiếp trên phạm vi là để đảm bảo phân tách mối quan tâm đúng đắn. Quan điểm của bạn không cần biết gì về dịch vụ của bạn để hoạt động. Công việc của bộ điều khiển là dán mọi thứ lại với nhau; công việc của nó là lấy dữ liệu từ các dịch vụ của bạn và xử lý chúng theo bất kỳ cách nào cần thiết và sau đó cung cấp cho bạn quan điểm của bạn với bất kỳ chi tiết cụ thể nào nó cần. Nhưng tôi không nghĩ rằng công việc của nó là chỉ cần chuyển dịch vụ ngay để xem. Nếu không, những gì bộ điều khiển thậm chí làm ở đó? Các nhà phát triển AngularJS cũng tuân theo lý do tương tự khi họ chọn không đưa bất kỳ "logic" nào vào các mẫu (ví dụ: các ifcâu lệnh).

Công bằng mà nói, có lẽ có nhiều quan điểm ở đây và tôi mong chờ những câu trả lời khác.


3
Bạn có thể xây dựng một chút? Bạn có thích đồng hồ $ vì chế độ xem ít được kết hợp với dịch vụ không? Tức là, {{lastUpdated}}so với{{timerData.lastUpdated}}
Mark Rajcok

2
@BarDev, để sử dụng Timer.datatrong đồng hồ $, Timerphải được xác định trên phạm vi $, vì biểu thức chuỗi bạn chuyển đến $ watch được đánh giá theo phạm vi. Dưới đây là một plunker chỉ ra cách làm cho công việc đó. Tham số objectEquality được ghi lại ở đây - tham số thứ 3 - nhưng không thực sự giải thích quá rõ.
Mark Rajcok

2
Hiệu suất khôn ngoan, $watchkhá kém hiệu quả. Xem câu trả lời tại stackoverflow.com/a/17558885/932632stackoverflow.com/questions/12576798/ săn
Krym

11
@Kyrm Điều đó có thể đúng trong một số trường hợp, nhưng khi xử lý hiệu suất, chúng ta cần tìm kiếm các cải tiến hiệu suất "có ý nghĩa lâm sàng", thay vì chỉ có ý nghĩa thống kê, tổng quát. Nếu có một vấn đề về hiệu năng trong một ứng dụng hiện có, thì nó sẽ được giải quyết. Mặt khác, đó chỉ là một trường hợp tối ưu hóa sớm, dẫn đến mã khó đọc, dễ bị lỗi, không tuân theo các thực tiễn tốt nhất và không có lợi ích được chứng minh.
Josh David Miller

1
Giả sử người theo dõi sử dụng một chức năng để gọi getter, thì có. Nó hoạt động tốt. Tôi cũng là người đề xuất các dịch vụ trả về các thể hiện trong một bộ sưu tập, trong đó các tính năng getter và setter của es5 khá tốt thay thế.
Josh David Miller

19

Đến bữa tiệc muộn, nhưng đối với nhân viên Google trong tương lai - không sử dụng câu trả lời được cung cấp.

JavaScript có cơ chế truyền đối tượng theo tham chiếu, trong khi nó chỉ truyền một bản sao nông cho các giá trị "số, chuỗi, v.v.".

Trong ví dụ trên, thay vì các thuộc tính ràng buộc của một dịch vụ, tại sao chúng ta không đưa dịch vụ ra phạm vi?

$scope.hello = HelloService;

Cách tiếp cận đơn giản này sẽ làm cho góc cạnh có thể thực hiện ràng buộc hai chiều và tất cả những điều kỳ diệu bạn cần. Đừng hack bộ điều khiển của bạn với người theo dõi hoặc đánh dấu không cần thiết.

Và nếu bạn lo lắng về quan điểm của mình vô tình ghi đè lên các thuộc tính dịch vụ của bạn, hãy sử dụng definePropertyđể làm cho nó có thể đọc được, có thể đếm được, có thể định cấu hình hoặc xác định getters và setters. Bạn có thể giành được nhiều quyền kiểm soát bằng cách làm cho dịch vụ của bạn vững chắc hơn.

Mẹo cuối cùng: nếu bạn dành thời gian làm việc trên bộ điều khiển của bạn nhiều hơn các dịch vụ của bạn thì bạn đã làm sai :(.

Trong mã demo cụ thể mà bạn cung cấp, tôi khuyên bạn nên làm:

 function TimerCtrl1($scope, Timer) {
   $scope.timer = Timer;
 }
///Inside view
{{ timer.time_updated }}
{{ timer.other_property }}
etc...

Biên tập:

Như tôi đã đề cập ở trên, bạn có thể kiểm soát hành vi của các thuộc tính dịch vụ của mình bằng cách sử dụng defineProperty

Thí dụ:

// Lets expose a property named "propertyWithSetter" on our service
// and hook a setter function that automatically saves new value to db !
Object.defineProperty(self, 'propertyWithSetter', {
  get: function() { return self.data.variable; },
  set: function(newValue) { 
         self.data.variable = newValue; 
         // let's update the database too to reflect changes in data-model !
         self.updateDatabaseWithNewData(data);
       },
  enumerable: true,
  configurable: true
});

Bây giờ trong bộ điều khiển của chúng tôi nếu chúng ta làm

$scope.hello = HelloService;
$scope.hello.propertyWithSetter = 'NEW VALUE';

dịch vụ của chúng tôi sẽ thay đổi giá trị propertyWithSettervà cũng đăng giá trị mới vào cơ sở dữ liệu bằng cách nào đó!

Hoặc chúng ta có thể thực hiện bất kỳ phương pháp nào chúng ta muốn.

Tham khảo tài liệu MDN cho defineProperty.


Khá chắc chắn đó là những gì tôi đã mô tả ở trên, với $scope.model = {timerData: Timer.data};, chỉ cần gắn nó vào mô hình thay vì trực tiếp trên Phạm vi.
Scott Silvi

1
AFAIK, tham chiếu đối tượng js hoạt động cho mọi thứ trong Dịch vụ. Mã hóa như thế này: $ scope.controllVar = ServiceVar, mọi thứ bạn làm trong $ scope.controllVar cũng được thực hiện trong ServiceVar.
Kai Wang

@KaiWang đồng ý, trừ khi bạn quyết định sử dụng DefineAttribution thì bạn có thể làm cho dịch vụ của mình có chức năng setter để ngăn bộ điều khiển vô tình làm thay đổi dữ liệu dịch vụ của bạn.
Zalaboza

12

Tôi nghĩ rằng câu hỏi này có một thành phần theo ngữ cảnh.

Nếu bạn chỉ đơn giản là lấy dữ liệu từ một dịch vụ và phát thông tin đó đến chế độ xem, tôi nghĩ rằng ràng buộc trực tiếp vào thuộc tính dịch vụ là tốt. Tôi không muốn viết nhiều mã soạn sẵn để ánh xạ các thuộc tính dịch vụ thành các thuộc tính mô hình để sử dụng theo quan điểm của tôi.

Hơn nữa, hiệu suất trong góc được dựa trên hai điều. Đầu tiên là có bao nhiêu ràng buộc trên một trang. Thứ hai là làm thế nào các chức năng getter đắt tiền. Misko nói về điều này ở đây

Nếu bạn cần thực hiện logic cụ thể đối với dữ liệu dịch vụ (trái ngược với việc tạo khối dữ liệu được áp dụng trong chính dịch vụ) và kết quả của điều này tác động đến mô hình dữ liệu được hiển thị cho chế độ xem, thì tôi sẽ nói rằng một trình theo dõi $ là phù hợp, như miễn là chức năng không quá đắt. Trong trường hợp hàm đắt tiền, tôi sẽ đề xuất lưu trữ kết quả vào biến cục bộ (tới bộ điều khiển), thực hiện các hoạt động phức tạp của bạn bên ngoài hàm $ watcher, sau đó ràng buộc phạm vi của bạn với kết quả đó.

Như một lời cảnh báo, bạn không nên treo bất kỳ thuộc tính nào trực tiếp ra khỏi phạm vi $ của bạn. Các $scopebiến là KHÔNG mô hình của bạn. Nó có tài liệu tham khảo cho mô hình của bạn.

Trong tâm trí của tôi, "thực hành tốt nhất" chỉ đơn giản là phát thông tin từ dịch vụ xuống để xem:

function TimerCtrl1($scope, Timer) {
  $scope.model = {timerData: Timer.data};
};

Và sau đó, quan điểm của bạn sẽ chứa {{model.timerData.lastupdated}}.


cẩn thận với đề xuất đó, một người không phải là chuyên gia về javascript có thể thử làm điều đó với một thuộc tính là một chuỗi. Trong trường hợp đó, javascript không tạo tham chiếu đến đối tượng, nhưng sao chép chuỗi thô. (và đó là nguyên nhân xấu khiến nó không được cập nhật, nếu nó thay đổi)
Valerio

7
Tôi đã không bao gồm điều đó với 'lời cảnh báo' của mình rằng bạn nên luôn luôn sử dụng dấu chấm (có nghĩa là không treo nó ngoài phạm vi $, nhưng tắt $ scope.model). Nếu bạn có $ scope.model.someStringProperty và bạn tham chiếu model.someStringProperty trong chế độ xem của mình, nó S get được cập nhật, bởi vì các trình theo dõi bên trong sẽ nằm trên đối tượng, không phải là prop.
Scott Silvi

6

Dựa trên các ví dụ trên tôi nghĩ rằng tôi sẽ đưa ra một cách liên kết một cách rõ ràng một biến bộ điều khiển với một biến dịch vụ.

Trong ví dụ dưới đây, các thay đổi đối với $scope.countbiến Bộ điều khiển sẽ tự động được phản ánh trong countbiến Dịch vụ .

Trong sản xuất, chúng tôi thực sự sử dụng ràng buộc này để cập nhật id trên một dịch vụ sau đó tìm nạp dữ liệu không đồng bộ và cập nhật các lọ dịch vụ của nó. Ràng buộc hơn nữa có nghĩa là bộ điều khiển tự động được cập nhật khi dịch vụ tự cập nhật.

Mã dưới đây có thể được nhìn thấy làm việc tại http://jsfiddle.net/xuUHS/163/

Lượt xem:

<div ng-controller="ServiceCtrl">
    <p> This is my countService variable : {{count}}</p>
    <input type="number" ng-model="count">
    <p> This is my updated after click variable : {{countS}}</p>

    <button ng-click="clickC()" >Controller ++ </button>
    <button ng-click="chkC()" >Check Controller Count</button>
    </br>

    <button ng-click="clickS()" >Service ++ </button>
    <button ng-click="chkS()" >Check Service Count</button>
</div>

Dịch vụ / Kiểm soát viên:

var app = angular.module('myApp', []);

app.service('testService', function(){
    var count = 10;

    function incrementCount() {
      count++;
      return count;
    };

    function getCount() { return count; }

    return {
        get count() { return count },
        set count(val) {
            count = val;
        },
        getCount: getCount,
        incrementCount: incrementCount
    }

});

function ServiceCtrl($scope, testService)
{

    Object.defineProperty($scope, 'count', {
        get: function() { return testService.count; },
        set: function(val) { testService.count = val; },
    });

    $scope.clickC = function () {
       $scope.count++;
    };
    $scope.chkC = function () {
        alert($scope.count);
    };

    $scope.clickS = function () {
       ++testService.count;
    };
    $scope.chkS = function () {
        alert(testService.count);
    };

}

Đây là một giải pháp tuyệt vời, cảm ơn bạn, điều này đã giúp tôi rất nhiều, cách làm rất thông minh :)
Javis Perez

2

Tôi nghĩ rằng đó là một cách tốt hơn để ràng buộc vào chính dịch vụ thay vì các thuộc tính trên nó.

Đây là lý do tại sao:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>
<body ng-app="BindToService">

  <div ng-controller="BindToServiceCtrl as ctrl">
    ArrService.arrOne: <span ng-repeat="v in ArrService.arrOne">{{v}}</span>
    <br />
    ArrService.arrTwo: <span ng-repeat="v in ArrService.arrTwo">{{v}}</span>
    <br />
    <br />
    <!-- This is empty since $scope.arrOne never changes -->
    arrOne: <span ng-repeat="v in arrOne">{{v}}</span>
    <br />
    <!-- This is not empty since $scope.arrTwo === ArrService.arrTwo -->
    <!-- Both of them point the memory space modified by the `push` function below -->
    arrTwo: <span ng-repeat="v in arrTwo">{{v}}</span>
  </div>

  <script type="text/javascript">
    var app = angular.module("BindToService", []);

    app.controller("BindToServiceCtrl", function ($scope, ArrService) {
      $scope.ArrService = ArrService;
      $scope.arrOne = ArrService.arrOne;
      $scope.arrTwo = ArrService.arrTwo;
    });

    app.service("ArrService", function ($interval) {
      var that = this,
          i = 0;
      this.arrOne = [];
      that.arrTwo = [];

      $interval(function () {
        // This will change arrOne (the pointer).
        // However, $scope.arrOne is still same as the original arrOne.
        that.arrOne = that.arrOne.concat([i]);

        // This line changes the memory block pointed by arrTwo.
        // And arrTwo (the pointer) itself never changes.
        that.arrTwo.push(i);
        i += 1;
      }, 1000);

    });
  </script>
</body> 

Bạn có thể chơi nó trên plunker này .


1

Tôi thà giữ người theo dõi của tôi càng ít càng tốt. Lý do của tôi dựa trên kinh nghiệm của tôi và người ta có thể tranh luận về mặt lý thuyết.
Vấn đề với việc sử dụng trình theo dõi là bạn có thể sử dụng bất kỳ thuộc tính nào trên phạm vi để gọi bất kỳ phương thức nào trong bất kỳ thành phần hoặc dịch vụ nào bạn muốn.
Trong một dự án thực tế, rất nhanh chóng, bạn sẽ kết thúc với một phi tracable (nói đúng hơn khó có thể dấu vết) chuỗi các phương pháp được gọi và các giá trị được thay đổi trong đó đặc biệt làm cho quá trình on-nội trú bi thảm.


0

Để ràng buộc bất kỳ dữ liệu nào gửi dịch vụ không phải là một ý tưởng tốt (kiến trúc), nhưng nếu bạn cần nó nữa, tôi đề nghị bạn 2 cách để làm điều đó

1) bạn có thể lấy dữ liệu không phải trong dịch vụ của mình. Bạn có thể lấy dữ liệu trong bộ điều khiển / chỉ thị của mình và bạn sẽ không gặp vấn đề gì khi liên kết nó ở bất cứ đâu

2) bạn có thể sử dụng các sự kiện angularjs. Bất cứ khi nào bạn muốn, bạn có thể gửi tín hiệu (từ $ rootScope) và bắt nó bất cứ nơi nào bạn muốn. Thậm chí bạn có thể gửi dữ liệu trên tên sự kiện đó.

Có lẽ điều này có thể giúp bạn. Nếu bạn cần thêm với các ví dụ, đây là liên kết

http://www.w3docs.com/snippets/angularjs/bind-value-b between-service-and -controll-directive.html


-1

Thế còn

scope = _.extend(scope, ParentScope);

ParentScope là một dịch vụ được tiêm?


-2

Các giải pháp thanh lịch nhất ...

app.service('svc', function(){ this.attr = []; return this; });
app.controller('ctrl', function($scope, svc){
    $scope.attr = svc.attr || [];
    $scope.$watch('attr', function(neo, old){ /* if necessary */ });
});
app.run(function($rootScope, svc){
    $rootScope.svc = svc;
    $rootScope.$watch('svc', function(neo, old){ /* change the world */ });
});

Ngoài ra, tôi viết EDA (Kiến trúc hướng sự kiện) vì vậy tôi có xu hướng làm một cái gì đó giống như [phiên bản đơn giản hóa] sau đây:

var Service = function Service($rootScope) {
    var $scope = $rootScope.$new(this);
    $scope.that = [];
    $scope.$watch('that', thatObserver, true);
    function thatObserver(what) {
        $scope.$broadcast('that:changed', what);
    }
};

Sau đó, tôi đặt một trình nghe trong bộ điều khiển của mình trên kênh mong muốn và chỉ cần cập nhật phạm vi cục bộ của mình theo cách này.

Tóm lại, không có nhiều "Thực tiễn tốt nhất" - thay vào đó, chủ yếu là ưu tiên của nó - miễn là bạn giữ mọi thứ RẮN và sử dụng khớp nối yếu. Lý do tôi sẽ ủng hộ mã sau là vì các EDA có tính khớp nối thấp nhất khả thi về bản chất. Và nếu bạn không quá quan tâm đến thực tế này, chúng ta hãy tránh làm việc trên cùng một dự án.

Hi vọng điêu nay co ich...

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.