Updating an AngularJS directive following a service method invocation

My goal is to set up a reusable alert service that can be easily called from anywhere in my application with just:

alertService.showAlert('warning', 'something went wrong!');

One example of where I want to use this is after an AJAX call to a backend API. Currently, I am using a factory and a directive for this purpose. However, it seems like something is not working correctly because the directive does not update after calling the showAlert method. Here's a simplified version of what I have so far:

var srv = angular.module('srv', []);
srv.factory('alertService', ['$timeout', function($timeout){
    var alertService = this;
        alertService.alertNeeded = false;
        alertService.alertClass = '';
        alertService.alertMessage = '';
        alertService.setAlertNeeded = function(){
            alertService.alertNeeded = true
        };
        alertService.setAlertClass = function(type){
            if(type === 'warning')
                alertService.alertClass = 'alert-warning';
            if(type === 'success')
                alertService.alertClass = 'alert-success';
            if(type === 'info')
                alertService.alertClass = 'alert-info';
            if(type === 'danger')
                alertService.alertClass = 'alert-danger';
        };
        alertService.setAlertMessage = function(message){
            alertService.alertMessage = message;
        };

        return {
            showAlert: function(class, msg){
                alertService.setAlertNeeded();
                alertService.setAlertClass(class);
                alertService.setAlertMessage(msg);
            }
        };
 }]).
directive('myAlerts', ['alertService', function(alertService){
        return {
            restrict: 'A',
            template: '<div ng-class="alertClass" ng-show="alertNeeded">{{alertMessage}}</div>',
            link: function(scope){                   
                    scope.alertNeeded = alertService.alertNeeded;
                    scope.alertMessage = alertService.alertMessage;
                    scope.alertClass = alertService.alertClass;
            }
        }
    }]).
controller('alertShowingController', ['$scope', 'alertService', function($scope, alertService){
     alertService.showAlert('warning', 'Warning alert!!!')   
 }]);

Although my code is slightly different, the concept remains the same - I want to trigger alertService.showAlert(...) from another controller in a different module (which depends on the srv module) to update the variables in the myAlerts directive and display the correct alert.

The issue I'm facing is that after calling the showAlert method, the values are set properly, but within the directive code, I'm receiving alertService.alertNeeded as undefined.

I am new to AngularJS, so there might be something fundamental that I am missing. I've spent a lot of time trying to figure it out, but I haven't found a solution yet.

Please provide some guidance!

Answer №1

Here's a pattern I've utilized in the past:

