0

I am having a problem displaying product information from the database using an AngularJS factory. Basically its a shopping cart application that I modified to display the products from the database instead of a hard coded array.

Heres the main application file source code (app.js) :

'use strict';

var shop = angular.module('shop', []);

// create a data service that provides a store and a shopping cart that
// will be shared by all views (instead of creating fresh ones for each view)
shop.factory("DataService",['$http', function($http) {
    // create store
    var myStore = new store($http);

    // return data object with store and cart
    return {
       store: myStore
       //cart: myCart ignore for now
    }
}]);

// adding the config on the module
shop.config(function($routeProvider) {
      $routeProvider // angular object, injected dynamically
        .when('/', // we show the store on the root
          {
            controller: 'StoreController',
            templateUrl: 'partials/store.htm'
          })
        .when('/cart',
          {
            controller: 'CartController',
            templateUrl: 'partials/cart.htm'
          })
        .when('/checkout',
          {
            controller: 'CheckoutController',
            templateUrl: 'partials/checkout.htm'
          })
       .when('/invoice',
          {
            controller: 'InvoiceController',
            templateUrl: 'partials/invoice.htm'
          })
       .otherwise({ redirectTo: '/' }); // store
    });

var controllers = {};
controllers.StoreController = function($scope, $routeParams, DataService) {
  $scope.store = DataService.store;
  console.log($scope.store);
  //$scope.cart = DataService.cart;
}

The store source code (store.js) where I retrieve the data :

function store($http) {
  this.products = [];
  this.url = 'php/db.php';
  this.fields = [
    'product_id',
    'product_name', 
    'product_description', 
    'product_price'
  ];
  this.products = this.getProducts($http);
}

store.prototype.getProducts = function($http) {
  $http.post(this.url, { "action" : 'products', "fields" : this.fields })
    .success(function(data, status) {
      //$scope.result = data; // Show result from server in our <pre></pre> element
      //return data;
      this.products = data;
    })
    .error(function(data, status) {
      //return data || "Request failed";
      //this.status = status;        
      this.products = [];
  }); 
}

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

Can anybody tell me what am I doing wrong here?

  • Why cant I set my this.product variable to the database result?
  • I would also like to extend the products class with more functions to save items to the database, how would I go about doing that?

Any advice much appreciated.

Regards

UPDATE

I have added the app.js code below, I am having an issue with the controller (StoreController) when accessing the data from the store class (store.js). It still shows an empty array. I have changed my code as suggested by m.e.conroy.

app.js

'use strict';

var shop = angular.module('shop', []);


shop.factory("DataService",['$http', '$q', function($http, $q) {
    // create store
    var myStore = new store($http, $q);
    return {
       store: myStore      
    }
}]);


// adding the config on the module
shop.config(function($routeProvider) {
      $routeProvider // angular object, injected dynamically
        .when('/', // we show the store on the root
          {
            controller: 'StoreController',
            templateUrl: 'partials/store.htm'
          })
        .when('/cart',
          {
            controller: 'CartController',
            templateUrl: 'partials/cart.htm'
          })
        .when('/checkout',
          {
            controller: 'CheckoutController',
            templateUrl: 'partials/checkout.htm'
          })
       .when('/invoice',
          {
            controller: 'InvoiceController',
            templateUrl: 'partials/invoice.htm'
          })
       .otherwise({ redirectTo: '/' }); // store
    });

var controllers = {};
controllers.StoreController = function($scope, $http, $routeParams, DataService) {
  $scope.store = DataService.store;              
}

shop.controller(controllers); 
4
  • DataService.store return a promise, so you need to resolve to catch the value. And it seems that you store function doesn't return the result object... you need to return this object. Can you create a plunker example with this full store factory implementation? Commented Oct 24, 2013 at 10:54
  • Look this nice example of angularjs app github.com/andreev-artem/angular_experiments/tree/master/… and here jsfiddle.net/u5gV2 for simple memory cache. Commented Oct 24, 2013 at 10:56
  • the data which is returned is undefined. in the code I set the this.products variable to that which is returned in the success handler. Commented Oct 24, 2013 at 11:19
  • You don't need to set this.products = this.getProducts($http) if you're going to set this.products in the success or error functions of the $http.post You're not even returning anything from getProducts so this.products is being set to the definition of the function. Commented Oct 24, 2013 at 13:34

1 Answer 1

1
function store($http) {
    this.products = [];
    this.url = 'php/db.php';
    this.fields = ['product_id', 'product_name', 'product_description', 'product_price'];
    this.products = this.getProducts($http);
}

store.prototype.getProducts = function ($http) {
    return $http.post(this.url, {
        "action": 'products',
        "fields": this.fields
    })
    .success(function (data, status) {
        return data;
    })
    .error(function (data, status) { 
        return [];
    });
}

When you return $http.post you're returning the promise it creates, so this.products would contain the $http promise. When the call returns from the server the promise is resolved by the success or error functions returning data in those functions sets the variable this.products to what is returned.

In this case this.products = [] is getting replaced right away by the promise from $http. If you try to access that data in your application before the resolve happens you'll get back the promise it contains which could cause problems if you try and use it in other functions as if it contained the array you need. You can use $q.when to "wait" on the promise to resolve and then assign the returned data, so if you try to use this.products elsewhere before then it will still contain an empty array and thus code like this.products.length will still work instead of throwing an error. So you could do the following:

function store($http,$q) {
    this.products = [];
    this.url = 'php/db.php';
    this.fields = ['product_id', 'product_name', 'product_description', 'product_price'];
    $q.when(this.getProducts($http)).then(function(data){ this.products = data; });
}

If you do decide to go this route, just take note that you'll need to inject $q into your service and then pass it on through your new operator during class creation. This would resolve any race situation you may have in your controller.

Of course you could also use the resolve provided for the .when method of the $routeProvider to resolve and controller dependencies prior to the controller taking "control"

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for your answer, I have changed my code and updated the question with my controllers. Just need a little more direction. Thanks!
Did you change your store.js as well to add in $q?
I did some testing and while in my example the this.getProducts for the store gets called and returns the proper value in the $q statement the value in $scope.store in the controller never gets set. I have a feeling that doing it this way won't work during object creation. I suggest going the pure Angular route and using a factory service as your object store (service).
Thanks! I will have a look. I appreciate all your help.
Thanks for the guidance and advice, marking the question as answered.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.