Utilizing AngularJS: Employing the $q Promise Feature to Await Data Readiness in this Scenario

I am currently facing an issue with my Controller and Factory. The Controller initiates an API call in the Factory, but I am struggling to make it wait for the data to be gathered before proceeding. This is where I believe implementing something like $q might be necessary.

In the code snippet below, you can see that vs.tickers initially returns an empty object or array, and only after a while does the console.log in GetTickersFactory execute:

1st Controller

if (root.tickerType === 'portfolio') {

    // Initiating the API GET request in the factory:
    GetTickersFactory.getTickers('portfolio');

    // Calling a function in the Factory to retrieve the array
    // However, since the data is not ready yet, it returns undefined...
    vs.tickers = GetTickersFactory.returnPortfolioTickers();

    console.log('portfolio');
    console.log('vs.tickers = ', vs.tickers);
}

getTickers function in the GetTickersFactory | For more details: check out the full Factory on Gist.

function getTickers(type, load, searchedTicker) {
    load = load || loadString; searchedTicker = searchedTicker || '';

    var portfolioTickersArray = [], searchedTickersArray  = [];

    tickersPane.loadingTickersDone = false;

    switch(type) {
        case 'searched':
            ....
            break;

        case 'portfolio':

            if (portfolioCached) {
                // Making the API Call using cache
                ApiFactory.getWatchList().then(function(data) {
                    portfolioTickersArray = renderTickers(data.data.tickers, undefined, type);
                    portfolioTickers.that = portfolioTickersArray;
                    tickersPane.loadingTickersDone = true;
                    console.log('portfolioTickersArray: ', portfolioTickersArray);
                    return portfolioTickersArray;
                });

            } else {
                // Making the API Call without cache
                ApiFactory.getWatchListRefresh().then(function(data) {
                    portfolioTickersArray = renderTickers(data.data.tickers, undefined, type);
                    portfolioTickers.that = portfolioTickersArray;
                    portfolioCached = true;
                    tickersPane.loadingTickersDone = true;
                    console.log('portfolioTickersArray: ', portfolioTickersArray);
                    return portfolioTickersArray;
                });
            }
            break;
    }

    function renderTickers(data, searchedTicker, type) {
        ....
    }
}

The return Array function within the getTickersFactory.js | I suspect I should find a way to use promises here instead of relying on this method:

function returnPortfolioTickers() {
    return portfolioTickers.that;
}

I did attempt the following previously, with no success:

vs.tickers = GetTickersFactory.getTickers('portfolio');

vs.tickers would still end up as undefined

Answer №1

When utilizing the ES6-style promise, you can easily implement it in your code:

function fetchTickers(type, load, searchedTicker) {

  return $q(function (resolve, reject) {

     if (performGoodActions()) {
       resolve('Operation successful!');
     } else {
       reject('Action failed');
     }

  }
}

Then, within your controller:

if (root.tickerType === 'portfolio') {

  FetchTickersProvider.fetchTickers('portfolio').then(function (result) {
    console.log('Success: ' + result);
  }, function (error) {
    console.log('Encountered Error: ' + error);
  });

}

Answer №2

Ensure that your switch cases return a promise so that the caller function can utilize .then when the promise is resolved.

Factory Code

function getTickers(type, load, searchedTicker) {
    // keep other code unchanged
    tickersPane.loadingTickersDone = false;
    switch (type) {
        case 'searched':
            return ApiFactory.getTickers(null, load).then(function(data) {
                // keep other code unchanged
                if (tickersPane.tempTickers.length > 0) {
                    // keep other code unchanged
                    return returnData(searchedTickersArray);
                }
                return []; // return default variable to continue promise chain
            });
            break;
        case 'portfolio':
            tickersPane.addOption = false;
            tickersPane.removeOption = true;
            tickersPane.displayTopTags = false;
            tickersPane.displayPortfolio = true;

            if (portfolioCached) {
                return ApiFactory.getWatchList().then(function(data) {
                    // keep other code unchanged
                    return returnData(portfolioTickersArray);
                });
            } else {
                return ApiFactory.getWatchListRefresh().then(function(data) {
                    // keep other code unchanged
                    return returnData(portfolioTickersArray);
                });
            }
            break;
    }
    function renderTickers(data, searchedTicker, type) {
        // this remains as is
    }
    function returnData(data) {
        tickersPane.loadingTickersDone = true;
        return data;
    }
    //tickersPane.loadingTickersDone = true;
    //return data; // removed this line and moved to function
}

Controller

if (root.tickerType === 'portfolio') {
    // Call the factory to initiate API GET requests:
    GetTickersFactory.getTickers('portfolio').then(resp){
         vs.tickers = GetTickersFactory.returnPortfolioTickers();
         console.log('portfolio');
         console.log('vs.tickers = ', vs.tickers);
    };
}

Answer №3

You have two existing methods that return promises (getWatchList and getWatchListRefresh), so there is no need to use $q here. Simply return these methods and chain more .then functions.

Consider this simplified example to guide you in the right direction...

function fetchStockData(type) {
    var stockData;

    switch(type) {
        // ...
        case 'portfolio': 
            var getList = portfolioCached ? getWatchList : getWatchListRefresh;
            // stockData will be set to the promise returned
            // by getWatchList or getWatchListRefresh
            stockData = getList().then(function(data) {
                if (!portfolioCached) {
                  portfolioCached = true;
                }
                // the result returned by renderStockData will be 
                // passed to any `.then` that is chained
                return renderStockData(data.data.tickers, undefined, type);
            });
        break;
    }

    // return the stockData promise
    return stockData;
}

In your controller:

// the `stockData` parameter here will hold the result
// returned by renderStockData()
FetchStockDataFactory.fetchStockData('portfolio').then(function(stockData) {
    vs.stockData = stockData;
});

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

Evaluating the use of promise in Angular using Jasmine testing

I'm currently troubleshooting whether a method with a promise is being properly called Below is the snippet of my controller code: app.controller('StoresListController', function ($scope, StoresService) { $scope.getStores = function ( ...

Tips for creating animated clouds that move across the screen using Flash, jQuery, or JavaScript

Are there methods to create scrolling clouds using tools such as Flash, jQuery or JavaScript? If so, can you provide me with the code required to implement this on my homepage? I have three small cloud images that I would like to animate moving in the bac ...

An improved solution for avoiding repetitive typeof checks when accessing nested properties in the DOM

One common issue I encounter when working with nested DOM objects is the risk of undefined errors. To address this, I often use a conditional check like the one shown below: if("undefined" != typeof parent && "undefined" != typeof parent.main ...

What is the method for identifying which individual element is responsible for activating an event listener?

While working on a color picker project in JavaScript for practice, I encountered an issue. I needed the #screen element to display the selected color when clicked. How can I determine which color has been clicked and pass its value to the function so that ...

Javascript code experiences a shift in two different styles within a single conditional statement

I'm new to web design and I'm having trouble getting this code to work properly. Can anyone help me fix it so that both styles are applied correctly? // Access the sign_up modal window var modal = document.getElementById('id01'); // A ...

Differentiating between mouseenter and tap events: What's the key?

When a mouseenter event is present, touch-enabled devices will activate this event when the user taps on the element. Is there a way to differentiate between an actual physical mouse entering and a simulated tap (which resembles a mouse enter)? ...

How can I retrieve the length of an array in vuejs?

This snippet includes a script tag <script> export default { data() { return { blogs: [], }; }, created() { this.paginate_total = this.blogs.length / this.paginate; }, }; </script> Displayed below is the respo ...

Is it possible to pass multiple parameters in one onClick event?

My search bar is located below. Upon clicking the 'search' button, I wish for it to update results and redirect to a different page simultaneously. function SearchBar(props) {   const [innerSearch, setInnerSearch] = useState(""); ...

Arranging an array of objects by a specific property in an Angular controller with the help of $filter

In my data set, there is an array of objects referred to as $scope.segments, which looks like this: [ { "_id": "55d1167655745c8d3679cdb5", "job_id": "55d0a6feab0332116d74b253", "status": "available", "sequence": 1, "body_original": " ...

Is there a workaround for the issue with AngularJs ng-disabled not functioning in Internet Explorer 9?

Having an issue with the ng-disabled directive in AngularJS. It works well in Chrome, but not in IE 9. Any suggestions on how to make it work in IE 9 would be greatly appreciated. main.html <select kendo-drop-down-list k-data-text-field="'text&a ...

Differences between Javascript object constructor and object literal

Similar Questions: Creating Objects - New Object or Object Literal Notation? Literal Notation VS. Constructor to Create Objects in JavaScript As I embark on my initial Javascript tutorial journey, I have come across two distinct methods of creatin ...

Is there a way to showcase the string message from BadRequest(message) utilizing Ajax?

I am currently working on implementing an API Controller. public ActionResult<Campaigns> AddCampaign([Bind("Name, Venue, AssignedTo, StartedOn, CompletedOn")] Campaigns campaigns) { try { if (ModelState.IsVal ...

Identify any missing periods and combine the years into a single range

I am working on restructuring year ranges with gaps and consolidating them. For example, converting [{start: 2002, end: 2020}, {start: 2020, end: null}] to {start: 2002, end: null} or [{2002, 2004},{2006, 2008}, {2008, null}] to [{2002-2004}, {2006-null}]. ...

What is the best way to reset local state after triggering a success action in Redux Saga?

I'm looking to reset my component state after a successful call in redux saga. Is there a way to achieve this? Currently, I am using the component state to track my input value. this.state = { department: '', }; The solution I have im ...

Is it possible to utilize $(this) within the $.post() function?

I seem to be having trouble accessing $(this) from inside the $.post section. It works perfectly fine outside of it. Here is the JavaScript code: $('.idea').each(function(){ var title = $(this).html(); $.post("votes.php", { t ...

What is the best way to send information using an array of objects?

In my Angular 5 project, I am using the ngx select dropdown package (https://www.npmjs.com/package/ngx-select-dropdown) and I'm wondering how to pass an array of objects instead of an array of strings. Currently, when I do so, it displays [Object Obje ...

Arranging elements in an array based on two different properties

Trying to organize an array of elements (orders details). https://i.stack.imgur.com/T2DQe.png [{"id":"myid","base":{"brands":["KI", "SA"],"country":"BG","status":&qu ...

Tips for implementing HTTP requests in Angular 2 without TypeScript

The demonstrations provided by the angular team only illustrate injecting Http for typescript. https://angular.io/docs/js/latest/api/http/Http-class.html How can this be accomplished in JavaScript? import {Http, HTTP_PROVIDERS} from 'angular2/http& ...

React does not always remove event listeners when using the useEffect hook's return callback

I have a functionality in my component where it initializes a key event listener. This event is supposed to trigger an API call to fetch random data. useEffect(() => { const keyUpEvent = (event) => { if (event.code === "Enter") { ...

Refine the JSON data to only include the values you need

I've been spending a considerable amount of time on this challenge, but I'm facing difficulty in solving it. The issue at hand is that I have a JSON file that I am fetching from a URL, and my objective is to create a filter that displays only nam ...