Get data from Parse.com using AngularJS factory - javascript

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.

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;
}]);

Related

Angularjs undefined err scope variable

Hi folks I'm having some difficulty with angularjs. I have lterally spent the whole day today trying to figure this out! I am new to this and really stuck so hoping someone can help. I'm getting an error 'Cannot read property 'length' of undefined'.. my program has an array of objects '$scope.products' taken from a .json file.. I filter this array to show only those products with
category:'special offers'..
$scope.specialOffers = $filter('filter')($scope.products,{category:"Special
Offers"}, true);
then take the length of this new array and pass it to my randomInt function thereby creating a random integer between 0 and the array length.. but for some reason '$scope.specialOffers' is showing as undefined.. here is the full controller code:
app.controller('ProductsController', ['$scope','$filter', 'productFactory',
'$location', '$routeParams',
function ($scope, $filter, productFactory, $location, $routeParams) {
$scope.path;
$scope.category;
$scope.products;
$scope.rand;
$scope.specialOffers;
$scope.id = $routeParams.id;
specifyCategory();
getProducts();
$scope.specialOffers = $filter('filter')($scope.products,{category:"Special Offers"}, true);
$scope.rand = randomInt($scope.specialOffers.length, 0);
function specifyCategory() {
$scope.path = $location.path();
if ($scope.path == "/products/woodentoys") {
$scope.category = "Wooden Toys"
} else if ($scope.path == "/products/woodenaccessories") {
$scope.category = "Wooden Accessories"
} else if ($scope.path == "/products/specialoffers"){
$scope.category = "Special Offers"
}
}
function getProducts() {
productFactory.getProducts()
.then(function (response) {
$scope.products = response.data;
}, function (error) {
$scope.status = 'unable to load product data ' + error.message;
});
}
function randomInt(max,min){
max++;
return Math.floor((Math.random())*(max-min))+min;
}
}]);
This is my first question on stack overflow so your patience is appreciated
Many thanks in advance!
Without seeing the actual error message, my first guess is that $scope.products is not being set before it is being filtered on. It appears getProducts is returning an asynchronous promise:
function getProducts() {
productFactory.getProducts()
.then(function (response) {
$scope.products = response.data;
}, function (error) {
$scope.status = 'unable to load product data ' + error.message;
});
}
If you haven't tried already, move your accessing of this data within the anonymous callback function.
function getProducts() {
productFactory.getProducts()
.then(function (response) {
$scope.products = response.data;
$scope.specialOffers = $filter('filter')($scope.products, {category:"Special Offers"}, true);
$scope.rand = randomInt($scope.specialOffers.length, 0);
}, function (error) {
$scope.status = 'unable to load product data ' + error.message;
});
}
This is happening because your request to get the products is taking some time, in the mean while you are trying to access $scope.products whilst the request hadn't finished yet which result in showing as undefined
Try applying your filter in the callback of your request or look into using $watch

Single Angular Controller w/ multiple $HTTP Get request

I have two mongoose schemas running in on my server end. I would like to add two $http.get request in my app.js and eventually display two tables from my collection in MongoDB on a webpage. Only one get function is called without errors.
server.js
//Data Schema
var tempSchema = new mongoose.Schema({
topic: String,
message: Number,
when: Date
}, {collection: "temperature"});
var humiditySchema = new mongoose.Schema({
topic: String,
message: Number,
when: Date
}, {collection: "humidity"});
var temperature =mongoose.model('temperature', tempSchema);
var humidity =mongoose.model('humidity', humiditySchema);
app.js
app.controller("FormController", function ($http, $scope){
$http.get("/api/temperature")
.then(function (response) {
$scope.temperatures = response.data;
});
})
app.controller("FormController", function ($http, $scope){
$http.get("/api/humidity")
.then(function (response) {
$scope.humiditys = response.data;
});
})
Also thinking of how I can display both collections on the webpage. Using ng-repeat. Unfortunately I cannot paste my HTML code here.
I would appreciate any help I can get. Thanks
Another way you could handle the $http requests is by creating an Angular Factory.
angular.module('myApp.services',[])
add.factory('ApiService', function($http) {
return {
getHumidity: function() {
return $http.get("/api/humidity");
},
getTemperature: function() {
return $http.get("/api/temperature");
}
}
})
Then inside your controller, you should do the following (Note that you must inject the factory as a dependency for the controller)
angular.module('myApp.controllers',[])
.controller("FormController", function (ApiService, $scope){
function getHumidity() {
var promise = ApiService.getHumidity();
promise.then(
function(response) {
$scope.humiditys = response.data;
},
function(errorPayload) {
console.log(errorPayload);
});
};
function getTemperature() {
var promise = ApiService.getTemperature();
promise.then(
function(response) {
$scope.temperatures = response.data;
},
function(errorPayload) {
console.log(errorPayload);
});
};
getHumidity();
getTemperature();
})
then where you define your angular App (app.js in most of the cases):
angular.module('myApp', ['myApp.controllers','myApp.services'])
.run(...)
.config(...)
...

AngularJS - Why I can't get the JSON by $http.get?

I'm developing a web application in java with NetBeans
using AngularJS.
When I'm accessing my WebService in localhost I'm getting the JSON array with the objects that I need, working very well
BUT
in the controller, I'm not getting the information
Log of the Web browser:
Result: [object Object] OR {} script.js:140:5
Success/Error: undefined
Code:
minhaAplicacao.controller('inicioPacienteCTRL', function ($scope, $http) {
$scope.medicoSelecionado;
var aux;
var $scope.result = window.console.log($http.get("http://localhost:8080/Clinica5/webresources/medicos")).then(function (success) {
aux = success;
}, function (error) {
aux = error;
});
window.console.log("Result: "+$scope.result+ " OR "+JSON.stringify($scope.result));
window.console.log("Success/Error: "+aux);
});
And if I put this code in the view I got an error:
<div ng-bind="$scope.result"></div>
Error: $scope.result is not defined
I have configured the $routeProvider and is absolutely correct
Thanks a lot <3 Big Hug!
You can try in the following way.
minhaAplicacao.controller('inicioPacienteCTRL', function ($scope, $http) {
$scope.functionName = function(){
//define
$scope.medicoSelecionado = {};
$http.get("http://localhost:8080/Clinica5/webresources/medicos").then(function (success) {
console.log(success);
//success data passed
$scope.medicoSelecionado = success;
}, function (error) {
console.log(error);
//error message
$scope.error = error;
});
}
});
And use this html to display error
<div class="error">{{error}}</div>
You need to assign your response to controller scope variable result as a result of asynch request like this
$scope.result = success
MoreOver you can avoid using var when declaring $scope variables
minhaAplicacao.controller('inicioPacienteCTRL', function ($scope, $http) {
$scope.medicoSelecionado;
$scope.aux = {};
$scope.result {};
$http.get("http://localhost:8080/Clinica5/webresources/medicos").then(function (success) {
$scope.result = success;
}, function (error) {
$scope.aux = error;
});
window.console.log("Result: ",$scope.result, " OR ",JSON.stringify($scope.result));
window.console.log("Success/Error:",$scope.aux);
});
also in view
<div ng-bind="result"></div>
no need of $scope
You have to define var aux = {} because if you not defined anything then it will show undefined
and you are getting object in success so that it is showing [object, object]
minhaAplicacao.controller('inicioPacienteCTRL', function ($scope, $http) {
$scope.medicoSelecionado;
var aux = {};
var $scope.result = window.console.log($http.get("http://localhost:8080/Clinica5/webresources/medicos")).then(function (success) {
aux = success;
}, function (error) {
aux = error;
});
window.console.log("Result: "+$scope.result+ " OR "+JSON.stringify($scope.result));
window.console.log("Success/Error: "+aux);
});
Try <div ng-model="result"></div> for the second error.
And no need to do:
$scope.medicoSelecionado;
$scope.aux;
$scope.result;
Just use a new model when you need one; no declaration is needed.
Doing $scope.result = successin your .then() should be fine, as suggested by Vinod Louis.
The way I would do it:
minhaAplicacao.controller('inicioPacienteCTRL', function ($scope, $http) {
$http.get("http://localhost:8080/Clinica5/webresources/medicos")
.then(function (success) {
$scope.result = success;
}, function (error) {
$scope.result = error;
});
window.console.log("Result: "+$scope.result+ " OR "+JSON.stringify($scope.result));
});
What does aux do by the way?
GOT!
For some reason was having conflict with the route 'login'. I don't known why.
The solution was deleting the redirectTo line
when('/inicioMedico', {
templateUrl: 'inicioMedico.html',
controller: "inicioMedicoCTRL"
}).
otherwise ({
// redirectTo: 'login' ERROR HERE
});

Angular promise service as global dataservice

I'am not pro in Angular and am still lerning. Hope I get some help here.
I want to build an App with different views. I need to detect the browser and also fetch some data from a server. For this I created a service, where I do this work.
My desire is to use the data of the service all views. How is proper way to store and cache the data so that I can use it in all my Views/Controllers?
Here is what I got so far.
My Service:
.factory('DataService', function($http, $q, $timeout) {
var data = { };
return {
notes: function() {
// This exposed private data
return data;
},
addItem: function(itemname, itemvalue) {
// This is a public function that modifies private data
data[itemname] = itemvalue;
}
getPlatform: function() {
var getPlatformData = function() {
var deferred = $q.defer();
BrowserDetect.init();
deferred.resolve(BrowserDetect.OS);
return deferred.promise;
};
return {
getPlatformData: getPlatformData
};
},
getServerData: function() {
//if(!data.getServerData){
var getData = function() {
var deferred = $q.defer();
$http({
url: 'js/fakeGet.json',
method: 'get',
dataType: 'json',
}).success(function(data) {
data.scanResponse = data;
deferred.resolve(data);
})
return deferred.promise;
};
return {
getData: getData
};
//}
// return data.scanResponse;
}
};
});
My controller:
DataService.getPlatform().getPlatformData().then(function(platform) {
console.log('Another browserDetect request');
$scope.platform = platform;
DataService.addItem("platform", $scope.userPlatform);
});
First of all, as nordyke mentioned in his answer, you'd better split the service to smaller ones.
Second, you're asking for how to caching the data, and since you're using promise, $q.when() is what you need. I will take the getPlatform as an example to get you started:
.factory('DataService', function($http, $q, $timeout) {
var os; // this variable is used to store the result
return {
getPlatform: function() {
var getPlatformData = function() {
if (!os) { // no previous data available, look into other service to fetch the data
var deferred = $q.defer();
BrowserDetect.init();
os = BrowserDetect.OS; // store data
deferred.resolve(os);
return deferred.promise;
}
return $q.when(os); // there is previous data, return it as promise
};
return {
getPlatformData: getPlatformData
};
}
};
});
In this way, OS information is cached, and
DataService.getPlatform().getPlatformData().then(function(platform) {
...
});
will only fetch the platform information once during the life-time of the DataService. You can apply the same idea to getServerData as well to cache the data from the server.
Caching your data in a service singleton is a good approach, and I like your straightforward implementation of it. My only recommendation would be to split up your 3 concerns into separate services.
Browser Detection
Server Requests (which will be split up even more once you have more requests.)
Data Caching

Angularjs controller assigning variable from service, before service method returns

I'm trying to use a service to get a user's profile information to display in the header of my template.
The problem is that my variable in my controller is getting set before the service actually returns anything (or at least it seems that way).
app.js
// This gets the basic information that is needed for every page
myapp.service('base', function($http) {
this.getProfile = function() {
// Get the logge din users info
$http.get(baseUrl+'v1/users?api_key=1234')
.success(function(response) {
console.log('base response = '+response);
return response;
})
}
});
profile.js
myapp.controller('ProfileController', ['$scope', '$http', 'base', function($scope, $http, base) {
base.getAuthHeader();
$scope.profile = base.getProfile();
console.log('$scope.profile = '+$scope.profile);
}]);
In my firebug, this is the output in this exact order:
$scope.profile = undefined
base repose = [object Object]
How is the line console.log('$scope.profile = '+$scope.profile); getting called before console.log('base response = '+response);?
You need to be using callback.
myapp.service('base', function($http) {
this.getProfile = function() {
// Get the logge din users info
$http.get(baseUrl+'v1/users?api_key=1234')
.success(function(response) {
// this code is async
// it wont fire as a part of the execution block
// but rather on its own once the `$http.get` returns
console.log('base response = '+response);
return response; // also this return returns
// the .success function not the .getProfile function
})
}
});
with callbacks your code would look something like this:
myapp.service('base', function($http) {
// accept a function as an argument
this.getProfile = function(callback) {
// Get the logge din users info
$http.get(baseUrl+'v1/users?api_key=1234')
.success(function(response) {
console.log('base response = '+response);
// fire that function when response is available
callback(response);
})
}
});
then in the controller
myapp.controller('ProfileController', ['$scope', '$http', 'base', function($scope, $http, base) {
base.getAuthHeader();
base.getProfile(function(response){
$scope.profile = response;
console.log('$scope.profile = '+$scope.profile);
});
}]);
Or you could handle the async nature with promises instead of callbacks.

Categories