Bidirectional communication between two AngularJS scopes or controllers utilizing a service

I am facing numerous situations where I require clicks or other interactions to trigger actions in a different area of the webpage (one-way communication). Now, I have encountered a need for bidirectional communication, where changes made in element A can affect specific attributes in the context behind element B and vice versa. Up until now, I have been using $rootScope.$broadcast to achieve this functionality, but it seems excessive and results in redundant code in both places:

$scope.$on('event-name', function(event, someArg) {
    if(someArg === $scope.someProperty) return;

    $scope.someProperty = someArg;
});

$scope.$watch('someProperty', function(newValue) {
    $rootScope.$broadcast('event-name', newValue);
});

Is there a more efficient solution? I would like to connect the two (or three, or N) scopes through a service, but I cannot find a way to do so without resorting to magical event names and repetitive code.

Answer №1

Although I have not personally tried this method, this answer provides a detailed explanation of how it can be implemented. You can also refer to this code snippet for a visual representation:

(function() {
    var mod = angular.module("App.services", []);

    //additional services go here...

    /* pubsub - sourced from https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js*/
    mod.factory('pubsub', function() {
        var cache = {};
        return {
            publish: function(topic, args) { 
                cache[topic] && $.each(cache[topic], function() {
                    this.apply(null, args || []);
                });
            },

            subscribe: function(topic, callback) {
                if(!cache[topic]) {
                    cache[topic] = [];
                }
                cache[topic].push(callback);
                return [topic, callback]; 
            },

            unsubscribe: function(handle) {
                var t = handle[0];
                cache[t] && d.each(cache[t], function(idx){
                    if(this == handle[1]){
                        cache[t].splice(idx, 1);
                    }
                });
            }
        }
    });


    return mod;
})();

It's important to note the potential memory leak that may occur if controllers are "deleted" without proper unsubscribing.

Answer №2

Consider giving this service a try:

'use strict';
angular.module('test')
  .service('messageBus', function($q) {
    var subs = {};
    var pendingQs = [];

    this.sub = function(name) {
      subs[name].reqDfr = $q.defer();
      return subs[name].reqDfr.promise;
    }

    this.unsub = function(name) {
      subs[name].reqDfr.resolve();
      subs[name].reqDfr = null;
    }

    function pub(name, data) {
      subs[name].reqDfr.notify(data);
    }

    this.req = function(name, code, details) {
      var dfrd = null;
      if (subs[name].reqDfr) {
        if (pendingQs[code]) {
          dfrd = pendingQuestions[code];
        } else {
          dfrd = $q.defer();
          pendingQs[code] = dfrd;
          pub(name, {
            code: code,
            details: details
          });
        }
      } else {
        dfrd = $q.defer();
        dfrd.resolve({
          code: "not subscribed"
        });
      }
      return dfrd.promise;
    }

    this.res = function(data) {
      var dfrd = pendingQs[data.code];
      if (dfrd) {
        dfrd.resolve(data);
      } else {
        handlePreemptiveNotifications(data);
      }
    }

    function handlePreemptiveNotifications() {
      switch (data.code) {
        
      }
    }
  });

This method can facilitate multi-controller communication as a message bus. It leverages the promises API in Angular for notification callbacks. All controllers involved should subscribe to the service like so:

angular.module('test')
  .controller('Controller1', function($scope, messageBus) {
    var name = "controller1";

    function load() {
      var subscr = messageBus.sub(name);
      subscr.then(null, null, function(data) {
        handleRequestFromService(data);
      });
    }

    function handleRequestFromService(data) {
      if (data.code == 1) {
        data.count = 10;
        messageBus.res(data);
      }
    }

    $scope.$on("$destroy", function(event) {
      messageBus.unsub(name);
    });

    load();
  });

angular.module('test')
  .controller('Controller2', function($scope, messageBus) {
    var name = "controller2";

    function load() {
      var subscr = messageBus.sub(name);
      subscr.then(null, null, function(data) {
        handleRequestFromService(data);
      });
    }

    function handleRequestFromService(data) {
      
    }

    $scope.getHorseCount = function() {
      var promise = messageBus.req("controller1", 1, {});
      promise.then(function(data) {
        console.log(data.count);
      });
    }

    $scope.$on("$destroy", function(event) {
      messageBus.unsub(name);
    });

    load();
  });

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to associate an object with a component in Angular2/TypeScript using HTTP

I am currently working on displaying a list of item names retrieved from a REST API. By utilizing the map function on the response observable and subscribing to it, I was able to obtain the parsed items object. Now, my challenge is how to bind this object ...

Modify the contents of the 'data-content' area in the popover

Looking for a solution where I can update only a portion of the data-content within a popover. Here is an example code snippet: <div class="popover-wrapper"> <i class="glyphicon glyphicon-question-sign hover_content" ...

