What are the best methods for capturing individual and time-sensitive occurrences within a service?

I am currently working on structuring the events within a service to enable a mechanism for subscribing/unsubscribing listeners when a controller's scope is terminated. Previously, I utilized $rootScope.$on in this manner:

if(!$rootScope.$$listeners['event']) {
    $rootScope.$on('event', function(ev, data){
        // perform some actions...
    });
}

or

$scope.$on('$destroy', function(ev, data){
    // unsubscribe the listener
});

To ensure that only one listener exists for each event, it is crucial to remove the listener when the corresponding controller is no longer active; otherwise, the registered function would still be executed.

Therefore, I aim to introduce a $destroy event listener within my controller to eliminate the listener upon scope destruction without repeating this process for every event creation. This motivates me to create a specialized service for encapsulating these events.

angular.module('core').factory('event', [
    function() {
        var service = {};
        service.events = {};
        service.on = function(scope, eventId, callback) {
            scope.$on('$destroy', function(ev, other){
                //unsubscribe
            });
            service.events[eventId] = callback;
            // scope = null; perhaps?
        };
        service.emit = function(eventId, data){
            if (service.events[eventId])
                service.events[eventId](data);
            else
                return new Error('The event is not subscribed');
        };
        return service;
    }
]);

While I could utilize $rootScope directly instead of implementing custom methods, enclosing $on and $emit from $rootScope would lead to similar drawbacks.

As such, I have the following inquiries:

  1. Is it advisable to include the scope reference in a service?
  2. What does $$destroyed signify? Does it indicate that AngularJS has released all internal references to the instance?
  3. Would assigning scope as null in my service facilitate garbage collection or does AngularJS handle object deletion explicitly?
  4. Are there more effective strategies to achieve my objectives?

Answer №1

Your goal here is essentially to create an event hub. It's clear that you've identified the issues with the current setup and proposed a different approach.

One way to tackle this problem is by extending the $rootScope with your event bus. Here's how you can do it:

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

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

          self.$on('$destroy',
            function () {
              console.log("Unsubscribing...");
              sub.unsubscribe();

            });
        },

        publish: $$bus.publish
      };
    },
    enumerable: false
  });

  return $delegate;
}]);
});

Here's a basic implementation of the $$bus for reference:

app.factory('$$bus', function () {
  var api = {};
  var events = {};

  api.subscribe = function (event) {
    if (!events.hasOwnProperty(event.name)) {
      events[event.name] = [event];
    } else {
      events[event.name].push(event);
    }
    return {
      unsubscribe: function () {
        api.unsubscribe(event);
      }
    }
  };

  api.publish = function (eventName, data) {
    if (events.hasOwnProperty(eventName)) {
      console.log(eventName);

      angular.forEach(events[eventName], function (subscriber) {
        subscriber.callback.call(this, data);
      });
    }
  };

  api.unsubscribe = function (event) {
    if (events.hasOwnProperty(event.name)) {
      events[event.name].splice(events[event.name].indexOf(event), 1);
      if (events[event.name].length == 0) {
        delete events[event.name];
      }
    }
  };

  return api;
});

All you need to do now is subscribe or publish events, with automatic unsubscription when the $scope is destroyed:

$scope.$bus.subscribe({
  name: 'test', callback: function (data) {
    console.log(data);
  }
});

You can then publish an event later on:

$scope.$bus.publish('test', {name: "Publishing event!"});

It's worth noting that the events are subscribed to each individual $scope, not the $rootScope itself. This ensures that each $scope knows when to release.

This should address your query. You have the flexibility to enhance this mechanism further (e.g., releasing controller event listeners upon view routing, auto-unsubscribing from specific events only, etc.). Best of luck!

** The concept is derived from this source, which utilizes a different bus framework.

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

Utilizing Zoomdata data in conjunction with echarts index.js to create a dynamic stacked line chart

I am currently working on integrating Zoomdata with an echarts javascript chart to visualize data from 20 different computers in a stacked line chart format. While I can manually code this setup, I am looking for a way to dynamically link the data from Zoo ...

Adding JavaScript dependencies in Directives in AngularJS: A Step-by-Step Guide

When working with directives in AngularJS, I have encountered a challenge. I need to ensure that my JavaScript is loaded before calling the directive. Is there a way to directly inject JavaScript into AngularJS directives? Consider the following code snip ...

Error encountered when asynchronously iterating over an object in TypeScript

