Làm việc với $ scope. $ Emit và $ scope. $ Trên


887

Làm cách nào tôi có thể gửi $scopeđối tượng của mình từ bộ điều khiển này sang bộ điều khiển khác bằng cách sử dụng .$emit.$onphương thức?

function firstCtrl($scope) {
    $scope.$emit('someEvent', [1,2,3]);
}

function secondCtrl($scope) {
    $scope.$on('someEvent', function(mass) { console.log(mass); });
}

Nó không hoạt động theo cách tôi nghĩ nó nên. Làm thế nào $emit$onlàm việc?


6
Chỉ dành cho độc giả trong tương lai: không sử dụng $rootScopeđể phát / phát khi có thể tránh được.
Mistalis

Câu trả lời:


1499

Trước hết, quan hệ phạm vi cha-con có vấn đề. Bạn có hai khả năng để phát ra một số sự kiện:

  • $broadcast - gửi sự kiện xuống dưới cho tất cả các phạm vi con,
  • $emit - gửi sự kiện đi lên thông qua hệ thống phân cấp phạm vi.

Tôi không biết gì về mối quan hệ (bộ điều khiển) của bạn, nhưng có một số tùy chọn:

  1. Nếu phạm vi firstCtrllà mẹ của secondCtrlphạm vi, mã của bạn nên làm việc bằng cách thay thế $emitbởi $broadcasttrong firstCtrl:

    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
  2. Trong trường hợp không có mối quan hệ cha-con giữa các phạm vi của bạn, bạn có thể đưa $rootScopevào bộ điều khiển và phát sự kiện tới tất cả các phạm vi con (tức là cũng vậy secondCtrl).

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
  3. Cuối cùng, khi bạn cần gửi sự kiện từ bộ điều khiển con đến phạm vi trở lên, bạn có thể sử dụng $scope.$emit. Nếu phạm vi firstCtrllà cha mẹ của secondCtrlphạm vi:

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }

8
Có cách nào để kích hoạt một sự kiện từ một dịch vụ đến một bộ điều khiển không?
Zlatko

29
Có về mặt lý thuyết bạn có thể tiêm $rootScopevào dịch vụ của mình và phát sự kiện từ dịch vụ.
zbynour

13
@Zlatko Tôi khá chắc chắn các dịch vụ theo mặc định là không có phạm vi và bạn cần một phạm vi để tham gia vào hệ thống sự kiện. Vì vậy, bằng cách nào đó bạn cần cung cấp một phạm vi cho dịch vụ của bạn. $ rootScope là giải pháp có mục đích chung nhất cho điều đó, nhưng nếu bạn muốn dịch vụ của mình gửi các sự kiện từ một phạm vi khác, bộ điều khiển của bạn có thể chuyển phạm vi của nó cho dịch vụ bằng cách đặt thuộc tính trên dịch vụ và bây giờ dịch vụ có thể sử dụng phạm vi điều khiển. Một kỹ thuật đơn giản hơn có thể dành cho bộ điều khiển để cung cấp chức năng cho dịch vụ mà dịch vụ có thể gọi trực tiếp.
Oran Dennison

3
Nếu bạn đang sử dụng iframe, bài viết này sẽ hữu ích charemza.name/blog/posts/angularjs/iframe/ trộm
leticia

1
Các dịch vụ có thể thực hiện $rootScope- nhưng tôi muốn biết rằng nếu tôi phát ra một sự kiện từ một dịch vụ (ngoài $rootScope), thì sự kiện đó vẫn sẽ thấm vào $rootScope; VÌ SAO, nếu $broadcastpercolate DOWN hệ thống phân cấp và $emitpercolates UP - điều gì xảy ra GIỮA "UP" và "DOWN" - vì bộ phát / phát cũng là người nghe (?). Điều gì sẽ xảy ra nếu tôi muốn sự kiện im lặng với TẤT CẢ phạm vi "LÊN TỚI" và TẤT CẢ "TẢI XUỐNG", nhưng chỉ có thể 'nghe được' ở cùng cấp độ với người điều phối?
Cody

145

Tôi cũng sẽ đề xuất một tùy chọn thứ 4 như là một lựa chọn tốt hơn cho các tùy chọn được đề xuất bởi @zbynour.

