"Delving into the intricacies of Angular's factory

If I have a factory like the one below:

app.factory("categoryFactory", function (api, $http, $q) {    
    var selected = null;
    var categoryList = [];
    return {
        getList: function () {
            var d = $q.defer();
            if(categoryList.length  <= 0){
                $http.get(api.getUrl('categoryStructure', null))
                    .success(function (response) {
                        categoryList = response;
                        d.resolve(categoryList);
                    });
            }
            else
            {
                d.resolve(categoryList)
            }
            return d.promise;
        },
        setSelected: function (category) {
            selected = category;
        },
        getSelected: function () {
            return selected;
        }
    }    
});

Now, I have two controllers using this factory simultaneously. Both controllers need to be alerted when there are updates, so I implemented the following:

app.controller('DashboardController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api','categoryFactory', function ($http, $scope, $sessionStorage, $log, Session, api, categoryFactory) {

$scope.selectedCategory = categoryFactory.getSelected();

}]);

The other controller is as follows:

app.controller('NavController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api', 'FileUploader', 'categoryFactory', function ($http, $scope, $sessionStorage, $log, Session, api, FileUploader, categoryFactory) {
$scope.categories = [];

categoryFactory.getList().then(function (response) {
$scope.categories = response;
});
$scope.selectCategory = function (category) {
categoryFactory.setSelected(category);
}

}]);

However, when the value was changed in the NavController, it did not reflect in the DashboardController.

My question is how can I either set up a watch or another method to receive notifications when the value changes?

Answer №1

If you want to implement an observer pattern in your AngularJS application, follow this example:

app.factory("categoryFactory", function (api, $http, $q) {   
    // the list of callbacks to call when something changes
    var observerCallbacks = []; 

    // ...

    function notifyObservers() {
        angular.forEach(observerCallbacks, function(callback) {
            callback();
        });
    }

    return {
        setSelected: function (category) {
            selected = category;
            // notify the observers after you change the value
            notifyObservers();
        },
        registerObserver: function(callback) {
            observerCallbacks.push(callback);
        }
    }    
});

In your controllers, for example 'NavController':

app.controller('NavController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api', 'FileUploader', 'categoryFactory', function ($http, $scope, $sessionStorage, $log, Session, api, FileUploader, categoryFactory) {
    // ...

    // initialization
    (function() {
        categoryFactory.registerObserver(function() {
            categoryFactory.getList().then(function (response) {
                $scope.categories = response;
            });
        });
    })();

}]);

Whenever setSelected is called, all registered callbacks in observerCallbacks will be triggered. You can register these callbacks from any controller since factories are singletons.

Edit: It's possible that the notifyObservers() call is placed incorrectly in the code and the update call might not be right either, but the overall architecture remains. In the registerObserver function, define what needs to happen when values are updated and remember to call notifyObservers() whenever necessary.

Answer №2

By implementing the dot rule, you can ensure that prototypal inheritance is properly followed in your AngularJS code.

The key is to have an object within your service that contains a selected variable, eliminating the need for a getSelected method.

Factory

app.factory("categoryFactory", function(api, $http, $q) {
    var categoryFactory = {};
    categoryFactory.getList = function() {
        var d = $q.defer();
        if (categoryList.length <= 0) {
            $http.get(api.getUrl('categoryStructure', null))
                .success(function(response) {
                    categoryList = response;
                    d.resolve(categoryList);
                });
        } else {
            d.resolve(categoryList)
        }
        return d.promise;
    }
    categoryFactory.setSelected = function(category) {
        categoryFactory.data.selected = category;
    }
    categoryFactory.data = {
        selected: null
    }
    return categoryFactory;
});

Controller

app.controller('DashboardController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api', 'categoryFactory',
    function($http, $scope, $sessionStorage, $log, Session, api, categoryFactory) {
        //this will provide you binding without watcher
        $scope.selection = categoryFactory.data;
    }
]);

Simply use {{selection.selected}} in the HTML section to automatically update the value when changes occur in the selection.

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

Encountering a pair of errors while working with Node.js and Express

(Apologies for the vague title) I have been developing a project using Firebase and Express, but I am encountering some issues. src/index.js import { initializeApp } from "firebase/app"; import { doc, getFirestore } from "firebase/firesto ...

Creating Vue components and including Javascript code within them

Attempting to develop a component using Vue for my JavaScript code, but encountering issues. My primary aim is to build a component with either Vue or Vue3 <head> <title></title> <script src="https://cdn.jsdelivr ...

The connection was denied! Has the Selenium server been launched for Nightwatch on Edge?

I have created a project using vue.js. The project includes a small set of unit tests (jest) and an end-to-end test (night watch). Unfortunately, when attempting to run the end-to-end test using npm I encountered the following error: Error retrieving a ne ...

