Pass a JSON $http promise to an Angular factory

Currently, the structure of my Angular app is as follows:

Factory within app.js

StoreApp.factory("DataService", function () {

    // create store
    var myStore = new store();

    // create shopping cart
    var myCart = new shoppingCart("Store");

   // return data object with store and cart
   return {
       store: myStore,
       cart: myCart
   };
});

controller.js

function storeController($scope, $http, $routeParams, $location, DataService) {

            $scope.store = DataService.store;
            $scope.cart = DataService.cart;

            // use routing to pick the selected product
            if ($routeParams.productUrlTitle != null) {
                $scope.product = $scope.store.getProduct($routeParams.productUrlTitle) || $scope.store.getHero($routeParams.productUrlTitle);
            }

            $scope.predicate = '-price';
            $scope.store.isCart = $location.path() == "/cart";
}

The issue lies in store.js (below) where the this.products[] currently has inline assignments. This needs to be modified to load an external JSON file (also below). I've attempted various methods from including/passing the promise to var myStore = new store();, to actually using $http.get() paired with .then() inside of store.js — all without success.

store.js

function store() {
   this.products = [
       new product("USD", 20, "https://foo.jpg", "Name", "Description"),
   new product("USD", 20, "https://bar.jpg", "Name", "Description"),
];

}
store.prototype.getProduct = function (urlTitle) {
    for (var i = 0; i < this.products.length; i++) {
    if (this.products[i].urlTitle == urlTitle)
        return this.products[i];
    }
    return null;
}

payload.json

[
    {
    "currency": "usd",
    "cost": 1000,
    "image_url": "https://whatever.domain/someimage.jpg",
    "id": "xyz",
    "name": "A title",
    "description": "Some details"
   },
   ...
]

For those curious, my project is inspired by this: A Shopping Cart Application Built with AngularJS.

Thank you in advance.


Update

I was able to achieve my goal, although I am unsure if it's the most optimal (Read: correct) approach. Essentially, a new factory named "InventoryService" was added and passed to my controller.

app.js

// New Factory Added

StoreApp.factory('InventoryService', ['$http', '$rootScope',
    function ($http, $rootScope) {
        var inventory = [];

        return {
            getInventory: function () {
                return $http.get('http://localhost/ShoppingCart/payload.json').then(function (response) {
                    inventory = response;
                    $rootScope.$broadcast('handleInventoryService', inventory);
                    return inventory;
                })
            }
        };
    }
]);

controller.js