Sử dụng $rootScope.$emitchứ $rootScope.$broadcastkhông quan tâm đến mối quan hệ giữa bộ điều khiển và bộ điều khiển nhận. Theo cách đó, sự kiện vẫn nằm trong tập hợp $rootScope.$$listenerstrong khi với $rootScope.$broadcastsự kiện được truyền tới tất cả các phạm vi trẻ em, hầu hết trong số đó có thể sẽ không phải là người nghe sự kiện đó. Và tất nhiên trong phần cuối của bộ điều khiển nhận bạn chỉ cần sử dụng $rootScope.$on.

Đối với tùy chọn này, bạn phải nhớ tiêu diệt trình nghe rootScope của bộ điều khiển:

var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
  unbindEventHandler();
});

3
Điều này về cơ bản sẽ phục vụ như một xe buýt sự kiện trung tâm đúng không?
jusopi

5
Theo một nghĩa nào đó, lợi ích là bạn tránh được việc truyền bá sự kiện.
Thalis K.

3
@ThalisK. cảm ơn vì lựa chọn này Nó tránh sự lan truyền nhưng mặt khác, nó đòi hỏi phải $rootScopetiêm vào bộ điều khiển (nói chung là không cần thiết). Nhưng chắc chắn là một lựa chọn khác, thx!
zbynour

77
Coi chừng $ rootScope tồn tại mãi mãi. Nếu bộ điều khiển của bạn được chạy hai lần, bất kỳ $ rootScope. $ Ở bên trong nó sẽ được chạy hai lần và các sự kiện bị bắt sẽ dẫn đến một cuộc gọi lại được gọi hai lần. Nếu bạn sử dụng $ scope. $ Thay vào đó, cuộc gọi lại sẽ bị hủy cùng với bộ điều khiển của bạn bởi AngularJS.
Filip Sobczak

1
Theo nhận xét của @FilipSobczak, bạn có thể tránh hành vi không mong muốn này bằng cách hủy xử lý sự kiện $ hủy với mã sau jsfiddle.net/ndqexjsg/1
Krzysztof Grzybek

111

Làm cách nào tôi có thể gửi đối tượng phạm vi $ của mình từ bộ điều khiển này sang bộ điều khiển khác bằng cách sử dụng. $ Emit và. $ Trên các phương thức?

Bạn có thể gửi bất kỳ đối tượng nào bạn muốn trong hệ thống phân cấp của ứng dụng, bao gồm $ scope .

Dưới đây là một ý tưởng nhanh về cách phát sóngphát ra công việc.

Lưu ý các nút bên dưới; tất cả được lồng trong nút 3. Bạn sử dụng quảng báphát ra khi bạn có kịch bản này.

Lưu ý: Số lượng của mỗi nút trong ví dụ này là tùy ý; nó có thể dễ dàng là số một; số hai; hoặc thậm chí là số 1.348. Mỗi số chỉ là một định danh cho ví dụ này. Điểm của ví dụ này là hiển thị lồng các bộ điều khiển / chỉ thị Angular.

                 3
           ------------
           |          |
         -----     ------
         1   |     2    |
      ---   ---   ---  ---
      | |   | |   | |  | |

Kiểm tra cây này. Làm thế nào để bạn trả lời các câu hỏi sau đây?

Lưu ý: Có nhiều cách khác để trả lời những câu hỏi này, nhưng ở đây chúng ta sẽ thảo luận về phát sóngphát ra . Ngoài ra, khi đọc văn bản bên dưới, giả sử mỗi số có tệp riêng (chỉ thị, bộ điều khiển) ex one.js, Two.js, ba.js.

Làm thế nào để nút 1 nói với nút 3 ?

Trong tệp one.js

scope.$emit('messageOne', someValue(s));

Trong tệp ba.js - nút cao nhất cho tất cả các nút con cần thiết để giao tiếp.

scope.$on('messageOne', someValue(s));

Làm thế nào để nút 2 nói với nút 3?

Trong tệp Two.js

scope.$emit('messageTwo', someValue(s));

Trong tệp ba.js - nút cao nhất cho tất cả các nút con cần thiết để giao tiếp.

scope.$on('messageTwo', someValue(s));

Làm thế nào để nút 3 nói với nút 1 và / hoặc nút 2?

Trong tệp ba.js - nút cao nhất cho tất cả các nút con cần thiết để giao tiếp.

scope.$broadcast('messageThree', someValue(s));

Trong tệp one.js && Two.js bất kỳ tệp nào bạn muốn bắt thông báo hoặc cả hai.

scope.$on('messageThree', someValue(s));

Làm thế nào để nút 2 nói với nút 1?

Trong tệp Two.js

scope.$emit('messageTwo', someValue(s));

