Passing variables into a looped asynchronous callback in AngularJS

I have a function that iterates through an undetermined number of items and performs asynchronous calls on each to retrieve additional data (specifically, the content of HTML template files). The callback also includes some validation checks. The end result should be chainable. $q is injected beforehand, and this code is a part of a factory.

function searchHelpTopics(topics, searchPhrase) {
    if (topics == null || topics.length == 0) return "No search results";
    var results = [];
    var promises = [];
    for (var i = 0; i < topics.length; i++) {
        var templateURL = topics[i].URL;
        var topic = topics[i];
        if (topics[i].HelpTopicId != "Search") {
            var promise = $templateRequest(templateURL).then(function (template) {
                var text = HTMLToText(template, true);
                // perform the search
                if (text.indexOf(searchPhrase) > -1) {
                    if (text.length > 50) text = text.substring(0, 50);
                    var result = {};
                    result.title = topic.Title;
                    result.excerpt = text;
                    result.helpID = topic.HelpTopicID;
                    results.push(result);
                }
            });
            promises.push(promise);
        }
    }
    return $q.all(promises).then(function () {
        return results;
    })

The issue here is that the for loop does not wait for the callbacks to finish, causing the callback to operate on the incorrect topic object. I need a method to pass the correct topic into the callback during each iteration.

Answer №1

One way to enhance your JavaScript code is by utilizing function scope instead of a 'for' loop, which is typically more efficient.

You can achieve this by leveraging built-in JS methods like forEach (available in most browsers from version 1.6 onwards) or using functional style libraries such as underscore.js or lodash.js.

Alternatively, a better approach would be to make use of Array.map and Array.filter functions as shown below:

function processTemplate(topic, template) {
  var text = HTMLToText(template, true);
  // perform search
  if (text.indexOf(searchPhrase) < 0) {
    return;
  }
  if (text.length > 50) {
    text = text.substring(0, 50);
  }
  return {
    title: topic.Title,
    excerpt: text,
    helpID: topic.HelpTopicID
  };
}

function searchHelpTopics(topics, searchPhrase) {
  if (!topics || topics.length === 0) {
    return "No search results";
  }
  var promises = topics
    .filter(function(topic) { return topic.HelpTopicId !== "Search"; })
    .map(function(topic) {
      return $templateRequest(topic.URL).then(processTemplate);
    });
  return $q.all(promises)
    .then(function (results) {
      return results.filter(function (result) {
        return result; // filters out 'undefined'
      });
    });
}

Answer №2

This solution serves as a guide to understanding the functionality, although it is not a comprehensive one.

somefactory.getHelpTopics().then(function (topics) {
    somefactory.searchHelpTopics(topics, searchText).then(function (searchResults) {
        vm.searchResults = searchResults;
        vm.helpID = "Search";
    });
});

--- Explanation of factory functions ----
function searchHelpTopics(topics, searchPhrase) {
    if (!topics || topics.length === 0) return "No search results";
    var promises = topics
        .filter(function (topic) { return topic.HelpTopicId !== "Search"; })
        .map(function (topic) {
            return $templateRequest(topic.URL).then(function (template) {
                return searchHelpTemplate(template, topic, searchPhrase);
            });
        });
    return $q.all(promises).then(function (results) {
        return results.filter(function (result) {
            return result; // filters out 'undefined'
        });
    });
}
function searchHelpTemplate(template, topic, searchPhrase) {
    var text = HTMLToText(template, true);
    // perform the search
    if (text.indexOf(searchPhrase) < 0 && topic.Title.indexOf(searchPhrase) < 0) {
        return;
    }
    if (text.length > 50) {
        text = text.substring(0, 50);
    }
    return {
        title: topic.Title,
        excerpt: text,
        helpID: topic.HelpTopicId
    };
}

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

Unable to decompress using gunzip and serverless-http due to incorrect header check error, yet functions properly with a standard Node.js Express server

Currently, I am tackling an issue with the serverless framework and serverless-http library while attempting to decompress a binary request. The peculiar thing is that the exact code segment functions properly in a typical expressjs server but encounters i ...

Concealing a navigation tab with Angular4 in Typescript: A tutorial

I have successfully implemented 3 tabs in my Angular 4 project. At the moment, I am focusing on working with the first two tabs and planning to tackle the third tab in the near future. To keep things clean and organized, I am looking to use JavaScript/Typ ...

Are there any available resources for comparing the performance of JavaScript libraries?

In preparing a presentation for my company, I am outlining the reasons for choosing jQuery as our primary JavaScript / AJAX library. While most of the work is already completed, it would be beneficial to include a comparison with other libraries, particul ...

What is the best way to execute useEffect in React Router?

Struggling to get my useEffect function to run properly in a React app using a REST API for a Inventory Management application. The issue seems to be with setting the item variable. Any insights on why this might be happening? Item.jsx: import React, { us ...

Updating the count property of an object using setState in React

I have an array of integers ranging from 0 to 6 as my input. My goal is to create an object that gives the count of each number in the array. edition = [6, 6, 6, 1, 1, 2]; const [groupedEdition, setGroupedEdition] = useState([{"0": 0, "1&quo ...

React Component UseEffect does not refresh when there is a change in prop

In my React project, I implemented a dynamic Countdown Timer. You can see the timer in action here. The Timer is encapsulated within a component called MovementTimer.js, and the main component GameRoom.js is responsible for invoking the render method of t ...

unable to decipher a JSON file obtained from Instagram

I have been working on a project that involves parsing a JSON file obtained from the Instagram API. However, I am facing an issue where I can parse the data but I cannot read it properly in my code: <!DOCTYPE html> <html> <head> <meta ...

Adjust div size when the window size changes

I am facing an issue where the 5 divs fit well when the content is initially loaded, but if I resize the window, the size of the containers no longer align with the layout. For my layout, I am using Isotope and the following script to adjust the images to ...

Error: The function props.addToCart is not accessible

While attempting to trigger my action on the client's click of the "addToCart" button to add a new product to the cart, I encountered the error message: "TypeError: props.addToCart is not a function." I am relatively new to Redux and have grasped the ...

What is the best way to transfer a variable from a form to a PHP page using an AJAX script?

Here is the form I am working with: <form onsubmit="serverconnect('Div1', 'query.php'); return false;" Minimum Discount: <br /> <input type="radio" value="20%" name="discount" /> 20% <br /> <input type="radi ...

What is a method to categorize an array of objects by the identical value of a specified key field without relying on any third-party libraries?

I have a collection of unorganized JavaScript objects with similar keys but potentially different values. For example: const raw_data = { "sweets":[ { "flavor":"chocolate", "product":" ...

Picking out specific SharePoint components using jQuery

I encountered an issue with making my two radio boxes readonly. To resolve this, I attempted to disable the unchecked option. Despite trying to access the elements using jQuery and reviewing the data from the console, the solution did not work as expected. ...

Engagement in the assortment of assurances within mongoose

I recently wrote some code to retrieve all tours belonging to a specific user. When fetching the tours, I found that the code worked perfectly for updating the tour status in the parent promises. However, I encountered an issue when trying to get the cou ...

Sharing edited images from various devices while preserving their original high resolution

My aim is to allow users the ability to upload a profile image and then crop it before uploading to ensure it fits perfectly within the image container. This is a common issue that is encountered frequently. The challenge I am facing arises when a user us ...

What is the proper way to utilize $apply and $watch in the Angularjs 1.4.10 Directive Structure?

`.directive('counter', function counter() { return { scope: {}, bindToController: { count: '=' }, controller: function () { function increaseCount() { this.count++; } function decreaseCo ...

"An ActionResult is received as null when the model is passed as an

Has anyone encountered a situation where the model is null when passed to the controller? I inserted an alert in the ajax call to verify the value and it seemed correct, but upon debugging on the first line of the controller's ActionResult, it shows a ...

ReactJs: difficulty in resetting input field to empty string

I have an application using React v 0.13.1 (Old version). I am struggling to update my input field to "" after retrieving the updated value from the database. Scenario: I am updating the input fields by clicking on the button named "Pull&qu ...

Put identical objects into several arrays at the same time

This question is in relation to a previous query about arranging 3 arrays to correspond with each other. After creating my objects, I am now attempting to push values into their respective arrays based on JSON data received. let objName = ["object1", "obj ...

Steps to finish (refresh) a mongoDB record

Currently, I am dealing with the following scenario: An API request from one service is creating multiple MongoDB documents in a single collection. For example: [ {_id: 1, test1: 2, test: 3}, {_id: 2, test1: 3, test: 4} ] Subsequently, a second service ...

Deactivate multiple input fields by utilizing various radio buttons

I need help with enabling and disabling input fields based on radio button selection. When the first radio button is selected, I want to disable three input fields, when the second is selected, only two specific input fields should be enabled (SHIFT START ...