วิธีที่ถูกต้องในการสื่อสารระหว่างตัวควบคุมใน AngularJS คืออะไร?


473

วิธีที่ถูกต้องในการสื่อสารระหว่างตัวควบคุมคืออะไร?

ฉันกำลังใช้เรื่องเหลวไหลที่น่ากลัวเกี่ยวกับwindow:

function StockSubgroupCtrl($scope, $http) {
    $scope.subgroups = [];
    $scope.handleSubgroupsLoaded = function(data, status) {
        $scope.subgroups = data;
    }
    $scope.fetch = function(prod_grp) {
        $http.get('/api/stock/groups/' + prod_grp + '/subgroups/').success($scope.handleSubgroupsLoaded);
    }
    window.fetchStockSubgroups = $scope.fetch;
}

function StockGroupCtrl($scope, $http) {
    ...
    $scope.select = function(prod_grp) {
        $scope.selectedGroup = prod_grp;
        window.fetchStockSubgroups(prod_grp);
    }
}

36
moot ทั้งหมด แต่ใน Angular คุณควรใช้ $ window แทนที่จะเป็นวัตถุหน้าต่าง JS ดั้งเดิม วิธีนี้คุณสามารถต้นขั้วมันออกมาในการทดสอบของคุณ :)
แดน M

1
โปรดดูความคิดเห็นในคำตอบด้านล่างจากฉันเกี่ยวกับปัญหานี้ $ Broadcast ไม่แพงกว่า $ emit อีกต่อไป ดูลิงก์ jsperf ที่ฉันอ้างถึงที่นั่น
zumalifeguard

คำตอบ:


457

แก้ไข : ปัญหาการแก้ไขในคำตอบนี้ได้รับการแก้ไขใน angular.js เวอร์ชัน 1.2.7 $broadcastตอนนี้หลีกเลี่ยงการเดือดดาลเหนือขอบเขตที่ไม่ได้ลงทะเบียนและทำงานได้เร็วเท่ากับ $ emit การแสดง $ Broadcast เหมือนกับ $ emit ที่มีมุม 1.2.16

ดังนั้นตอนนี้คุณสามารถ:

  • ใช้$broadcastจาก$rootScope
  • ฟังการใช้งาน$on จากท้องถิ่น$scopeที่จำเป็นต้องรู้เกี่ยวกับเหตุการณ์

คำตอบเดิมด้านล่าง

ผมขอให้คำแนะนำที่จะไม่ใช้$rootScope.$broadcast+ $scope.$onแต่+$rootScope.$emit $rootScope.$onอดีตอาจทำให้เกิดปัญหาประสิทธิภาพการทำงานที่ร้ายแรงโดย @numan นั่นเป็นเพราะเหตุการณ์จะทำให้ฟองดูในขอบเขตทั้งหมด

อย่างไรก็ตามหลัง (ใช้$rootScope.$emit+ $rootScope.$on) ไม่ประสบกับสิ่งนี้และสามารถใช้เป็นช่องทางการสื่อสารที่รวดเร็ว!

จากเอกสารเชิงมุมของ$emit:

ยื้อชื่อเหตุการณ์ขึ้นไปตามลำดับชั้นขอบเขตแจ้งการลงทะเบียน

เนื่องจากไม่มีขอบเขตด้านบน$rootScopeจึงไม่มีการเดือดปุด ๆ มีความปลอดภัยทั้งหมดที่จะใช้$rootScope.$emit()/ $rootScope.$on()เป็น EventBus

อย่างไรก็ตามมีหนึ่ง gotcha เมื่อใช้มันจากภายในตัวควบคุม ถ้าคุณผูกโดยตรง$rootScope.$on()จากภายในตัวควบคุมคุณจะต้องทำความสะอาดตัวเองเมื่อท้องถิ่นของคุณ$scopeถูกทำลาย นี่เป็นเพราะตัวควบคุม (ตรงกันข้ามกับบริการ) สามารถรับอินสแตนซ์ได้หลายครั้งตลอดอายุการใช้งานของแอปพลิเคชันซึ่งจะส่งผลให้เกิดการสรุปรวมในที่สุดการสร้างหน่วยความจำรั่วทั่วสถานที่ :)