var srv = angular.module('srv', []);
srv.factory('alertService', ['$timeout', function($timeout){
    var alertListeners = [];

    this.register = function (listener) {
        alertListeners.push(listener);
    };

    this.notifyAll = function (data) {
        for (// each listener in array) {
            var listenerObject = alertListeners[i];
            try { // do not allow exceptions in individual listeners to corrupt other listener processing
                listenerObject.notify(data);
            } catch(e) {
                    console.log(e);
            }   
        }
    };
 }]).
 directive('myAlerts', ['alertService', function(alertService){

     var alertDirectiveObserver = function($scope, alertService) {

         this.notify = function(data) {
            /*
             * TO DO - utilize data to display alert
             */
         };

         alertService.register(this);
     };


   return {
     restrict: 'A',
     template: '<div ng-class="alertClass" ng-show="alertNeeded">{{alertMessage}}</div>',
     controller: ['$scope', 'alertService', alertDirectiveObserver],
     link: function(scope){  
     }
    }
}]).
controller('alertShowingController', ['$scope', 'alertService',   function($scope, alertService){
    alertService.notifyAll({'warning', 'Warning alert!!!'})   
 ]);

Remember to also clean up by registering a function to remove objects on scope destroy.

For example:

element.on('$destroy', function() {
    alertService.unregister(// some listener id);
});

Answer №2

Your code has a mix-up with the alertService variable, as it has different meanings within the factory definition and outside of it. To resolve this issue, you can add some missing methods to the object returned by the factory:

return {
    showAlert: function(cssClass, msg){
        alertService.setAlertNeeded();
        alertService.setAlertClass(cssClass);
        alertService.setAlertMessage(msg);
    },

    alertClass: function() { return alertService.alertClass; },     
    alertMessage: function() { return alertService.alertMessage; },
    alertNeeded: function() { return alertService.alertNeeded; }
};

After updating the object's methods, you will need to adjust your directive's template to utilize these functions on each digest cycle:

directive('myAlerts', ['alertService', function(alertService){
        return {
            restrict: 'A',
            template: '<div ng-class="alertClass()"' +
                      '     ng-show="alertNeeded()">' +
                      '  {{alertMessage()}}' +
                      '</div>',
            link: function(scope){                   
                scope.alertNeeded = alertService.alertNeeded;
                scope.alertMessage = alertService.alertMessage;
                scope.alertClass = alertService.alertClass;
            }
        }
}])

By making these adjustments, you should be able to display your warning message correctly. Give it a try in a fiddle.

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

What steps can I take to make my animation work in the opposite direction as well?

I'm currently working with an angular slider that is set to TRUE/OPEN by default. The issue I am facing is that while I am able to slide it using angular animations in one direction, I am unable to see the transition when sliding it back. Any assistan ...

Angular.js loads, processes, and presents a dynamic template that is fetched through an $resource from a REST

My Angular application requires customizable reporting functionality. The goal is to permit users to select from a variety of available reports, with a backend REST API providing both the template and data in JSON format for user customization. The app wi ...

Ensuring the Persistence of Column State in Material-UI DataGrid/DataGridPro when Adjusting Visibility Using Column Toolbar

We have integrated MUI DataGrid into our React project. Currently, I am exploring options to save the state of columns after toggling their visibility using the DataGrid toolbar column menu. After each re-render, the column setup returns to its default st ...

Exploring the concept of reflection in JavaScript and jQuery

Here is a javascript object I have: person.Name = "John"; person.Nick = "Smith"; person.Info = "hi there"; In addition, I have some HTML elements as shown below: <input id="Name" /> <input id="Nick" /> <input id="Info" /> My question ...

Steps to invoking a custom-named function

I am currently exploring how to call a specific function name in WebApi. When calling the WebApi with the structure api/{controllerName}, it works (as long as the function name starts with "get"). My goal is to achieve the following: Using JavaScript ...

Guide on changing the order of Vue sibling components when rendering a shared array within a parent component list

Currently facing a unique challenge and seeking input: Within the 'App', utilize 'TestListItem' for odd item indexes and 'TestListBetterItem' for even indexes. The same index must be used for both components. Initial attemp ...

Generate a fresh array using the data from the existing array object

I have nested objects within arrays that I need to manipulate. { "page": [ { "num": "1", "comp": [ { "foo": "bar", "bar": "foo", ...

What significance does the symbol "'" hold in the ng-style attribute?

Can someone explain the purpose of the " \' " in this code snippet? <div ng-style="{ \'cursor\': row.cursor }" Is there a reason why it can't be written simply as <div ng-style="{ cursor: row.cursor }" Here is ...

Navigating to a particular value in Vue: A step-by-step guide

In my Vue application, I have a basic table structure: <tbody> <tr v-for="(item, index) in items"> <td> ... </td> </tr> </tbody> The items are dynamically added using the unsh ...

What is the process of using JavaScript code to read a text file?

Trying to use Google Charts while reading data from a text file. The code in JS is written for this purpose: function readTextFile(file){ var rawFile = new XMLHttpRequest(); rawFile.open("GET", file, false); // using synchronous call var allTe ...

Guide for registering to modifications of a single key within a form group in Angular 2

I am facing an issue with my userForm group that consists of keys name, email, and phone. I have implemented an onValueChanged function to validate these fields whenever they are changed. However, the validation runs every time even if the field has not ...

Does the use of 'window.location.replace' in Chrome cause a session to be destroyed in PHP when redirecting to a specified page?

Apologies if the question seems a bit convoluted, but let me explain the scenario. Here is an example of an Ajax function I am working with: $.ajax({ url:"../controllers/xx_register.php", type:"POST", data:send, success: function(resp ...

Initiate a JavaScript confirmation dialog using server-side logic in ASP.NET

Does anyone know how to troubleshoot the issue with this code? I am trying to make it so that when btnOne is clicked and a is true, a confirm box pops up. If yes is selected, then it should execute the btnTwo method. Currently, the popup isn't appeari ...

Getting data from an API with authorization in Next.js using axios - a step-by-step guide

click here to view the image user = Cookies.get("user") I'm having trouble accessing this pathway. I stored user data, including the token, using cookies. Please assist me with this issue. ...

AngularJS: The blend of bo-bind, bindonce, and the translate filter

I am currently working with angular 1.2.25, angular-translate 2.0.1, angular-translate-loader-static-files 2.0.0, and angular-bindonce 0.3.1. My goal is to translate a static key using bindonce. Here is the code snippet I have: <div bindonce> < ...

Steps to deactivate a textarea in angularjs based on a selected option's value meeting a specific condition

Hello, I am a newcomer to AngularJS and I am looking to disable the textarea when a specific option is selected from a dropdown menu. I attempted to use ng-disabled="(here my ng-model value)" but unfortunately, it did not work as expected. If creating a ...

Steps for choosing an option in MUI select list using jest

I am looking for a way to automate tests for a Material UI select component using Jest. Is there a way to select an option from the list of options in the Material UI component and confirm that its value has been successfully populated in Jest? I have se ...

Angular parse:syntax ERROR - An unexpected issue has been encountered

I encountered an issue while attempting to retrieve data from a MySQL database using AngularJS. angular.js:13708 Error: [$parse:syntax] http://errors.angularjs.org/1.5.7/$parse/syntax?p0=Code&p1=is%20an%20unexpected%20token&p2=7&p3=x.Zip%20Cod ...

Issue with HTML 5 audio player not functioning correctly in Chrome browsers when trying to forward or rewind playback

I am encountering an issue with running the HTML5 audio player in Chrome. It works perfectly fine in IE 9+ and Firefox. I have implemented JavaScript functions for forwarding and rewinding the audio player on F7 and F8 key press. These functions work seaml ...

How can I restrict the navigation buttons in FullCalendar to only allow viewing the current month and the next one?

I recently downloaded the FullCalendar plugin from Is there a way to disable the previous button so that only the current month is visible? Also, how can I limit the next button so that only one upcoming month is shown? In my header, I included this code ...