Angularjs undefined err scope variable - javascript

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

Related

Get data from Parse.com using AngularJS factory

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

Angular Unshift error when submit data to mongodb

When I submit data to MongoDB angular unshift keyword gives this error. I don't know much about Angular. Why does it give me this error?
TypeError: Cannot read property 'unshift' of undefined
Here is my code which gives this error:
var app=angular.module('app',[]);
app.controller('PostsCtrl', function($scope, $http){
$http.get('/api/posts')
.then(function(response) {
$scope.posts = response.post;
alert(JSON.stringify(response));
});
$scope.addPost=function(){
if($scope.postBody){
$http.post('/api/posts',{
username:'Tahir',
body:$scope.postBody
}).then(function successCallback(response) {
$scope.posts.unshift(post)
$scope.postBody=null
alert(JSON.stringify(response));
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
})
}
}
}
You got this error for this code $scope.posts.unshift(post) because of $scope.posts is undefined so you should define first $scope.posts.
can try this one
var app=angular.module('app',[]);
app.controller('PostsCtrl', function($scope, $http){
$scope.posts = [];
$http.get('/api/posts')
.then(function(response) {
// if response.post is single post then should push on posts
// $scope.posts.push(response.post);
// if got array then
$scope.posts = response.post ? response.post : [];
alert(JSON.stringify(response));
});
$scope.addPost=function(){
if($scope.postBody){
$http.post('/api/posts',{
username:'Tahir',
body:$scope.postBody
}).then(function successCallback(response) {
$scope.posts.unshift(post)
$scope.postBody=null
}, function errorCallback(response) {
})
}
}
}
The $scope.posts is undefined.
You can solve it like this
$scope.posts = $scope.posts || [];
$scope.posts.unshift(post);
the first line checks assign empty array if the posts is null

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 $http.get not passing parameters

This seems like a simple problem, and I must be overlooking something small.
I have a function that accesses Spotify API and searches for an artist. I know that accessing this route via a normal URL returns a result. (e.g. http://localhost:3001/search?artist=%27Linkin%20Park%27) Here the code that does that:
router.get('/search', function(req, res, next)
{
var artist = req.param('artist');
console.log("Artist: " + artist);
smartSpot.getArtistID(artist, function(data)
{
console.log("Data: " + data);
res.json(data.id);
});
});
Then, there is the code on the front end to search for the artist. This is all done via angular.
angular.module('smart-spot', [])
.controller('MainCtrl', [
'$scope', '$http',
function($scope, $http)
{
$scope.createPlaylist = function()
{
var artist = $scope.artist;
console.log(artist);
window.open("/login", "Playlist Creation", 'WIDTH=400, HEIGHT=500');
return $http.get('/search?=' + $scope.artist) //this doesn't pass in the artist
.success(function(data)
{
console.log(data);
});
}
}
]);
The $http.get() does not pass in the $scope.artist` value properly.
Looks like you might be missing the "artist" query param in your string concatenation.
$http.get('/search?artist=' + $scope.artist)
Alternatively, you could pass the artist as a query param.
function createPlaylist() {
return $http.get('/search', { params : { artist : $scope.artist } })
.then(function(response) {
return response;
}, function(error) {
return $q.reject(error);
});
}
Also, I would avoid using .success. I believe that's depreciated in favor of the syntax above. First param is success function, second is fail function.
you can pass parameters via
$http.get('/search', {
params: {
artist: $scope.artist
}
})
.success(function(data)
{
console.log(data);
});

Angular - $routeParams.itemID for loading domain.com/:itemID ALMOST WORKING?

I am looking to have parameters in the route, by using a colon before the variable name
// dynamic pages for each ITEM, once selected
// from $routeParams.itemID in ItemCtrl
.when('/:itemID', {
templateUrl: 'views/item.html',
controller: 'ItemController'
})
When a div-box is clicked, Angular should route to the specific item
<div class="itemBox" ng-click="getItem(item._id)">
Right now, the call to the node/express API seems to be working
[16:36:18.108] GET http://localhost:8080/api/items/534240001d3066cc11000002 [HTTP/1.1 304 Not Modified 4ms]
But this error logs in the console:
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (http.js:691:11)
...
at Promise.<anonymous> (/Users/Username/Downloads/project/v19/app/routes.js:41:8)
...
Line 41 (for 41:8?) in routes.js is res.json(item);
// load the item model
var Item = require('./models/item');
// get One item
app.get('/api/items/:item_id', function(req, res) {
// use mongoose to get the one item from the database
Item.findById({
_id : req.params.item_id
},
function(err, item) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
res.send(err)
res.json(item); // return the item in JSON format
});
});
Though it seems like the issue might be in the Controller because all of the other API calls work.. And so I tried passing $routeParams all over the place!
angular.module('ItemCtrl', [])
// inject the Item service.factory into our controller
.controller('ItemController', function($scope, $routeParams, $http, Items, isEmptyObjectFilter) {
// get an Item after clicking it
$scope.getItem = function(id, $routeParams) {
Items.getOne(id, $routeParams)
// if successful getByID, call our function to get the Item data
.success(function(data, $routeParams) {
// assign our Item
$scope.item = data;
// for use with a parameter in appRoutes.js using itemID as the variable
$scope.itemID = $routeParams.itemID;
})
.error(function(data) {
console.log('Error: ' + data);
});
};
});
Or maybe it's the service? Does this need to pass $routeParams as function(id, $routeParams)
angular.module('ItemService', [])
// super simple service
// each function returns a promise object
.factory('Items', function($http) {
return {
get : function() {
return $http.get('/api/items');
},
getOne : function(id) {
return $http.get('/api/items/' + id);
},
create : function(itemData) {
return $http.post('/api/items', itemData);
},
delete : function(id) {
return $http.delete('/api/items/' + id);
}
}
});
Would really appreciate some assistance debugging this.. Thanks
It looks like you are getting the data correctly. The problem is that you want the route to change after successfully getting the API call?
$routeParams won't change the route for you. That just gets the data. Use $location to change the route.
.controller('ItemController', function($scope, $routeParams, $location, $http, Items, isEmptyObjectFilter) {
$scope.getItem = function(id) {
Items.getOne(id)
.success(function(data) {
$scope.item = data;
$scope.itemID = $routeParams.itemID;
// redirect
$location.path('/' + $routeParams.itemID);
});
});
});
Since all of your data seems to be ready to go, you just need Angular to redirect to the route. $location should be the way to go.
That message is because you are getting an error and executing the res.send() method, and after that you have res.json(), express is trying to respond twice.
Try changing:
if (err)
res.send(err)
To:
if (err) {
res.json({ error: err });
} else {
var object = item.toObject();
res.json(object);
}
Angular resource example:
angular.module('ItemService')
.factory('Items', ['$resource', function($resource) {
return $resource('/api/items/:itemID', {
itemID: '#_id'
}, {
update: {
method: 'PUT'
}
});
}]);
Now you can do this in your controller:
// Find
Items.get({
itemID: $routeParams.itemID
}, function(item) {
$scope.item = item;
});
// Update
$scope.item.name = 'New name';
$scope.item.$update();
// Remove
$scope.item.$remove();

Categories