ถอนการลงทะเบียนเพียงแค่ฟังคุณ$scope's เหตุการณ์แล้วเรียกฟังก์ชั่นที่ถูกส่งกลับโดย$destroy$rootScope.$on

angular
    .module('MyApp')
    .controller('MyController', ['$scope', '$rootScope', function MyController($scope, $rootScope) {

            var unbind = $rootScope.$on('someComponent.someCrazyEvent', function(){
                console.log('foo');
            });

            $scope.$on('$destroy', unbind);
        }
    ]);

ฉันจะบอกว่านั่นไม่ใช่สิ่งที่เฉพาะเจาะจงเชิงมุมเพราะมันใช้กับการใช้งาน EventBus อื่น ๆ เช่นกันว่าคุณต้องทำความสะอาดทรัพยากร

อย่างไรก็ตามคุณสามารถทำให้ชีวิตของคุณง่ายขึ้นสำหรับกรณีเหล่านั้น ตัวอย่างเช่นคุณสามารถแก้ไขลิง$rootScopeและให้มัน$onRootScopeที่สมัครสมาชิกกับเหตุการณ์ที่ปล่อยออกมาใน$rootScopeแต่ยังทำความสะอาดตัวจัดการโดยตรงเมื่อท้องถิ่น$scopeถูกทำลาย

วิธีที่สะอาดที่สุดในการติดตั้งลิง$rootScopeเพื่อให้$onRootScopeวิธีการดังกล่าวคือผ่านมัณฑนากร

เพื่อให้แน่ใจว่า$onRootScopeคุณสมบัติไม่ปรากฏขึ้นไม่คาดคิดเมื่อแจงมากกว่า$scopeที่เราใช้Object.defineProperty()และการตั้งค่าไปenumerable falseโปรดทราบว่าคุณอาจต้องการ ES5 shim

angular
    .module('MyApp')
    .config(['$provide', function($provide){
        $provide.decorator('$rootScope', ['$delegate', function($delegate){

            Object.defineProperty($delegate.constructor.prototype, '$onRootScope', {
                value: function(name, listener){
                    var unsubscribe = $delegate.$on(name, listener);
                    this.$on('$destroy', unsubscribe);

                    return unsubscribe;
                },
                enumerable: false
            });


            return $delegate;
        }]);
    }]);

ด้วยวิธีนี้ในสถานที่รหัสควบคุมจากด้านบนสามารถง่ายต่อการ:

angular
    .module('MyApp')
    .controller('MyController', ['$scope', function MyController($scope) {

            $scope.$onRootScope('someComponent.someCrazyEvent', function(){
                console.log('foo');
            });
        }
    ]);

เพื่อให้เป็นผลสุดท้ายของทั้งหมดนี้ผมขอแนะนำให้คุณใช้+$rootScope.$emit$scope.$onRootScope

Btw ฉันพยายามโน้มน้าวให้ทีมงานเชิงมุมเพื่อแก้ไขปัญหาภายในแกนเชิงมุม มีการสนทนาเกิดขึ้นที่นี่: https://github.com/angular/angular.js/issues/4574

นี่คือ jsperf ที่แสดงให้เห็นว่าผลกระทบที่สมบูรณ์แบบ$broadcastนำมาสู่ตารางในสถานการณ์ที่ดีเพียง 100 $scopeเท่านั้น

http://jsperf.com/rootscope-emit-vs-rootscope-broadcast

ผลลัพธ์ jsperf


ฉันพยายามที่จะทำตัวเลือกที่ 2 ของคุณ แต่ฉันได้รับข้อผิดพลาด: Uncaught TypeError: ไม่สามารถกำหนดคุณสมบัติใหม่: $ onRootScope ขวาที่ฉันกำลังทำ Object.defineProperty ....
สกอต

