Is there a way to make this AngularJS service wait until it has a value before returning it?

Multiple controllers call a service that loads data into an object named categories:

.service('DataService', ['$http', '$q', function($http, $q) {

    var categories = {};

    // Public API.
    return({
        setCategory: setCategory,
        getCategory: getCategory,
        getJSON: getJSON,
        categories: categories
    });


    function setCategory(name, category) {
        console.log("setting category");
        console.log(name, category)
        categories[name] = category;
    }

    function getCategory(name) {
        console.log("getCategories:");
        console.log(categories[name]);
        return categories[name];
    }

    function getJSON() {
    //JSON stuff to initialize categories values.
    }

The issue arises when calling getCategory(name) before categories is populated:

 $scope.category = DataService.getCategory(name);
 //$scope.category is undefined

How can I modify the Service so that getCategory waits until categories is defined? Alternatively, how can I adjust the Controller so that getCategory only executes once categories has a value? Attempts with $scope.$watch in the controller have not been successful as it does not update the value.

Answer №1

Utilize the promises that are already included in your service. Here is a suggested approach among many possibilities:

var pendingQueue = [];
var loaded = false;
var self = this;
function getCategory(name) {
    var deferred = $q.defer();

    if (loaded) {
        // Resolve immediately
        console.log('Categories already loaded, resolving immediately...');
        deferred.resolve(self.categories[name]);
        return deferred.promise;
    }

    // Queue the request
    pendingQueue.push({
        promise: deferred.promise,
        name: name
    });

    if (pendingQueue.length === 1) {
        console.log('First request for a category, sending request...');

        // We are the FIRST request. Make the necessary call to load the data.
        // This wouldn't be thread-safe in a 'real' language, but with only one thread it's fine.
        $http.get('/my-data').then(function(data) {
            self.categories = data;
            console.log('Categories loaded', self.categories);
            loaded = true;

            pendingQueue.map(function(entry) {
                entry.promise.resolve(entry.name);
            });

            pendingQueue.length = 0;
        });
    }

    return deferred.promise;
}

In your controller:

DataService.getCategory(name).then(function(category) {
    // Handle the category here
});

This process will:

  • For the first request, initiate the asynchronous request and resolve the promise once the data is fetched.

  • For subsequent requests before the data is available, queue them to avoid duplicate requests.

  • For requests made after the data is loaded, promptly resolve with the requested information.

No error handling has been incorporated - remember to use deferred.reject() for error responses, and .catch() / .finally() to manage them within the controller(s).

There are multiple approaches; this is just one possibility.

Answer №2

Within DataService Component

myApp.service('DataService', function($resource, $q) {
var resource = $resource('/api/category/:id', {}, {
    query: {
        method: 'GET',
        isArray: false,
        cache: false
    },
    save: {
        method: 'POST',
        isArray: false,
        cache: false
    }
});
return {
    getCategory: function(id) {
        var deferred = $q.defer();
        resource.query({id: id},
            function(response) {
                deferred.resolve(response);
            },
            function(response) {
                deferred.reject(response);
            }
        );
        return deferred.promise;
    },
    setCategory: function(categoryObj) {
        var deferred = $q.defer();
        resource.save(categoryObj,
            function(response) {
                deferred.resolve(response);
            },
            function(response) {
                deferred.reject(response);
            }
        );
        return deferred.promise;
    },
    getJSON: function() {
        // operations to be performed
    }
};
});

Inside DataController Section:

myApp.controller('DataCtrl', function($scope, DataService) {

    $scope.handleSuccessResponse = function(response) {
        $scope.data = response;
    };

    $scope.handleErrorResponse = function(response) {
        $scope.error = response;
    };

    DataService.getCategory(123).then($scope.handleSuccessResponse, $scope.handleErrorResponse);
});

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

You are unable to use multiple background colors on a material UI snackbar

Is there a way to apply multiple background colors to a material UI snackbar? I attempted using linear-gradient as shown below, but it didn't work. import Snackbar from 'material-ui/Snackbar'; const bodyStyle = { border: `2px solid ${co ...

FNS Date-Timezone Abbreviation

Is there a way to shorten the Australian Eastern Daylight Time abbreviation to just AEDT? When I use it currently, it displays as 11/11/2022 15:29:25 Australian Eastern Daylight Time. I would like it to show as 11/11/2022 15:29:25 AEDT import { formatInT ...

Is there a way to make a selected option stay selected in vue.js 2?

Here is the structure of my Vue component : <template> <select class="form-control" v-model="selected" :required @change="changeLocation"> <option :selected>Choose Province</option> <option v-for="option in o ...

Leveraging the value service in AngularJS

Struggling to grasp the concept of .value() and how to utilize it in controllers... Here's an example where we declare the .value() in services.js : .value("ScanDatas",{ scanData: {} }) Once "scanData" is defined, it should be available througho ...

Tips to avoid multiple HTTP requests being sent simultaneously

I have a collection of objects that requires triggering asynchronous requests for each object. However, I want to limit the number of simultaneous requests running at once. Additionally, it would be beneficial to have a single point of synchronization afte ...

Is the DOMContentLoaded event connected to the creation of the DOM tree or the rendering tree?

After profiling my app, I noticed that the event is triggered after 1.5 seconds, but the first pixels appear on the screen much later. It seems like the event may only relate to DOM tree construction. However, this tutorial has left me feeling slightly con ...

Trouble with selecting inputs within a Div Element

Could you please review the code below and help me understand why I am unable to retrieve the ID of the selected radio buttons using this.id? <div id="pay" class="btn-group" data-toggle="buttons"> <label class="btn btn-primary"> < ...

Sending information from a rails controller to a react component

Wondering how to pass the example @post = Post.all from the controller to React component props while integrating Rails with React via Webpacker. Is it necessary to do this through an API or is there another way? ...

Setting the default selection in AngularJS based on the URL entered

I've encountered an issue with a dropdown menu in my AngularJS version 1.4 application. The dropdown menu contains links to different sections of the page. The problem arises when I directly enter the page URL - instead of selecting the correct link f ...

Displaying elements of array in Pug template is a key task for

As a newcomer to the Jade/Pug template engine used in Express, I am faced with a challenge. I need to display the name property of each object within an associative array that is passed as a parameter to my pug template from an express route module. I hav ...

Is it possible to implement UseState in Server-Side-Rendering scenarios?

Is it possible to utilize useState (and other react hooks?) with Server Side Rendering? I keep encountering the following error when attempting to execute the code: TypeError: Cannot read property 'useState' of null. Oddly enough, if I disable ...

Utilizing the 'input' method to modify the key of an array object within specified elements in a Vue.js application

i am looking to implement an input field that can be used to edit the title of the currently selected element component (the selection is made by clicking). The challenge here is to have a single input that works for each individually selected element. I h ...

Issues with the count up functionality in jQuery

I'm currently working on a project involving countups. My aim is to have multiple countups displayed on a single page. While having one countup using interval() function poses no issues, I encounter trouble when trying to display two or more countups ...

The issue with displaying inline block is that the divs are not appearing side by side on the

Two of my div elements, namely form-panel and data-panel, are currently not aligned on the same line. How can I use display:inline-block to align them in a single line? Please review the code provided below. I have already used display:inline-block on both ...

Guide to uploading a JavaScript File object to Cloudinary via the node.js API

After researching various options, I decided to use cloudinary for uploading a file to an image server from my node js api. I successfully installed the npm package for cloudinary and implemented the code based on their api documentation Below is the fun ...

Reversing the Jquery checkbox functionality

Seeking some quick assistance here. I'm puzzled as to why this function seems to be working in reverse. Whenever I try to check the checkboxes, they all get unchecked and vice versa. Everything was functioning smoothly until I introduced the .click() ...

The function signature '(_event: React.SyntheticEvent, value: number) => void' cannot be assigned to the type 'FormEventHandler<HTMLUListElement>'

I am facing an issue with my component "PageFooter" being duplicated in three other components. I am trying to refactor it as a UI component. " I am getting the error message: 'Type '(_event: React.SyntheticEvent, value: number) = ...

Error occurred in AngularJS service due to incorrect data type

Looking to store the URL of a query in an AngularJS service like this: var mortgageloanService = angular.module('loanstreetIpadAppApp', []); mortgageloanService.factory('updateTable', function($http) { return { getParams: fun ...

The map markers are nowhere to be found on the map when using Internet Explorer

Take a look at this code I wrote... var styles = [ { "featureType": "landscape", "stylers": [ {"weight": 0.1}, {"color": "#E7EDEF"} ] }, ... { "featureType": "poi.park", "elementType": "labels", "stylers": [ ...

Leverage the power of v-model props in your Vue 3 component to efficiently retrieve API breakpoints

I am currently working on integrating the API breakpoints in my child component. These components are designed for my football web application. The breakpoints are sourced from: api-football My challenge lies in passing multiple prop values into a c ...