Verifying Kentico Cloud webhook signatures using Express.js

Is there a way to verify the signature of webhooks using Express.js? I've looked through the documentation on notification signatures, but I'm unsure how to integrate it with Express.js. This question was originally posted on the official Ken ...

Tips for sending JSON data to .js files

I am experiencing an issue here. In locations.php, I have the following code to generate JSON data: <?php $locations = array( array('2479 Murphy Court', "Minneapolis, MN 55402", "$36,000", 48.87, 2.29, "property-detail.html", ...

Adjusting Header Size While Scrolling in Angular

Looking for assistance with AngularJS regarding handling events (specifically scrolling). I am trying to dynamically change the size of the header based on whether the user scrolls up or down. Below is a snippet of JavaScript code that achieves this effect ...

Leverage the power of JavaScript functions within the app.component.ts file of

I have a JavaScript file named action.js and I am trying to incorporate it into an Angular project. After doing some research, I found out that the js file should be placed in the assets folder and the path must be referenced in the scripts array within an ...

Troubleshooting the net::ERR_ABORTED 404 (Not Found) error while utilizing next/link to call an API route in NextJS

While developing an api route for downloading a CSV file, I encountered an error when using Next Link. Unfortunately, switching to another method is not an option as it would cause my application to fail to build. The component in question is straightforwa ...

How can I implement a feature in React.js where clicking a button triggers a Canvas to draw something?

I am trying to implement a <button> component that will trigger a specific function to draw on my HTML5 Canvas element. This is how the project files are structured and how I have passed the necessary props -> The main drawing function is locate ...

Inspecting a substring of an element dynamically added in VueJs

When I click a button in my form, it adds a new line. The challenge is making sure that each new line evaluates independently and correctly. In this case, the task involves checking the first 2 digits of a barcode against a dataset to determine a match or ...

Learn how to efficiently transfer a dynamic subtitle text value from an HTML5 video player to a database

I have successfully developed an HTML5 video player with subtitles. Currently, my goal is to allow the user to submit the subtitle text at a specific timestamp to a database. Despite trying various methods, I am facing challenges in fetching the subtitle t ...

Change the value of the material slide toggle according to the user's response to the JavaScript 'confirm' dialogue

I am currently working on implementing an Angular Material Slide Toggle feature. I want to display a confirmation message if the user tries to switch the toggle from true to false, ensuring they really intend to do this. If the user chooses to cancel, I&ap ...

What is the process for using the GitHub API to access a repository's README document?

For my project, I'm utilizing the GitHub API to access the raw README.md file using /repos/{owner}/{repo}/readme. I've successfully executed the call using Thunderclient in VSCode and retrieved the raw file. https://i.sstatic.net/FtkfW.png Howev ...

The WebDriver server at http://192.168.1.34:62334/wd/hub took too long to respond, causing a timeout error in Selenium

Currently, I am diving into the book "AngularJS: Novice to Ninja," and have completed the necessary installations by running the following commands: npm install karma npm install jasmine npm install karma-chome-launcher npm install protractor Following t ...

Employing the MVC framework along with AngularJS and the Sortable feature, ensure that the nodes are sorted by

I am facing an issue with the sorting of nodes in a list. Whenever I create multiple nodes in the same session and then update the site, the nodes are randomly sorted. Is there a way to make them sort by the latest created node so that the first created no ...

MVC with StructureMap, Repository Layer, and Console/Class Library Integration

I have successfully implemented StructureMap for dependency injection in my WebApi application. The ApiController includes a repository with dependencies like IMapper and DB connection string injected in its constructor. The repository is located in a sep ...

Issue with VueJS instance: Unable to prevent default behavior of an event

Is there a way to disable form submission when the enter key is pressed? Take a look at the different methods I've attempted along with the code and demo example provided below. SEE PROBLEM DEMO HERE Intended outcome: When you focus on the input, pr ...

Is there something I'm overlooking when it comes to vue router transitions?

I am attempting to implement a smooth transition between my Vue components with the following code: <template> <div id="app"> <router-link to="/">Go to home</router-link> <router-link to="Register">Go to register< ...

Is it possible to adjust the width of a parent element based on the number of child

In this particular example, I have structured a header with a logo-container positioned on the left side, a menu in the center, and a button on the right. The menu consists of 5 top-level items and 2 sub-menus. <div class="container"> <div cla ...

jQuery puzzle: a form within a form within a form

I am facing a bit of a dilemma with a partially working solution. The scenario is this - I am using a basic .load() function attached to a <select> element to fetch another form populated through PHP/MySQL. What I intend to achieve is for this newly ...