บางทีฉันอาจจะเมาบางอย่างเมื่อฉันวางที่นี่ ฉันใช้มันในการผลิตและใช้งานได้ดี ฉันจะดูพรุ่งนี้ :)
Christoph

@Scott ฉันวางมันแล้ว แต่รหัสถูกต้องแล้วและเป็นสิ่งที่เราใช้ในการผลิต คุณช่วยตรวจสอบอีกครั้งว่าคุณไม่มีการพิมพ์ผิดในเว็บไซต์ของคุณ? ฉันสามารถดูรหัสของคุณที่อื่นเพื่อช่วยแก้ไขปัญหาได้หรือไม่
Christoph

@Christoph มีวิธีที่ดีในการทำมัณฑนากรใน IE8 เพราะมันไม่รองรับ Object.defineProperty บนวัตถุที่ไม่ใช่ DOM หรือไม่?
joshschreuder

59
นี่เป็นวิธีแก้ปัญหาที่ฉลาดมาก แต่ไม่จำเป็นอีกต่อไป รุ่นล่าสุดของ Angular (1.2.16) และอาจเร็วกว่านี้ได้แก้ไขปัญหานี้แล้ว ตอนนี้การออกอากาศ $ จะไม่ไปที่ตัวควบคุมผู้สืบทอดทุกคนโดยไม่มีเหตุผล มันจะเยี่ยมชมผู้ที่กำลังฟังเหตุการณ์เท่านั้น ฉันอัปเดต jsperf ที่อ้างถึงด้านบนเพื่อแสดงให้เห็นว่าปัญหาได้รับการแก้ไขแล้ว: jsperf.com/rootscope-emit-vs-rootscope-broadcast/27
zumalifeguard

107

คำตอบด้านบนนี่เป็นงานรอบ ๆ จากปัญหาเชิงมุมซึ่งไม่มีอยู่แล้ว (อย่างน้อยในรุ่น> 1.2.16 และ "อาจจะก่อนหน้านี้") ในฐานะ @zumalifeguard ได้กล่าวถึง แต่ฉันไม่ได้อ่านคำตอบทั้งหมดเหล่านี้โดยไม่มีวิธีแก้ปัญหาจริง

สำหรับฉันแล้วดูเหมือนว่าคำตอบควรจะเป็น

  • ใช้$broadcastจาก$rootScope
  • ฟังการใช้งาน$on จากท้องถิ่น$scopeที่จำเป็นต้องรู้เกี่ยวกับเหตุการณ์

ดังนั้นเพื่อเผยแพร่

// EXAMPLE PUBLISHER
angular.module('test').controller('CtrlPublish', ['$rootScope', '$scope',
function ($rootScope, $scope) {

  $rootScope.$broadcast('topic', 'message');

}]);

และสมัครสมาชิก

// EXAMPLE SUBSCRIBER
angular.module('test').controller('ctrlSubscribe', ['$scope',
function ($scope) {

  $scope.$on('topic', function (event, arg) { 
    $scope.receiver = 'got your ' + arg;
  });

}]);

Plunkers

หากท่านลงทะเบียนผู้ฟังในท้องถิ่น$scopeก็จะถูกทำลายโดยอัตโนมัติด้วย$destroyตัวเองเมื่อตัวควบคุมที่เกี่ยวข้องจะถูกลบออก


1
คุณรู้หรือไม่ว่ารูปแบบเดียวกันนี้สามารถใช้กับcontrollerAsไวยากรณ์ได้หรือไม่ ฉันสามารถใช้$rootScopeในสมาชิกเพื่อฟังเหตุการณ์ แต่ฉันแค่อยากรู้ว่ามีรูปแบบที่แตกต่างกัน
edhedges