Could someone please help me understand why I am getting an error with this code? var promise = new Promise((resolve, reject) => { resolve([1, 2, 3, 4, 5]); }); async function doSomethingAsync() { var data = await promise; data.forEach(v = ...

What could be causing the Or operator to malfunction within the ng-pattern attribute in AngularJS?

Currently, I am implementing the ng-pattern="/^(([A-Za-z]{0,5}) | ([0-9]{0,10}))$/". However, it seems like the input control is not accepting values such as "asd" or "09", despite my expectation that both should be valid inputs. Do you think the pipe sy ...

Generating a client-side MD5 hash for an image file in order to compare it to the hash calculated by Firebase?

Is there a way to calculate the MD5 of an image file on the client side within my Angular Map application, that will match the MD5 when I store the file on Firestore? I need to verify that a user's file matches a reference version stored in Firebase ...

Unable to find custom components when using react-router

My goal is to improve the organization of my Routes in React and separate concerns. I am currently utilizing react-router-dom version 5. Within my Application Routes component, I have structured it with 3 children components: AuthenticatedRoutes PublicRo ...

What is the best way to access all sections of a JSON file containing nested objects within objects?

Here is an example of my JSON file structure: [{ "articles": [ { "1": { "sections": [ {"1": "Lots of stuff here."} ] } }, { "2": { "sections": [ {"1": "And some more text right here"} ] } } }] The c ...

Why is Socket.io functioning on localhost but fails to work once deployed to Heroku?

Sharing my socket server code: const io = require("socket.io")(8080, { cors: { // origin: "http://localhost:3000", origin: "https://mern-bubble.herokuapp.com", }, }); This is the client-side implementation: useEf ...

Working with JSON structure using Javascript

I successfully transformed an XML file into a JSON format, but now I need to manipulate the data in order to achieve a specific desired structure. Here is the Original format { "machine": "Hassia2", "actual_product_date": "08/24/2017", "holdi ...

Step-by-Step Guide on Dividing Table Row Data into Two Distinct Tables

At present, I have created a dynamic table that retrieves data from the database using forms in Django. I'm facing an issue with this table as even though there are 7 columns, only 2 of them are visible. However, all 5 hidden columns do contain impor ...

I encountered an error message while running the Angular JS code that I had written, and I am unable to find a solution to resolve it

Every time I attempt to run ng serve, the following error message pops up: "The serve command requires to be run in an Angular project, but a project definition could not be found." I've already experimented with various commands like: npm cache clean ...

Building a matrix-esque table using d3.js reminiscent of HTML tables

I would like to generate a table resembling a matrix without numerical values. 1. Here is an example of my database table: | CODE | STIL | SUBSTIL | PRODUS | |------|-------|----------|---------| | R | stil1 | substil1 | produs1 | | R | stil1 | s ...

Displaying Dates in an Express Application using EJS

I have a MySQL table with a column containing date data. I am building a webpage to display this data in a more user-friendly way. Currently, the displayed result looks like this: I would like the dates in the column to be formatted as something like ...

Triggering multiple onClick events in React / Material-UI when used within a data.map() loop

My English may not be perfect. {data.sort(getSorting(order, orderBy)) .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) .map(n => { {/*........*/} <Button onClick={this.handleLsClick}> Open Menu < ...

Unable to display returned data from an AJAX request made with jQuery autocomplete

After reviewing the debug developer tool, I noticed that the ajax request returned data but for some reason, the data is not being displayed in the text box. The data contains special characters which are visible in the provided image. I am trying to iden ...

"Utilizing jQuery's getJSON method in a session to fetch data

I have a code snippet that fetches the current number of likes on Facebook like this: $(document).ready(function() { $.getJSON('https://graph.facebook.com/<username>?callback=?', function(data) { var fb_count = data['likes ...

[entity: undefined prototype] { type: 'clip', info: 'Watch my latest video!!' } using nodejs - multer

import routes from "./routes"; import multer from "multer"; const multerVideo = multer({ dest: "videos/" }); export const localsMiddleware = (req, res, next) => { res.locals.siteName = "Webtube"; res.locals.routes = routes; res.locals.user = { isA ...

Why isn't the connect.use() function working in Node.js?

I have been studying and applying examples from a book to learn Node.js. While replicating one of the examples, which involved creating a middleware, I encountered an error when trying to run the JavaScript file. The error message stated "undefined is not ...

React array mapping issue does not produce any error message

I have exhaustively searched through every answer on Stack Overflow in hopes of finding a solution to my problem. However, I find myself at an impasse as there are no error messages appearing in the console. It seems that there is a hidden bug eluding my d ...

`Switching between two buttons: A guide`

Here is the codepin: https://jsfiddle.net/magicschoolbusdropout/dwbmo3aj/18/ I currently have a functionality in my code that allows me to toggle between two buttons. When I click on one button, content opens and then closes when clicked again. The same b ...