"Learn how to create a scrolling div using a combination of CSS and JavaScript with absolute and relative

After relying solely on "pre-made" components like Mui or TailWind, I decided to create a component using only CSS and maybe JavaScript. However, I encountered some difficulties when attempting to position a div inside an image using relative and absolute ...

sending a variable from routes.js to my .ejs template

I need help figuring out how to display user information from my database in a template. var aboutUser = connection.query("SELECT about FROM users WHERE username = ?", req.user, function(err, rows) {});` I want to pass this data to the template like so: ...

Click event from Angular Material menu is triggered twice on mobile devices

After implementing a basic angular material side menu from the demo, I noticed that all click events are being fired twice on the entire page when viewed in mobile browsers. This issue can even be replicated in the Chrome emulator. (To reproduce, enable th ...

Are there ways to incorporate a variable within a Regular Expression in Javascript?

I've already checked the Mozilla website and W3schools, but I can't seem to find the solution. var modifyString = function (string1, string2) { if (string2.match(string1)) { string1 = new RegExp(string1); string2 = string2.replac ...

What's the reason why Angular stops flickering with the use of "/" in href?

Recently, I've come across a peculiar issue in one of my web applications while using Angular's routeProvider as a template engine. The app functions properly, but the behavior seems inexplicable to me. The problem arises during page transitions ...

Navigating in Angular with parameters without modifying the URL address

In a nutshell, my goal is to navigate to a page with parameters without showing them in the URL. I have two components: Component A and B. What I want to do is route to B while still needing some parameters from A. I know I can achieve this by setting a ...

Setting relative paths in Ui-Router: a beginner's guide

We are currently working on an angular application where all links are relative paths. Our framework of choice is Ui-Router. How can we effectively map a URL with multiple "tokens" while still maintaining the base URL? For instance, whether the state URL ...

What is the best way to fulfill a promise after a CSV file has finished being read?

I have been utilizing the 'fast-csv' module that can be found at this link (https://www.npmjs.org/package/fast-csv), but I am open to switching to a different one. I attempted promised-csv (https://www.npmjs.org/package/promised-csv) but I had tr ...

Having trouble getting the HTML5 Video to play using the AngularJS ng-src tag?

There seems to be an issue with AngularJS ng-src not working properly with the HTML5 Video element in this specific fiddle: http://jsfiddle.net/FsHah/5/ Upon inspecting the video element, it appears that the src tag is correctly filled with the appropriat ...

Working with arrays and data to populate tables and cross tables using a single Eloquent Model in Vue and Laravel

There are three tables in the database: species, panel, and a cross table called species_panel. The relationship between them is that one panel can contain multiple species, so a one-to-many relationship is used. Data needs to be inserted into the panel ta ...

Angular/JS - setTimeout function is triggered only upon the occurrence of a mouse click

I'm currently working on a website that calculates the shortest path between two points in a grid utilizing AngularJS. Although I have already implemented the algorithm and can display the colored path, I am facing an issue where the color changes ti ...

`Datatables sorting feature for British date and time`

I'm currently attempting to organize a column in a table using the DataTables plugin that contains UK date and time formats such as: 21/09/2013 11:15 Implementing Ronan Guilloux's code: jQuery.extend( jQuery.fn.dataTableExt.oSort, { "uk_dat ...

The dilemma of selecting objects in Three.js

Currently, I am developing a small web application that utilizes three.js. However, I have encountered an issue: I created a prototype with my three.js content and everything functions correctly (the canvas size in the prototype matches the browser window ...

Unable to connect to server using React-Native fetch. Localhost is not being used

I recently encountered an issue with my app where the fetch function used for user authentication stopped working. Despite not making any changes, the transition from React 0.27 to 0.28 seemed to have caused this problem. After scouring through numerous S ...

Authenticating the identity of the client application - the client is currently within the browser

I have a PHP backend (although it's not really important) and a Javascript client that runs in the browser. Here is how the application operates: The user accesses a URL and receives raw templates for rendering data An Ajax GET query is sent to the ...

Build a dynamic table by leveraging JSON with the power of JavaScript and HTML

I'm currently working on creating a table where information is stored and updated (not appended) in a JSON file using JavaScript and HTML. The structure of my JSON data looks like this: [{ "A": { "name": "MAK", "height": 170, ...

Unable to reference a property or method in Vue.js and Vuetify due to an issue with the <v-btn-toggle> button causing an error

Just started using vuetify and exploring the <v-btn-toggle> component for the first time. I'm trying to implement a toggle feature to filter my user list based on employee, manager, or admin types... Check out a snippet of my code below: <v ...

AngularJS - seamless navigation to url with hash symbol

Currently, I am in the process of developing a web application using a combination of AngularJS and Laravel. Everything seems to be working fine when I navigate through the URLs and links within the app. However, an issue arises when I try to directly inp ...