3
@ edhedges ฉันเดาว่าคุณสามารถฉีดได้$scopeอย่างชัดเจน John Papa เขียนเกี่ยวกับเหตุการณ์ที่เกิดขึ้นว่าเป็น "ข้อยกเว้น" หนึ่งเดียวกับกฎการรักษา$scope"ออก" ของผู้ควบคุม (ฉันใช้เครื่องหมายคำพูดเพราะในขณะที่เขาพูดถึงController Asยังมีอยู่$scopeมันอยู่ใต้ฝากระโปรง)
poshest

ภายใต้ฝากระโปรงคุณหมายความว่าคุณยังสามารถรับมันผ่านการฉีด?
edhedges

2
@ edhedges ฉันได้อัปเดตคำตอบของฉันด้วยcontroller asทางเลือกไวยากรณ์ตามที่ร้องขอ ฉันหวังว่านั่นคือสิ่งที่คุณหมายถึง
poshest

3
@dsdsdsdsd, บริการ / โรงงาน / ผู้ให้บริการจะอยู่ที่นี่ตลอดไป มีหนึ่งและหนึ่งเดียวเท่านั้น (Singletons) ในแอปเชิงมุม ในทางกลับกันตัวควบคุมจะเชื่อมโยงกับการทำงาน: ส่วนประกอบ / คำสั่ง / ng-controller ซึ่งสามารถทำซ้ำได้ (เช่นวัตถุที่ทำจากคลาส) และพวกเขามาและไปตามความจำเป็น เหตุใดคุณจึงต้องการให้ตัวควบคุมและตัวควบคุมนั้นคงอยู่เมื่อคุณไม่ต้องการมันอีกต่อไป? นั่นคือคำจำกัดความของหน่วยความจำรั่ว
poshest

54

ใช้$ rootScope $ broadcastและ $ scope $ $ สำหรับการสื่อสาร PubSub

นอกจากนี้ดูโพสต์นี้: AngularJS - การสื่อสารระหว่างตัวควบคุม


3
วิดีโอที่เพียง reinvents และ$rootScope $watchไม่แน่ใจว่าเป็นการปรับปรุงใด ๆ
nilskp

42

เนื่องจาก defineProperty มีปัญหาความเข้ากันได้ของเบราว์เซอร์ฉันคิดว่าเราสามารถคิดถึงการใช้บริการได้

angular.module('myservice', [], function($provide) {
    $provide.factory('msgBus', ['$rootScope', function($rootScope) {
        var msgBus = {};
        msgBus.emitMsg = function(msg) {
        $rootScope.$emit(msg);
        };
        msgBus.onMsg = function(msg, scope, func) {
            var unbind = $rootScope.$on(msg, func);
            scope.$on('$destroy', unbind);
        };
        return msgBus;
    }]);
});

และใช้มันในคอนโทรลเลอร์เช่นนี้:

  • ตัวควบคุม 1

    function($scope, msgBus) {
        $scope.sendmsg = function() {
            msgBus.emitMsg('somemsg')
        }
    }
  • ตัวควบคุม 2

    function($scope, msgBus) {
        msgBus.onMsg('somemsg', $scope, function() {
            // your logic
        });
    }

7
+1 สำหรับการยกเลิกการสมัครอัตโนมัติเมื่อขอบเขตถูกทำลาย
Federico Nafria

6
ฉันชอบวิธีนี้ 2 การเปลี่ยนแปลงที่ฉันทำ: (1) อนุญาตให้ผู้ใช้ส่งผ่าน 'ข้อมูล' ไปยังข้อความส่งเสียง (2) กำหนดให้การส่งผ่าน 'ขอบเขต' เป็นตัวเลือกเพื่อให้สามารถใช้บริการเดี่ยวและตัวควบคุมได้ คุณสามารถดูการเปลี่ยนแปลงเหล่านี้ได้ที่นี่: gist.github.com/turtlemonvh/10686980/…
turtlemonvh


15

