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

Having trouble getting the Angular 2 quickstart demo to function properly?

Just starting out with Angular 2, I decided to kick things off by downloading the Quickstart project from the official website. However, upon running it, I encountered the following error in the console: GET http://localhost:3000/node_modules/@angular/ ...

Showing a confirmation message to the user upon clicking outside of a specific section

On my webpage, I have a section for creating content, as well as a top bar and sidebar with internal links (both controlled by ng controllers). I am looking to implement a confirmation message that will appear if the user tries to leave the page while in t ...

Activate an event on a separate webpage using jQuery or JavaScript

Currently, I am in the process of working on a project with 2 distinct pages: Index Settings On the Settings page, I have implemented a button to close an element and hide it. However, I am encountering an issue where I want the elements on the Index pa ...

Run a series of promises in an array one after the other without relying on async and await

Imagine having an array filled with promises. Each element in this array represents a knex.js query builder that is prepared to be executed and generates a promise. Is there a way to execute each element of this dynamically built array sequentially? let ...

Alerting error message during Laravel array validation via ajax causes error to be thrown

Seeking assistance with validating arrays in Laravel using FormRequest validation <?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class VendorStoreRoom extends FormRequest { /** * Dete ...

Siblings are equipped with the advanced selector feature to

I'm struggling to understand this setup I have: <div class="container"> for n = 0 to ... <a href="some url">Link n</a> endfor for each link in ".container" <div class="poptip"></div> ...

Error in parsing JSON: An unexpected token < was encountered at the beginning of the JSON input, causing a SyntaxError at position 0 when parsing with

I need to transfer an array from a PHP file to JavaScript and save it in a JavaScript array. Below is the snippet of JavaScript code: xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange=function(){ if (xmlhttp.readyState==4 ...

Progressive Web App (PWA) default start URL

I am currently facing an issue with my Vue app, which is also a Progressive Web App (PWA). While the PWA functions correctly as planned, I realized that I am using generic paths for my web application. This means that in order to access the correct page, ...

Is it possible to utilize ng-app as an HTML element?

<!DOCTYPE html> <html > <head> <title>Angular ToDo</title> <script src="script/angular.js"></script> <script src="script/script.js"></script> </head> <ng-app> <body> {{6+ ...

Inject "incorrect" information using jQuery

I am dealing with the following constellation: <dl> <dt>Content</dt> <dd>123</dd> <dt>Content</dt> <dd>123</dd> <dt>Content</dt> <dd>123</dd> </dt> ...

sending parameters to a callback function that is listening for arguments

function setmclisten(message, sender, sendResponse) { console.log(data); if(message['type'] === 'startUp') { console.log(data); sendResponse(data) } } function QuarryToServer(){ chrome.runtime.onMessage.removeListener( ...

Having trouble getting the Socket.io package to install on Node.js in Windows 7?

Hey there! I'm having trouble installing the socket.io module using npm install socket.io. Unfortunately, I encountered the following error: npm WARN package.json <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ee8f80c3869 ...

How to use jQuery to remove the last element from an array when the back button

Currently encountering an issue with removing the last element from an array when a back button is clicked. The console is displaying the correct elements in the array, but it seems that the array.slice function is not working as expected, and I'm str ...

Node.js program closes immediately upon Java startup

I am building a Discord bot that features a Java FXML interface and utilizes a Node.js program to function as an Audio playing bot. However, I am encountering an issue where the process for Node.js closes immediately when opened in Java. The problem arise ...

Prevent the hiding of a select element with jQuery Chosen Select

I am facing difficulty hiding a select element with the chosen-select class if it does not have any elements present. I attempted: $("#Category2").hide(); and also tried removing the chosen-select class before hiding the element: $("#Category2").re ...

Step-by-step guide on removing the focusin event listener from a Bootstrap 5 modal

My current app has a legacy modal that uses a kendo dropdown list element bound to an ajax call with filtering. I'm trying to avoid rewriting this component if possible. However, there is an issue where when the modal opens and you focus on the dropdo ...

Exploring the concepts of nested If statements, for loops, and else if ordering within Javascript programming (FreeCodeCamp - lookUpProfile)

Currently tackling Free Code Camp's Javascript tutorials and encountering a roadblock on the "Contact Profile" <a href="https://www.freecodecamp.com/challenges/profile-lookup#?solution=%2F%2FSetup%0Avar%20contacts%20%3D%20%5B%0A%20%20%20%20%7B%0A%2 ...

What is the best way to utilize purgeCss in conjunction with other module exports?

After reading the documentation, I want to integrate purgeCss into my NextJS project. The recommended configuration in the docs suggests updating the next.config.js file like this: module.exports = withCss(withPurgeCss()) However, I am unsure where exact ...

Using node-fetch version 3.0.0 with jest results in a SyntaxError stating that import statements cannot be used outside a module

Recently, I've been updating my API to utilize node-fetch 3.0.0. One major change highlighted in their documentation is that node-fetch is now a pure ESM module. Click here for more information on the changes This update caused some of my unit tests ...

What is the best way to perform this task in Angular2?

I am currently working with three arrays: tables = [{number:1},{number:2},{number:3},{number:4}]; foods = [ {id:1, name:'Ice Cream'}, {id:2, name:'Pizza'}, {id:1, name:'Hot Dog'}, {id:2, name:'Salad'} ]; o ...