Trong tệp ba.js - nút cao nhất cho tất cả các nút con cần thiết để giao tiếp.

scope.$on('messageTwo', function( event, data ){
  scope.$broadcast( 'messageTwo', data );
});

Trong tệp one.js

scope.$on('messageTwo', someValue(s));

TUY NHIÊN

Khi bạn có tất cả các nút con lồng nhau này cố gắng giao tiếp như thế này, bạn sẽ nhanh chóng thấy nhiều nút $ on , $ Broadcast$ emit .

Đây là những gì tôi muốn làm.

Trong PARENT NODE cao nhất ( 3 trong trường hợp này ...), có thể là bộ điều khiển chính của bạn ...

Vì vậy, trong tệp ba.js

scope.$on('pushChangesToAllNodes', function( event, message ){
  scope.$broadcast( message.name, message.data );
});

Bây giờ trong bất kỳ nút con nào, bạn chỉ cần $ phát ra thông báo hoặc bắt nó bằng cách sử dụng $ on .

LƯU Ý: Thông thường khá dễ dàng để nói chuyện chéo trong một đường dẫn lồng nhau mà không sử dụng $ emit , $ Broadcast hoặc $ on , điều đó có nghĩa là hầu hết các trường hợp sử dụng là khi bạn đang cố gắng để nút 1 giao tiếp với nút 2 hoặc ngược lại.

Làm thế nào để nút 2 nói với nút 1?

Trong tệp Two.js

scope.$emit('pushChangesToAllNodes', sendNewChanges());

function sendNewChanges(){ // for some event.
  return { name: 'talkToOne', data: [1,2,3] };
}

Trong tệp ba.js - nút cao nhất cho tất cả các nút con cần thiết để giao tiếp.

Chúng tôi đã xử lý này nhớ chưa?

Trong tệp one.js

scope.$on('talkToOne', function( event, arrayOfNumbers ){
  arrayOfNumbers.forEach(function(number){
    console.log(number);
  });
});

Bạn vẫn sẽ cần sử dụng $ on với từng giá trị cụ thể mà bạn muốn nắm bắt, nhưng bây giờ bạn có thể tạo bất cứ thứ gì bạn thích trong bất kỳ nút nào mà không phải lo lắng về cách đưa thông điệp qua khoảng cách nút cha khi chúng tôi bắt và phát PushChangesTo ALLNodes chung .

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


Làm thế nào để quyết định cái nào là 3,2 và 1?
HIRA THAKUR

3, 2 và 1 là các bộ điều khiển hoặc chỉ thị lồng nhau. Khi bạn tạo ứng dụng của mình, hãy ghi nhớ việc lồng và áp dụng logic ở trên. Ví dụ, chúng ta có thể nói 3 là $ rootScope của ứng dụng; và mọi thứ được lồng bên dưới nó. 3, 2 và 1 là tùy ý.
SoEzPz

Những ví dụ tuyệt vời! Nhưng tôi vẫn nghĩ rằng tốt hơn để sử dụng riêng sự kiện phối trong phụ huynh để trao đổi nhóm điều khiển. Cũng hữu ích để giữ sáng tạo của người gửi làm dịch vụ để sử dụng nó làm mẫu.
DenisKolodin

1
Theo các tài liệu góc trên $ Broadcast, The event life cycle starts at the scope on which $broadcast was called. All listeners listening for name event on this scope get notified. do đó, bạn (như tôi) sẽ nhận được một vòng lặp vô hạn nếu bạn triển khai ctrl1 nói chuyện với ctrl2 với $on('x', function(e, data) { $broadcast('x', data) })trên ctrl3. Bạn sẽ cần những dòng này trước khi phát sóng; if (e.targetScope.$id === $scope.$id) { return; }
Renato Gama

39

Để gửi $scope objecttừ bộ điều khiển này sang bộ điều khiển khác, tôi sẽ thảo luận về $rootScope.$broadcast$rootScope.$emitở đây vì chúng được sử dụng nhiều nhất.

Trường hợp 1 :

$ rootScope. $ phát sóng: -

$rootScope.$broadcast('myEvent',$scope.data);//Here `myEvent` is event name

