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

Comparing two Objects in JavaScript results in automatic updates for the second Object when changes are made to the first

Can someone please assist me with a hash map issue I'm encountering in my for loop? When resetting the second object, it unintentionally alters the Map values of the previous Key in the Hash Map. Any guidance on how to prevent this behavior would be g ...

Steps for clearing input field with type=date in Protractor:

I am currently working with protractor version 4.0.4 and I'm encountering an issue where I cannot clear a date input field. It seems like Chrome is introducing some extra controls that are causing interference. You can find further details about Chro ...

Adding array elements to a JavaScript object

I find myself in a rather unique predicament that I'm struggling to navigate. I have come across some data structured as follows. Please bear with me if I use any incorrect terminology, as I am relatively new to this. usersByName: { "tester&q ...

What strategies can be utilized to condense code when needing to adjust a className based on various props?

I am looking to condense this code, particularly the [if~else if] block, in order to dynamically change a className based on different props passed. export default function Button(props) { const { name, height, color, bgColor } = props; let className = ...

Ways to retrieve the path of a button found within table cells

https://i.stack.imgur.com/pUYHZ.jpgI'm currently working on a table where I've created a button that's being used in various rows and tables based on certain conditions. There's a specific scenario where I need to display the button for ...

Getting the chosen option from a dropdown list mapped in ReactJS

I am working on a dropdown select option that is linked to the data of an array object called 'template_titles'. Currently, the value in the dropdown corresponds to the title in the object. My goal is to be able to extract and use the selected va ...

Code in Javascript that performs a check for pre-order traversal

I’m interested in writing a preorder traversal code in Java for JavaScript. I’ve been practicing this question on geeksforgeeks: Check if a given array can represent Preorder Traversal of Binary Search Tree This is the algorithm they provided: 1) Cr ...

Combine table cells to improve readability on smaller screens

I currently have a setup designed for larger screens: <table> <thead> <tr> <th>title and image</th> <th>description</th> </tr> </thead> <tbody> ...

Whenever I click on a button, I want to modify the background color of my react-modal

Having an array of images with clickable overlays and a modal that changes the background color based on the overlay name when clicked is the goal. However, passing this value as a prop proves challenging since the images are generated through a function a ...

Issue: ReactJS + MaterialUI + TypeScript - Property 'component' does not exist?Possible error in ReactJS: component property is

Currently, I am designing my own custom typography. However, I have encountered an error while implementing it. I have been referring to the following documentation: https://mui.com/material-ui/api/typography/ Here is the code snippet that I am using: h ...

Having trouble with CSS transitions in a Next.js or Tailwind application?

"use client"; import React, { useState } from "react"; import Image from "next/image"; import Link from "next/link"; const NavigationBar = () => ( <div id="navbar"> <Link href="/">Home</Link> <Link href="/about">About& ...

I'm looking for a method to retrieve the value of an option tag and then pass it to a helper within handlebars. Can someone

Currently, I am utilizing express and handlebars in my project. I have a specific view where I aim to display certain information based on the event when a client selects an option from a select tag. However, I have some queries regarding this: Is it fea ...

The process of initializing the Angular "Hello World" app

Beginning with a simple "hello world" Angular app was going smoothly until I attempted to add the controller. Unfortunately, at that point, the expression stopped working on the page. <!doctype html> <html ng-app='app'> <head> ...

extracting ng-repeat values and passing them to the controller

I have created a form that dynamically increases whenever the user clicks on the add sale button Here is the HTML code: <fieldset data-ng-repeat="choice in choices"> <div class="form-group"> <label for="name"> Qu ...

Connect a nearby dependency to your project if it has the same name as an npm repository

What is the best way to npm link a local dependency that has the same name as a project in the npm registry, like https://registry.npmjs.org/react-financial-charts? Here is an example: cd ~/projects/react-financial-charts // Step 1: Navigate to the packa ...

When an SVG image is embedded, its color may not change even after being converted to an inline SVG

I've inserted an SVG using an img tag. When hovering over it, I want the fill color of the SVG to change. I attempted to convert the SVG to inline SVG following this method, but it doesn't seem to be working as expected. No console errors are b ...

Sharing environment variables in gulpfile with other JavaScript files outside NODE_ENV

Is it possible to pass a variable other than NODE_ENV from the gulpfile.js to another javascript file? gulpfile.js // Not related to NODE_ENV! let isDevelopment = true; somejsfile.js /* I need to access "isDevelopment" from the gulpfile.js... For the ...

Angular Directive: Encountering issues with binding to model properties

I am currently working with Angular to develop a straightforward directive. My goal is to showcase the model properties x and y as attributes within the directive. However, instead of retrieving the values for x and y from scope.textItems, I am only seeing ...

When using props.onChange(e.target.value) in a textField component in Material UI, it unexpectedly returns an object instead of a value

function FormInput(props) { const classes = formInputStyles(); return ( <div> <TextField onChange={(e) => props.onChange(e.target.value)} InputProps={{ classes, disableUnderline: true }} {...pro ...

Unable to convert data to a JSON string

I've created a tool for building dynamic tables: <div class="container"> <div class="row"> <div class="col"> <hr> <h3>Add your data</h3> <button id="addTd" type="button" class="btn btn-pr ...