"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

Is there a way to make the text on my Bootstrap carousel come alive with animation effects?

My website features a Bootstrap Carousel with three elements structured like this: <a><img data-src="img" alt="Third slide" src="img"> </a> <div class="carousel-caption"> <h2> <u ...

Angular: encountering an issue with reading the property 'then' of undefined object

My app has a service in place to upload images to Amazon S3 after signing them with my backend using the Cordova file-transfer plugin. I trigger this service after capturing a picture with the Cordova camera plugin to upload the captured image to the S3 b ...

What is the method for showing a JavaScript variable in a popup window?

I am having an issue with a search form that is supposed to display a list of persons based on filters. Once the search results are shown, I want a link (id=names_list) that, when clicked, will open a dialog box containing the names of the persons. I have ...

Sending a `refresh` to a Context

I'm struggling to pass a refetch function from a useQuery() hook into a context in order to call it within the context. I've been facing issues with type mismatches, and sometimes the app crashes with an error saying that refetch() is not a funct ...

The system cannot locate the "default" task. Please consider using the --force option to proceed. The process has been halted due to warnings

Below is the content of my gruntfile.js file var fs = require("fs"), browserify = require("browserify"), pkg = require("./package.json"); module.exports = function(grunt) { grunt.initConfig({ mochaTest: { test: { options: { ...

"Learn the process of integrating Javascript files from the Angular assets folder into a specific Angular component or module (such as Angular 2, 4,

I have custom1.js, custom2.js, and custom3.js JavaScript files that I need to load into Angular components component1, component2, and component3 respectively. Instead of adding these files to the index.html globally, I want to load them specifically for e ...

Clear v-model without changing its associated values

I'm facing an issue with my <input> fields, which look like this: <input type="text" v-model=user.name" /> <input type="text" v-model="user.phone" /> <button @click="add">add user</button> Whenever the add user button i ...

I attempted to create a test scenario to verify that the length of the tasks array is not negative. However, when trying to test this using 'not.toBe' in the code below, an error was encountered

app.js var todos=[]; todos=['to-do one', 'to-do two']; module.exports=todos; app.test.js const todos=require('./app') test('should have a length of at least 0',()=>{ expect(todos.length).toBeGreaterThanOrEqu ...

Passing an object from an Angular application to a backend server in Node.js using the

I have a YAML file with the following structure: Layouts: - Name: Default Layout LayoutId : 1 ConfiguredSegments: LiveA : Height : 100 Id : LiveA Ref1A : ...

How can we use jQuery to animate scrolling down to a specific <div> when clicking on a link in one HTML page and then navigating to another HTML page?

I'm currently working on creating a website for my friend who is a massage therapist. Utilizing a bootstrap dropdown menu, I have added links to different treatments that direct to the treatment.html from the index.html page. Is there a way to creat ...

Exploring nested static properties within TypeScript class structures

Check out this piece of code: class Hey { static a: string static b: string static c: string static setABC(a: string, b: string, c: string) { this.a = a this.b = b this.c = c return this } } class A { static prop1: Hey static ...

Instructions on removing an HTML element from a div that has the attribute contentEditable

Here is an example of HTML code: <div id="editable" contentEditable="true"> <span contentEditable="false">Text to remove</span> </div> I want to be able to delete the entire span element (along with its text) with just one bac ...

Using NodeJS to convert a Base64 string into a JPEG image with the help of either NodeJS or

I have successfully stored a JPEG image in my MySQL database using the longblob format. Now, when I retrieve the longblob data, I am only getting a base64 string. How can I convert this base64 string back to a readable JPEG image using MySQL or Node.js? B ...

Can you tell me the appropriate type for handling file input events?

Using Vue, I have a simple file input with a change listener that triggers the function below <script setup lang="ts"> function handleSelectedFiles(event: Event) { const fileInputElement = event.target as HTMLInputElement; if (!fileInp ...

The Art of JavaScript Module Patterns in Image Sliders

I'm diving into the world of JavaScript and decided to try my hand at creating an image slider. I managed to put together a basic version by following a couple of tutorials, and although it's working fine, I want to move it to an external js file ...

Unexpected database query result in Node.js

In my newuser.js file for a node.js environment with a mongodb database managed through mongoose, I have the following code: //newuser.js //This code is responsible for creating new user documents in the database and requires a GET parameter along with a ...

The accuracy of getBoundingClientRect in calculating the width of table cells (td)

Currently, I am tackling a feature that necessitates me to specify the CSS width in pixels for each td element of a table upon clicking a button. My approach involves using getBoundingClientRect to compute the td width and retrieving the value in pixels (e ...

When using nodejs with sqlite3, the first callback parameter returns the class instance. How can this be resolved in order to prevent any issues?

Exploring a TypeScript class: class Log { public id: number; public text: string; construct(text: string){ this.text = text; } save(){ db.run( `insert into logs(text) values (?) `, this.text, ...

Transforming JSON object to an array of arrays using JavaScript

Attempting to extract specific values from a JSON object and store them in an array can be quite tricky. Below is an example of what this process might look like: Example: var obj = { "1":{"class":2,"percent":0.99,"box":[0.2,0.3,0.4,0.5]}, "2 ...

Achieving uniform width in material ui: Tips for maintaining consistency

I am encountering an issue with the width of my Goal components and can't figure out what is causing it. https://i.stack.imgur.com/jPlyf.png Despite setting the width of the Paper selector to 100%, each Goal component's width remains inconsiste ...