ที่จริงแล้วการใช้ emit และ broadcast นั้นไม่มีประสิทธิภาพเพราะเหตุการณ์ที่เกิดขึ้นนั้นทำให้ลำดับขั้นของขอบเขตนั้นลดลงอย่างง่ายดายซึ่งสามารถลดระดับลงในขวดประสิทธิภาพสำหรับแอพพลิเคชั่นที่ซับซ้อนได้อย่างง่ายดาย

ฉันอยากจะแนะนำให้ใช้บริการ นี่คือวิธีที่ฉันเพิ่งดำเนินการในหนึ่งในโครงการของฉัน - https://gist.github.com/3384419

แนวคิดพื้นฐาน - ลงทะเบียน pubsub / บัสเหตุการณ์เป็นบริการ จากนั้นฉีดเหตุการณ์นั้นที่คุณต้องการสมัครสมาชิกหรือเผยแพร่เหตุการณ์ / หัวข้อ


7
และเมื่อไม่ต้องการคอนโทรลเลอร์อีกต่อไปคุณจะยกเลิกการสมัครอัตโนมัติได้อย่างไร? หากคุณไม่ทำเช่นนี้เนื่องจากการปิดคอนโทรลเลอร์จะไม่ถูกลบออกจากหน่วยความจำและคุณจะยังคงตรวจจับข้อความได้ เพื่อหลีกเลี่ยงปัญหานี้คุณจะต้องลบด้วยตนเอง การใช้ $ กับสิ่งนี้จะไม่เกิดขึ้น
Renan Tomal Fernandes

1
นั่นเป็นประเด็นที่ยุติธรรม ฉันคิดว่ามันสามารถแก้ไขได้โดยวิธีที่คุณออกแบบใบสมัครของคุณ ในกรณีของฉันฉันมีแอพพลิเคชั่นหน้าเดียวดังนั้นมันจึงเป็นปัญหาที่น่ากังวลมากขึ้น ต้องบอกว่าฉันคิดว่ามันจะสะอาดกว่านี้ถ้ามุมมีวงจรชีวิตของส่วนประกอบที่คุณสามารถเดินสาย / ทำอะไรแบบนี้
numan salati

6
ฉันเพิ่งออกจากที่นี่เพราะไม่มีใครบอกมาก่อน ใช้ rootScope เป็น EventBus เป็นไม่ได้ไม่มีประสิทธิภาพเป็น$rootScope.$emit()เพียงฟองอากาศขึ้น อย่างไรก็ตามเนื่องจากไม่มีขอบเขตด้านบน$rootScopeจึงไม่มีอะไรน่ากลัว ดังนั้นหากคุณเพิ่งใช้$rootScope.$emit()และ$rootScope.$on()คุณจะมี EventBus ทั้งระบบที่รวดเร็ว
Christoph

1
สิ่งเดียวที่คุณต้องระวังคือถ้าคุณใช้$rootScope.$on()ภายในคอนโทรลเลอร์ของคุณคุณจะต้องล้างเหตุการณ์ที่มีผลผูกพันมิฉะนั้นพวกเขาจะสรุปเมื่อมันสร้างขึ้นใหม่ทุกครั้งที่คอนโทรลเลอร์มีอินสแตนซ์และพวกเขาไม่ ถูกทำลายโดยอัตโนมัติเพื่อคุณเนื่องจากคุณผูกพัน$rootScopeโดยตรง
Christoph

รุ่นล่าสุดของ Angular (1.2.16) และอาจเร็วกว่านี้ได้แก้ไขปัญหานี้แล้ว ตอนนี้การออกอากาศ $ จะไม่ไปที่ตัวควบคุมผู้สืบทอดทุกคนโดยไม่มีเหตุผล มันจะเยี่ยมชมผู้ที่กำลังฟังเหตุการณ์เท่านั้น ฉันอัปเดต jsperf ที่อ้างถึงด้านบนเพื่อแสดงให้เห็นว่าปัญหาได้รับการแก้ไขแล้ว: jsperf.com/rootscope-emit-vs-rootscope-broadcast/27
zumalifeguard