$rootScope.$on('myEvent', function(event, data) {} //listener on `myEvent` event

$rootScopengười nghe không bị phá hủy tự động. Bạn cần phải phá hủy nó bằng cách sử dụng $destroy. Tốt hơn là sử dụng $scope.$onkhi trình nghe trên $scopebị hủy tự động, tức là ngay khi phạm vi $ bị hủy.

$scope.$on('myEvent', function(event, data) {}

Hoặc là,

  var customeEventListener = $rootScope.$on('myEvent', function(event, data) {

  }
  $scope.$on('$destroy', function() {
        customeEventListener();
  });

Trường hợp 2:

$ rootScope. $ phát ra:

   $rootScope.$emit('myEvent',$scope.data);

   $rootScope.$on('myEvent', function(event, data) {}//$scope.$on not works

Sự khác biệt chính trong $ emit và $ Broadcast là sự kiện $ rootScope. $ Emit phải được nghe bằng $ rootScope. $ On, vì sự kiện phát ra không bao giờ đi qua cây phạm vi. .
Trong trường hợp này, bạn cũng phải hủy trình nghe như trong trường hợp $ Broadcast.

Biên tập:

Tôi không thích sử dụng $rootScope.$broadcast + $scope.$onnhưng sử dụng $rootScope.$emit+ $rootScope.$on. Các $rootScope.$broadcast + $scope.$onkết hợp có thể gây ra vấn đề hiệu suất nghiêm trọng. Đó là bởi vì sự kiện sẽ bong bóng xuống qua tất cả các phạm vi.

Chỉnh sửa 2 :

Vấn đề được giải quyết trong câu trả lời này đã được giải quyết trong phiên bản angular.js 1.2.7. $ phát sóng bây giờ tránh bong bóng trên phạm vi chưa đăng ký và chạy nhanh như $ phát ra.


10

Bạn phải sử dụng $ rootScope để gửi và chụp các sự kiện giữa các bộ điều khiển trong cùng một ứng dụng. Tiêm phụ thuộc $ rootScope vào bộ điều khiển của bạn. Dưới đây là một ví dụ làm việc.

app.controller('firstCtrl', function($scope, $rootScope) {        
        function firstCtrl($scope) {
        {
            $rootScope.$emit('someEvent', [1,2,3]);
        }
}

app.controller('secondCtrl', function($scope, $rootScope) {
        function secondCtrl($scope)
        {
            $rootScope.$on('someEvent', function(event, data) { console.log(data); });
        }
}

Các sự kiện được liên kết vào đối tượng $ scope chỉ hoạt động trong trình điều khiển chủ sở hữu. Giao tiếp giữa các bộ điều khiển được thực hiện thông qua $ rootScope hoặc Services.


7

Bạn có thể gọi một dịch vụ từ bộ điều khiển của bạn trả lại lời hứa và sau đó sử dụng nó trong bộ điều khiển của bạn. Và tiếp tục sử dụng $emithoặc $broadcastđể thông báo cho các bộ điều khiển khác về nó. Trong trường hợp của tôi, tôi đã phải thực hiện các cuộc gọi http thông qua dịch vụ của mình, vì vậy tôi đã làm một cái gì đó như thế này:

function ParentController($scope, testService) {
    testService.getList()
        .then(function(data) {
            $scope.list = testService.list;
        })
        .finally(function() {
            $scope.$emit('listFetched');
        })


    function ChildController($scope, testService) {
        $scope.$on('listFetched', function(event, data) {
            // use the data accordingly
        })
    }

và dịch vụ của tôi trông như thế này

    app.service('testService', ['$http', function($http) {

        this.list = [];

        this.getList = function() {
            return $http.get(someUrl)
                .then(function(response) {
                    if (typeof response.data === 'object') {
                        list = response.data.results;

                        return response.data;
                    } else {
                        // invalid response
                        return $q.reject(response.data);
                    }

                }, function(response) {
                    // something went wrong
                    return $q.reject(response.data);
                });

        }

    }])

4

Đây là chức năng của tôi:

$rootScope.$emit('setTitle', newVal.full_name);

$rootScope.$on('setTitle', function(event, title) {
    if (scope.item) 
        scope.item.name = title;
    else 
        scope.item = {name: title};
});

1
Tôi nghĩ rằng đây là thực tế xấu vì rootScope của bạn sẽ bị lộn xộn. Xem stackoverflow.com/questions/24830679/ từ
SKuijers 18/03/2015

4
<!DOCTYPE html>
<html>

<head>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
var app = angular.module('MyApp',[]);
app.controller('parentCtrl',function($scope){
  $scope.$on('MyEvent',function(event,data){    
    $scope.myData = data;
  });
 });

app.controller('childCtrl',function($scope){
  $scope.fireEvent = function(){ 
  $scope.$emit('MyEvent','Any Data');
  }  
 });
</script>
</head>
<body ng-app="MyApp">
<div ng-controller="parentCtrl" ng-model="myName">

{{myData}}

 <div ng-controller="childCtrl">
   <button ng-click="fireEvent()">Fire Event</button>
 </div>

</div>
</body>
</html>

2

Phạm vi có thể được sử dụng để tuyên truyền, gửi sự kiện đến phạm vi con hoặc cha mẹ.

$ phát ra - tuyên truyền sự kiện cho phụ huynh. $ Broadcast - tuyên truyền sự kiện cho trẻ em. $ on - phương thức để nghe các sự kiện, được truyền bá bởi $ emit và $ Broadcast.

thí dụ index.html :

<div ng-app="appExample" ng-controller="EventCtrl">
      Root(Parent) scope count: {{count}}
  <div>
      <button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
      <button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button><br>

      Childrent scope count: {{count}} 
  </div>
</div>

thí dụ app.js :

angular.module('appExample', [])
.controller('EventCtrl', ['$scope', function($scope) {
  $scope.count = 0;
  $scope.$on('MyEvent', function() {
    $scope.count++;
  });
}]);

Tại đây bạn có thể kiểm tra mã: http://jsfiddle.net/zp6v0rut/41/


2

Mã bên dưới hiển thị hai bộ điều khiển phụ từ đó các sự kiện được gửi lên tới bộ điều khiển chính (rootScope)

<body ng-app="App">

    <div ng-controller="parentCtrl">

        <p>City : {{city}} </p>
        <p> Address : {{address}} </p>

        <div ng-controller="subCtrlOne">
            <input type="text" ng-model="city" />
            <button ng-click="getCity(city)">City !!!</button>
        </div>

        <div ng-controller="subCtrlTwo">

            <input type="text" ng-model="address" />
            <button ng-click="getAddrress(address)">Address !!!</button>

        </div>

    </div>

</body>

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

// parent controller
App.controller('parentCtrl', parentCtrl);

parentCtrl.$inject = ["$scope"];

function parentCtrl($scope) {

    $scope.$on('cityBoom', function(events, data) {
        $scope.city = data;
    });

    $scope.$on('addrBoom', function(events, data) {
        $scope.address = data;
    });
}

// sub controller one

App.controller('subCtrlOne', subCtrlOne);

subCtrlOne.$inject = ['$scope'];

function subCtrlOne($scope) {

    $scope.getCity = function(city) {

        $scope.$emit('cityBoom', city);    
    }
}

// sub controller two

App.controller('subCtrlTwo', subCtrlTwo);

subCtrlTwo.$inject = ["$scope"];

function subCtrlTwo($scope) {

    $scope.getAddrress = function(addr) {

        $scope.$emit('addrBoom', addr);   
    }
}

http://jsfiddle.net/shushanthp/zp6v0rut/


0

Theo tài liệu sự kiện angularjs, phần cuối nhận phải chứa các đối số có cấu trúc như

@params

- Sự kiện {Object} là đối tượng sự kiện chứa thông tin về sự kiện

- {Object} lập luận được thông qua bởi callee (Lưu ý rằng đây chỉ có thể là một thứ tốt hơn để gửi trong một đối tượng từ điển)

$scope.$on('fooEvent', function (event, args) { console.log(args) }); Từ mã của bạn

Ngoài ra, nếu bạn đang cố gắng để có được một phần thông tin được chia sẻ trên các bộ điều khiển khác nhau, có một cách khác để đạt được điều đó và đó là các dịch vụ góc. Vì vậy, các dịch vụ là thông tin có thể được lưu trữ và tìm nạp trên các bộ điều khiển. các hàm setter trong dịch vụ đó, hiển thị các hàm này, tạo các biến toàn cục trong dịch vụ và sử dụng chúng để lưu trữ thông tin


0

Cách dễ nhất :

HTML

  <div ng-app="myApp" ng-controller="myCtrl"> 

        <button ng-click="sendData();"> Send Data </button>

    </div>

JavaScript

    <script>
        var app = angular.module('myApp', []);
        app.controller('myCtrl', function($scope, $rootScope) {
            function sendData($scope) {
                var arrayData = ['sam','rumona','cubby'];
                $rootScope.$emit('someEvent', arrayData);
            }

        });
        app.controller('yourCtrl', function($scope, $rootScope) {
            $rootScope.$on('someEvent', function(event, data) {
                console.log(data); 
            }); 
        });
    </script>
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.