-1

I'm developing an e-commerce web app using AngularJS (v1.6.7) and Parse Server (v2.3.3).

I created Category and Product class in Parse Server. I'm trying to fetch in a certain amount of products per category.

For example, in homepage, 20 products will be retrieved per category. The amount of products changes in other pages.

I want to do it using a factory that fetches given amount of products in any category (amount and category of products will be passed to the function as parameters). So I'll be able to reuse it inside other controllers.

ProductsFactory factory:

sebetimapp.factory('ProductsFactory', ['$q', function($q){
    Parse.initialize('MY_APP_ID', 'JS_KEY');
    Parse.serverURL = 'https://parseapi.back4app.com/';

    let fac = {};

    fac.getProducts = function(cat, lmt) {
        let Category = Parse.Object.extend('Category'),
            qr = new Parse.Query(Category);

        qr.get(cat, {
            success: function (res) {
                let product_dfd = $q.defer(),
                    Product = Parse.Object.extend('Product'),
                    query = new Parse.Query(Product);

                query.include('category');

                query.equalTo('category', res);

                if (lmt) {
                    query.limit(lmt);
                }

                query.find({
                    success: function(results) {
                        product_dfd.resolve(results);
                    },
                    error: function(err) {
                        product_dfd.reject(results);
                    }
                });

                return product_dfd.promise;
            },
            error: function(object, error) {
                //
            }
        });
    };

    return fac;
}]);

productsCtrl controller:

sebetimapp.controller('productsCtrl', ['$scope', '$log', '$location', '$q', 'ProductsFactory', function($scope, $log, $location, $q, ProductsFactory) {
    let params = $location.search(); // To grab category ID from URL.

    ProductsFactory.getProducts(params.cat, 20).then(function(response) {
        $log.log('Successfully retrieved products.');
    }, function(error) {
        $log.log('Unable to get products.');
    });
}]);

When I execute it, an error occurs:

TypeError: Cannot read property 'then' of undefined

But if I don't use this factory and define getProducts() function inside my controller, it works fine.

Why is this happening? I'm new to AngularJS. Any help would be appreciated.

1
  • you are creating and returning the $q promise in inner function not in getProducts. Would also imagine that Parse has promises also Commented Jan 9, 2018 at 12:02

1 Answer 1

1

The .then() method is only available on Promises. Your function appears to be not returning anything (and hence, .then() is unavailable).

This might help:

sebetimapp.factory('ProductsFactory', ['$q', function($q) {
  Parse.initialize('MY_APP_ID', 'JS_KEY');
  Parse.serverURL = 'https://parseapi.back4app.com/';
  var fac = {};
  fac.getProducts = function(cat, lmt) {
    var Category = Parse.Object.extend('Category'),
      qr = new Parse.Query(Category);
    return qr.get(cat)
      .then(function(res) {
        var Product = Parse.Object.extend('Product'),
          query = new Parse.Query(Product);
        query.include('category');
        query.equalTo('category', res);
        if (lmt) {
          query.limit(lmt);
        }
        return query.find();
      });
  };
  return fac;
}]);

Most methods in the Parse JS API return promises. You can use those directly (and not use the success and error callbacks). It's been ages since I worked on Parse (I thought it was no longer available) so you may have to figure out the details yourself.. Handy Link: http://docs.parseplatform.org/js/guide/#promises

TLDR; Your factory function needs to return a promise but is returning nothing and hence .then() is unavilable

EDIT: Here is another way to the same thing with minimal changes to you original code (this is not the best way to do this, however)

sebetimapp.factory('ProductsFactory', ['$q', function($q) {
  Parse.initialize('MY_APP_ID', 'JS_KEY');
  Parse.serverURL = 'https://parseapi.back4app.com/';
  var fac = {};
  fac.getProducts = function(cat, lmt) {
    var Category = Parse.Object.extend('Category'),
      qr = new Parse.Query(Category),
      // Move the deffered object out of the inner function
      product_dfd = $q.defer();
    qr.get(cat, {
      success: function(res) {
        var Product = Parse.Object.extend('Product'),
          query = new Parse.Query(Product);
        query.include('category');
        query.equalTo('category', res);
        if (lmt) {
          query.limit(lmt);
        }
        query.find({
          success: function(results) {
            product_dfd.resolve(results);
          },
          error: function(err) {
            product_dfd.reject(results);
          }
        });
      },
      error: function(object, error) {}
    });
    // Return the deferred object
    return product_dfd.promise;
  };
  return fac;
}]);

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

2 Comments

I tried your first snippet and it worked. Why is it better than second one?
The first one is potentially cleaner and more readable because it uses only promises. Mixing promises with the callback pattern usually creates codebases which are hard to read/reason about. It's preferable to stick with only promises or use only callbacks in general in the interest of consistency. Also, promises would be the better option as they were invented to deal with "Callback Hell". I would recommend reading up on Promises and using them correctly ( I can vouch for them in that learning about them has made my code a lot cleaner and simpler)

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.