14

การใช้วิธีรับและตั้งค่าภายในบริการคุณสามารถส่งข้อความระหว่างตัวควบคุมได้ง่าย

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

myApp.factory('myFactoryService',function(){


    var data="";

    return{
        setData:function(str){
            data = str;
        },

        getData:function(){
            return data;
        }
    }


})


myApp.controller('FirstController',function($scope,myFactoryService){
    myFactoryService.setData("Im am set in first controller");
});



myApp.controller('SecondController',function($scope,myFactoryService){
    $scope.rslt = myFactoryService.getData();
});

ใน HTML HTML คุณสามารถตรวจสอบได้เช่นนี้

<div ng-controller='FirstController'>  
</div>

<div ng-controller='SecondController'>
    {{rslt}}
</div>

+1 หนึ่งในวิธีการที่ชัดเจนเมื่อคุณได้รับการบอกว่ายอดเยี่ยม! ฉันใช้เวอร์ชันทั่วไปมากขึ้นด้วยวิธี set (key, value) และ get (key) ซึ่งเป็นทางเลือกที่มีประโยชน์สำหรับการออกอากาศ $
TonyWilk

8

เกี่ยวกับรหัสต้นฉบับ - ปรากฏว่าคุณต้องการแบ่งปันข้อมูลระหว่างขอบเขต ในการแบ่งปันข้อมูลหรือสถานะระหว่าง $ scope เอกสารแนะนำให้ใช้บริการ:

  • ในการรันรหัสไร้สัญชาติหรือสภาวะที่ใช้ร่วมกันระหว่างคอนโทรลเลอร์ - ใช้บริการเชิงมุมแทน
  • เพื่อยกตัวอย่างหรือจัดการวงจรชีวิตของส่วนประกอบอื่น ๆ (ตัวอย่างเช่นเพื่อสร้างอินสแตนซ์ของบริการ)

อ้างอิง: ลิงค์เอกสารเชิงมุมที่นี่


5

จริงๆแล้วฉันเริ่มใช้ Postal.js เป็นบัสข้อความระหว่างตัวควบคุม

มีประโยชน์มากมายเช่นบัสข้อความเช่นการผูกสไตล์ AMQP วิธีการไปรษณีย์สามารถรวม w / iFrames และซ็อกเก็ตเว็บและสิ่งอื่น ๆ อีกมากมาย

ฉันใช้มัณฑนากรเพื่อตั้งค่าไปรษณีย์บน$scope.$bus...

angular.module('MyApp')  
.config(function ($provide) {
    $provide.decorator('$rootScope', ['$delegate', function ($delegate) {
        Object.defineProperty($delegate.constructor.prototype, '$bus', {
            get: function() {
                var self = this;

                return {
                    subscribe: function() {
                        var sub = postal.subscribe.apply(postal, arguments);

                        self.$on('$destroy',
                        function() {
                            sub.unsubscribe();
                        });
                    },
                    channel: postal.channel,
                    publish: postal.publish
                };
            },
            enumerable: false
        });

        return $delegate;
    }]);
});

นี่คือลิงค์ไปยังโพสต์บล็อกในหัวข้อ ...
http://jonathancreamer.com/an-angular-event-bus-with-postal-js/


3

นี่คือวิธีที่ผมทำมันด้วยโรงงาน / บริการที่ง่ายและพึ่งพาการฉีด (DI)

myApp = angular.module('myApp', [])

# PeopleService holds the "data".
angular.module('myApp').factory 'PeopleService', ()->
  [
    {name: "Jack"}
  ]

# Controller where PeopleService is injected
angular.module('myApp').controller 'PersonFormCtrl', ['$scope','PeopleService', ($scope, PeopleService)->
  $scope.people = PeopleService
  $scope.person = {} 

  $scope.add = (person)->
    # Simply push some data to service
    PeopleService.push angular.copy(person)
]