function storeController($scope, $http, $routeParams, $location, InventoryService, DataService) {

    $scope.name = 'inventory';
    (function () {
        InventoryService.getInventory().then(function (inventory) {
            $scope.inventory = inventory;

            for (var i = 0; i < $scope.inventory.data.length; i++) {
                if ($scope.inventory.data[i].id == '11ca3ea26f0e431eb996a401f292581f2') {
                    DataService.store.hero.push(
                        new product(
                            $scope.inventory.data[i].id,
                            $scope.inventory.data[i].image_url,
                            $scope.inventory.data[i].name,
                            $scope.inventory.data[i].description,
                            $scope.inventory.data[i].cost
                        )
                    );
                } else {
                    DataService.store.products.push(
                        new product(
                            $scope.inventory.data[i].id,
                            $scope.inventory.data[i].image_url,
                            $scope.inventory.data[i].name,
                            $scope.inventory.data[i].description,
                            $scope.inventory.data[i].cost
                        )
                    );
                }
            }

            // get store and cart from service
            $scope.store = DataService.store;
            $scope.cart = DataService.cart;
 ...

store.html partial

<div ng-include src="'partials/header.html'"></div>

<div ng-repeat="product in store.hero" class="row-fluid">
    <div class="span12">
        <div class="span4">
            <a href="#/products/{{product.urlTitle}}">
                <img class="img-polaroid" ng-src="{{product.image_url}}" title="{{product.name}}" />
            </a>
        </div>
        <div class="span8">
            <h1 class="tango-tang weight-100">
                {{product.name}}
            </h1>
            <hr />
            <div class="row-fluid">
                <div class="span7">
                    <p>
                        {{product.description}} 
                    </p>
                </div>
                <div class="span5">
                    <div class="well">
                        <h1 class="weight-300 text-center">
                            {{product.price | currency}}
                        </h1>
                    </div>
                    <button class="btn btn-success btn-medium btn-block" ng-click="cart.addItem(product.sku, product.image_url, product.name, product.price, 1)">
                            <i class="icon-plus"></i> Add to Cart 
                    </button> 
                    <a href="#/products/{{product.urlTitle}}" class="btn btn-block">
                        <i class="icon-list"></i> Details
                    </a> 
                </div>
            </div>
        </div>
    </div>
</div>

Answer №1

In my explanation, I mentioned that the InventoryService may not be needed in your situation, as $q and $http.get should suffice.

Here is a quote from the comments:

You could potentially turn both products and hero into promises and then resolve the deferred objects together once the HTTP response is received.

Below is the code snippet:

App.factory('DataService', function($http, $q) {
    function Store() {
        var heroDeferred = $q.defer();
        var productsDeferred = $q.defer();

        this.hero = heroDeferred.promise;
        this.products = productsDeferred.promise;

        $http.get('/path/to/payload.json').success(function(data) {
            var hero = [];
            var products = [];

            for (var i = 0, len = data.length; i < len; i++) {
                var prod = data[i];
                if (prod.id === 'xyz') {
                    hero.push(prod);
                } else {
                    products.push(prod);
                }
            }

            heroDeferred.resolve(hero);
            productsDeferred.resolve(products);
        });
    }

    Store.prototype.getProduct = function(urlTitle) {
        return this.products.then(function(products) {
            for (var i = 0; i < products.length; i++) { // It's important to use products here, as it contains the actual values; this.products is a promise
                if (products[i].urlTitle == urlTitle)
                    return products[i];
            }
            return null;
        });
    };

    ...
    return {
        store: new Store()
        ...
    };
});

http://plnkr.co/edit/qff7HYyJnSdEUngeOWVb

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

Binding a value from a template model to the parent scope in AngularJS using directives

I have been attempting to develop an angular directive that mimics the functionality of this jquery jsfiddle. http://jsfiddle.net/RCaCM/7/ The main goal of the directive is for the controller scope to contain a width value, which will be displayed in the ...

Stopping the timer with clearInterval isn't functioning as expected

I posted my work online here: Instructions on how to reproduce: Click on a green pin on the map to select a station Fill in the fields for name and last name Sign on the canvas A timer should start counting down from 20 minutes If you click on the ...

Consolidating all my imports into separate files for each page

Currently, I have a manage.js page in my NextJS app with the following imports: import React from 'react'; import PaginatedItems from '../components/PaginatedItems' import TitleHeadline from "../components/ui/TitleHeadline&quo ...

After the EditorJS processes the saved block data, the hyperlink is displayed as an <a> tag

"content": "{\"time\":1725957883405,\"blocks\":[{\"id\":\"ILZ-IPMJxE\",\"type\":\"paragraph\",\"data\" ...

Do JavaScript functions operate synchronously or asynchronously?

Here is the JS code snippet I am working with: <script> first(); second(); </script> I need to ensure that second() will only be executed after first() has completed its execution. Is this the default behavior or do I need to make any modific ...

Troubleshooting automatic login problem in VB 2013 settings

For my application, I am utilizing the most recent version of Awesomium's WebControl. The goal is for it to automatically log in when it reaches "accounts.google.com/ServiceLogin" by executing some Javascript. In my.settings.java file, I have the foll ...

What is the best way to target children elements within individual instances of a specific class?

Below is a snippet of the code I am working with. <div class="row"> <div class="column"> </div> <div class="column"> </div> </div> <div class="row"> <div class="column"> </div&g ...

Encountering a problem when navigating directly to a URL in a Laravel and AngularJs single

As a newbie to Laravel, I have a question regarding my single page application that uses Angular and Laravel. I'm trying to retrieve all records from the "todos" table and display them on todo.html when clicking on a "View all Todos" button, which red ...

Guide on extracting specific data from a string and generating a JSON object using Python

After making a get request to a website and parsing it using BS4 with 'Html.parser', I have managed to narrow down the extracted string to focus on retrieving the ID, size, and availability information. The final string looks like this: '{" ...

Preventing Event Propagation in AngularJS with ng-click

One of my directives is set up like this: app.directive('custom', function(){ return { restrict:'A', link: function(scope, element){ element.bind('click', function(){ alert(&apo ...

expanding the input and duplicating the outcomes

I'm attempting to clone and add values to results in my project. Here's the link to what I have so far: http://jsfiddle.net/QNP3r/175/ Currently, it is functioning adequately with a table element. However, I'd like to make it work with a di ...

Multiple clicks causing the button to increment excessively and fire repeatedly

Currently, I am working on a web application that includes a deploy button in one section. When this button is clicked, a modal pops up with two additional buttons, one for confirmation and the other for cancelation. The issue I am facing is that when I c ...

The selected value in the ng-model is not defined

In my HTML, I have a select box with options generated from an ng-repeat. <select ng-model="pageId" ng-change="setFacebookPage()"> <option ng-repeat="page in pages" ng-value="page.id"> {{page.name}}</option> </select> Everythi ...

Issue with Moment.js amDateFormat consistently displaying date as 1970

Check out this link for more information I've integrated Moment.js and Angular-moment into my application. Oddly enough, all my epoch timestamps are being converted to the same date in 1970. <td class="timespan">{{tag.added_epoch | amDateForm ...

Error: React Js Module Not Found

Encountered a Compilation Failure. The module was not found: Error: An attempt to import ../components/App which exists outside the src/ directory of the project has been made. Relative imports from outside src/ are not permitted. To resolve this issue, c ...

Why does the pi-forall module appear to be included in a JSON parsing demonstration written in Haskell?

I came across a tutorial on parsing JSON in Haskell at After trying to load the file (mentioned below in this interactive listing), the following output is displayed: > ghci GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help Loading package ...

The Jenkins build on a specific branch is encountering issues with path exporting, causing it to fail

I have a set of files related to a new feature that I am trying to deploy through Jenkins. I have successfully deployed other branches in the past, but I am encountering difficulties with a specific branch. https://i.sstatic.net/BKVbH.png I believe the pro ...

Is the presence of a JSON array within a JSON object

When receiving a JSON response, I am dealing with a JSON array containing 80 other JSON objects. Some of these objects also contain JSON arrays. How can I determine if a JSON array is present? I attempted using optJSONArray but it did not yield the desire ...

Leveraging JSON data in subsequent GET request in Ionic 3

My application receives input, concatenates it to a string, and then requests JSON data. The response includes the following first two lines: https://i.sstatic.net/h6YNH.png Now, I need to update my code to be asynchronous. It should make the initial call ...

Issues with the angular html5 mode fallback strategy for handling deep routes are currently unresolved and

I am facing an issue with setting up html5 mode in angular along with node.js where the fallback to hash does not work for routes more than 1 level deep on browsers that do not support html5 mode. Here is my server route to catch all: app.all('/*&ap ...