# ... and again consume it in another controller somewhere...
angular.module('myApp').controller 'PeopleListCtrl', ['$scope','PeopleService', ($scope, PeopleService)->
  $scope.people = PeopleService
]

1
คอนโทรลเลอร์สองตัวของคุณไม่สื่อสารพวกเขาจะใช้บริการเดียวกันเท่านั้น นั่นไม่ใช่สิ่งเดียวกัน
เกร็ก

@Greg คุณสามารถทำสิ่งเดียวกันได้โดยใช้รหัสน้อยลงด้วยการใช้บริการร่วมกันและเพิ่ม $ watches ตามที่ต้องการ
Capaj

3

ฉันชอบวิธีที่$rootscope.emitใช้เพื่อให้เกิดการสื่อสารถึงกัน ฉันขอแนะนำวิธีที่มีประสิทธิภาพที่สะอาดและมีประสิทธิภาพโดยไม่สร้างมลภาวะให้กับพื้นที่โลก

module.factory("eventBus",function (){
    var obj = {};
    obj.handlers = {};
    obj.registerEvent = function (eventName,handler){
        if(typeof this.handlers[eventName] == 'undefined'){
        this.handlers[eventName] = [];  
    }       
    this.handlers[eventName].push(handler);
    }
    obj.fireEvent = function (eventName,objData){
       if(this.handlers[eventName]){
           for(var i=0;i<this.handlers[eventName].length;i++){
                this.handlers[eventName][i](objData);
           }

       }
    }
    return obj;
})

//Usage:

//In controller 1 write:
eventBus.registerEvent('fakeEvent',handler)
function handler(data){
      alert(data);
}

//In controller 2 write:
eventBus.fireEvent('fakeEvent','fakeData');

สำหรับหน่วยความจำรั่วคุณควรเพิ่มวิธีพิเศษในการยกเลิกการลงทะเบียนจากตัวรับฟังเหตุการณ์ ตัวอย่างเล็ก ๆ น้อย ๆ ที่ดีอยู่แล้ว
Raffaeu

2

นี่คือวิธีที่รวดเร็วและสกปรก

// Add $injector as a parameter for your controller

function myAngularController($scope,$injector){

    $scope.sendorders = function(){

       // now you can use $injector to get the 
       // handle of $rootScope and broadcast to all

       $injector.get('$rootScope').$broadcast('sinkallships');

    };

}

นี่คือฟังก์ชั่นตัวอย่างที่จะเพิ่มภายในตัวควบคุมพี่น้อง:

$scope.$on('sinkallships', function() {

    alert('Sink that ship!');                       

});

และแน่นอนนี่คือ HTML ของคุณ:

<button ngclick="sendorders()">Sink Enemy Ships</button>

16
ทำไมคุณไม่เพียงแค่ฉีด$rootScope?
Pieter Herroelen

1

เริ่มเชิงมุม 1.5 และเป็นจุดเน้นการพัฒนาตามองค์ประกอบ วิธีที่แนะนำสำหรับส่วนประกอบในการโต้ตอบคือการใช้คุณสมบัติ 'ต้องการ' และผ่านการเชื่อมโยงคุณสมบัติ (อินพุต / เอาต์พุต)

องค์ประกอบจะต้องมีองค์ประกอบอื่น (ตัวอย่างเช่นองค์ประกอบราก) และรับการอ้างอิงถึงมันเป็นตัวควบคุม:

angular.module('app').component('book', {
    bindings: {},
    require: {api: '^app'},
    template: 'Product page of the book: ES6 - The Essentials',
    controller: controller
});

จากนั้นคุณสามารถใช้วิธีการขององค์ประกอบรากในองค์ประกอบลูกของคุณ:

$ctrl.api.addWatchedBook('ES6 - The Essentials');

นี่คือฟังก์ชั่นการควบคุมองค์ประกอบราก:

function addWatchedBook(bookName){

  booksWatched.push(bookName);

}

นี่คือภาพรวมทางสถาปัตยกรรมที่สมบูรณ์: การสื่อสารส่วนประกอบ


0

คุณสามารถเข้าถึงฟังก์ชั่นสวัสดีทุกที่ในโมดูล

ตัวควบคุมที่หนึ่ง

 $scope.save = function() {
    $scope.hello();
  }

ตัวควบคุมที่สอง

  $rootScope.hello = function() {
    console.log('hello');
  }

ข้อมูลเพิ่มเติมที่นี่


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

0

ฉันจะสร้างบริการและใช้การแจ้งเตือน

  1. สร้างวิธีการในบริการการแจ้งเตือน
  2. สร้างวิธีการทั่วไปเพื่อออกอากาศการแจ้งเตือนในบริการการแจ้งเตือน
  3. จากตัวควบคุมแหล่งที่มาเรียกการแจ้งเตือนบริการวิธี ฉันยังผ่านวัตถุที่สอดคล้องกันเพื่อคงอยู่ถ้าจำเป็น
  4. ภายในวิธีนี้ฉันยืนยันข้อมูลในบริการแจ้งเตือนและเรียกวิธีแจ้งเตือนทั่วไป
  5. ในตัวควบคุมปลายทางฉันฟัง ($ scope.on) สำหรับเหตุการณ์ออกอากาศและเข้าถึงข้อมูลจากบริการการแจ้งเตือน

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

หวังว่านี่จะช่วยได้


0

คุณสามารถใช้บริการ build-in ของ AngularJS $rootScopeและฉีดเซอร์วิสนี้ในคอนโทรลเลอร์ทั้งสองของคุณ จากนั้นคุณสามารถฟังเหตุการณ์ที่เกิดขึ้นกับวัตถุ $ rootScope

$ rootScope มีตัวกระจายเหตุการณ์สองเหตุการณ์ที่เรียกว่า$emit and $broadcastซึ่งมีหน้าที่รับผิดชอบในการส่งเหตุการณ์ (อาจเป็นเหตุการณ์ที่กำหนดเอง) และใช้$rootScope.$onฟังก์ชั่นเพื่อเพิ่มฟังเหตุการณ์


0

คุณควรใช้บริการเพราะ$rootscopeเข้าถึงได้จากแอปพลิเคชั่นทั้งหมดและเพิ่มการโหลดหรือคุณใช้ rootparams หากข้อมูลของคุณไม่มาก


0
function mySrvc() {
  var callback = function() {

  }
  return {
    onSaveClick: function(fn) {
      callback = fn;
    },
    fireSaveClick: function(data) {
      callback(data);
    }
  }
}

function controllerA($scope, mySrvc) {
  mySrvc.onSaveClick(function(data) {
    console.log(data)
  })
}

function controllerB($scope, mySrvc) {
  mySrvc.fireSaveClick(data);
}

0

คุณสามารถทำได้โดยใช้เหตุการณ์เชิงมุมที่เป็น $ emit และ $ broadcast ตามความรู้ของเรานี่เป็นวิธีที่ดีที่สุดมีประสิทธิภาพและประสิทธิผล

ก่อนอื่นเราเรียกฟังก์ชั่นจากคอนโทรลเลอร์หนึ่งตัว

var myApp = angular.module('sample', []);
myApp.controller('firstCtrl', function($scope) {
    $scope.sum = function() {
        $scope.$emit('sumTwoNumber', [1, 2]);
    };
});
myApp.controller('secondCtrl', function($scope) {
    $scope.$on('sumTwoNumber', function(e, data) {
        var sum = 0;
        for (var a = 0; a < data.length; a++) {
            sum = sum + data[a];
        }
        console.log('event working', sum);

    });
});

คุณยังสามารถใช้ $ rootScope แทน $ scope ใช้คอนโทรลเลอร์ของคุณตามนั้น

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