$http.get(url) not returning data - angularjs

I am building a service in angular and injecting the service in controller. I am trying to fetch data from json file and am using $http. however the data is not getting returned and i get undefined.
I am updating my code as per suggestion by #Phil
Service.js
;(function(app) {
app.factory('authService', ['$log', '$http','$location', function($log, $http,$location) {
var url = 'js/user.json';
var authService= {};
var userExist=null;
authService.authenticate = function(userid) {
var userobj = $http.get(url).success(function (data) {
userExist = data
console.log(data);
return userExist;
$log.info("Loaded users");
})
.error(function (error) {
$log.info(error);
$log.info("No user exists");
return error;
})
return userobj;
}
return authService;
}]);
})(angular.module('userApp'));
Controller.js
;(function(app) {
app.controller('Controller', ['$scope', '$log','$location','authService', function($scope,$log,$location,authService) {
$scope.data={};
$scope.getUsers = function()
{
userid = "123";
$scope.data = authService.authenticate(userid);
console.log($scope.data);
return $scope.data ;
}
}])
}(angular.module('userApp')));
index.html
<div class="main" ng-controller="Controller">
<input type="button" name="btngetusers" ng-click="getUsers()"></input>
</div>
<script src ="js/app.js"> </script>
<script src ="js/controller/Controller.js"> </script>
<script src ="js/services/Service.js"> </script>
user.json
i have placed the json file under the js directory.
[
{
"UserId": "1",
"FName": "Hice",
"LastName": "Harry"
},
{
"UserId": "2",
"FName": "Andrew",
"LastName": "Ads"
}
]
The data is getting returned as undefined. what am i missing here?
UPDATED CODE
I am updating my code as per suggestion by #skubsi
Service.js
;(function(app) {
app.factory('authService', ['$log', '$http','$location', function($log, $http,$location) {
var url = 'js/user.json';
var authService = {};
var userExist=null;
authService.authenticate = function(userid,success,error) {
$http.get(url).success(function(data){
success(data);
})
.error(error);
};
return authService;
}]);
})(angular.module('userApp'));
Controller.js
;(function(app) {
app.controller('MainController', ['$scope', '$log','$location','authService', function($scope,$log,$location,authService) {
var self = this;
this.data = null;
this.getUsers = function(){
function success(response){
self.data = response;
}
function error(){
console.log("error");
}
authService.authenticate(1,success,error);
}
}])
}(angular.module('userApp')));
index.html
<div class="main" ng-controller="MainController as main">
{{main.data}}
<input type="button" name="btngetusers" value ="Get User" ng-click="main.getUsers()"></input>
</div>
<script src ="js/app.js"> </script>
<script src ="js/controller/MainController.js"> </script>
<script src ="js/services/authenticate.js"> </script>

First things first: your JSON is invalid, you can verify this yourself by entering the JSON you supplied in JSONLint.
Parse error on line 2:
[ { UserId: 123,
--------------^
Expecting 'STRING', '}'
Secondly you pass a unknown service into your controller:
authenService
Then you should realize a promise is code that will run asynchronously, meaning that:
userid = "123";
$scope.data = authService.authenticate(userid);
console.log($scope.data);
return $scope.data ;
will not run synchronously. console.log($scope.data); Will be executed long before your authenticate method will be done. So you need to find a way to make your controller handle accordingly whilst keeping concerns separated. (and not falling into a deferred-anti-pattern).
You could for example add additional parameters to your authenticate function, which will enable the function to call back the original caller.
authService.authenticate = function(userid, success, error) { //success and error are functions
$http.get(url).success(function(data) {
//separation of concerns:
//execute logic.. set flags, filter w/e belongs to your authentication process.
success(data);
})
.error(error); //no processing required
};
So that in your controller all that is left to do is calling the authService and providing it a way to set your data:
this.getUsers = function() {
//This will enable to set the response to your controllers data model.
function success(response) {
self.data = response;
window.alert(response);
}
function error() {
window.alert('shite happened');
}
authService.authenticate(1, success, error);
};
Note that I have used the controllerAs syntax instead of $scope. To prove this mechanism works I created a plunker for you to investigate.

Your authenticationService.authenticate method doesn't return anything.
Specifically, the service name is authService and you're calling authenticationService.authenticate.

Related

AngularJS: Service not returning data

I have been trying to get data from a json file through service in AngularJS(1.5.3). I am unable to retrieve the data from json and display it in the view. I get blank values instead. Below is the code:
//Service
angularStoreApp.factory('dataService', ['$http', function ($http, $q) {
return {
getProducts: function () {
var promise = $http.get('/api/json/products.json')
.then(function successCallback(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
return "Invalid data";
}
}, function errorCallback(response) {
return "Invalid data";
})
return promise;
}
};
}]);
//Controller
/// <reference path="SearchController.js" />
angularStoreApp.controller('SearchCtrl', ['$scope', 'dataService', function ($scope, dataService) {
$scope.ProductItems = [];
dataService.getProducts()
.then(function (data) {
$scope.ProductItems = data;
});
}]);
<blockquote>
<h4 style="color: salmon">Welcome to the New Store
<br />
Please select the products you want and add them to your shopping cart.
<br />
When you are done, click the shopping cart icon to review your order and checkout.
<br />
</h4>
<div class="row">
<div class="col-sm-12" ng-repeat="ProductItem in ProductItems track by $index">
{{ProductItem.Name}}, {{ProductItem.Price}}
</div>
</div>
Update:
Below is the json data that I have.
[{
"itemName": "Notepad",
"itemPrice": 12,
"itemQuantity": 0
},
{
"itemName": "Pen",
"itemPrice": 8,
"itemQuantity": 0
},
{
"itemName": "Pencil",
"itemPrice": 5,
"itemQuantity": 0
}];
Could anyone help me out.
I think the problem is in your first line!
you should pass all the services as string:
angularStoreApp.factory('dataService', ['$http', '$q' function ($http, $q) {
In order to get the code snippet working, you need to
initialize the module in the js: var angularStoreApp = angular.module('storeapp', []);
add ng-app in the view
add ng-controller in the view (or use routing)
Forgetting to add $q to the dependencies is indeed a mistake, but it doesn't prevent your app from working, as long as you don't use $q.
Below is an adjusted, working code snippet. I simulated the http call by returning json wrapped in a promise. If it still doesn't work, the problem is the HTTP call, i.e. the part I commented out. Execute the call in the browser and verify that the correct JSON is returned.
var angularStoreApp = angular.module('storeapp', []);
//Service
angularStoreApp.factory('dataService', ['$http', '$q',
function($http, $q) {
return {
getProducts: function() {
//simulate promise with json:
return $q.when([{
'Name': 'name 1',
'Price': 1.23
}, {
'Name': 'name 2',
'Price': 4.56
}]);
//var promise = $http.get('/api/json/products.json')
// .then(function successCallback(response) {
// if (typeof response.data === 'object') {
// return response.data;
// } else {
// invalid response
// return "Invalid data";
// }
// }, function errorCallback(response) {
// return "Invalid data";
// })
// return promise;
}
};
}
]);
//Controller
/// <reference path="SearchController.js" />
angularStoreApp.controller('SearchCtrl', ['$scope', 'dataService',
function($scope, dataService) {
$scope.ProductItems = [];
dataService.getProducts()
.then(function(data) {
$scope.ProductItems = data;
});
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="storeapp" ng-controller="SearchCtrl">
<blockquote>
<h4 style="color: salmon">Welcome to the New Store
<br />
Please select the products you want and add them to your shopping cart.
<br />
When you are done, click the shopping cart icon to review your order and checkout.
<br />
</h4>
<div class="row">
<div class="col-sm-12" ng-repeat="ProductItem in ProductItems track by $index">
{{ProductItem.Name}}, {{ProductItem.Price}}
</div>
</div>
</div>
I've replicated your code in jsbin and it is working fine through I've changed the code for brevity.
The issue might be with below code as it looks for products.json in root folder not in your project folder.
$http.get('/api/json/products.json')
Try with removing '/' in the URL.
$http.get('api/json/products.json')
I found the answer after reading the following links
http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/
http://bguiz.github.io/js-standards/angularjs/resolving-promises-for-a-controller/
The following is how I modified my code to get it working:
In the config routing section I used the resolve property for that route
var angularStoreApp = angular.module('AngularStore', ['ngRoute']);
angularStoreApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/products', {
templateUrl: '/Views/Store.html',
controller: 'SearchCtrl',
resolve: {
resolvedJSON: function (dataService) {
return dataService.getProducts();
}
}
})
.when('/product/:productName', {
templateUrl: '/Views/product.html',
controller: 'ProductCtrl'
})
.otherwise({
redirectTo: '/products'
});
}]);
Now I injected the resolvedJSON in my controller as follows
/// <reference path="SearchController.js" />
angularStoreApp.controller('SearchCtrl', ['$scope', 'resolvedJSON', function ($scope, resolvedJSON) {
$scope.ProductItems = [];
$scope.ProductItems = resolvedJSON;
}]);
And my service code as follows:
angularStoreApp.factory('dataService', ['$http', function ($http, $q) {
return {
getProducts: function () {
return $http.get('/api/json/products.json')
.then(function successCallback(response) {
return response.data;
}, function errorCallback(response) {
return "Invalid data";
})
}
};
following is my json data
[{
"itemName": "Notepad",
"itemPrice": 12,
"itemQuantity": 0
},
{
"itemName": "Pen",
"itemPrice": 8,
"itemQuantity": 0
},
{
"itemName": "Pencil",
"itemPrice": 5,
"itemQuantity": 0
}]
My view(Store.html) is something like this:
<div class="row">
<div class="col-sm-12" ng-repeat="ProductItem in ProductItems track by $index">
{{ProductItem.itemName}}, {{ProductItem.itemPrice}}
</div>
</div>
Hope this helps anyone facing a similar issue as mine. Thank you everyone who steered me to a better and simple answer.
u forgot the '$q' for minification
u should use the var deferred = $q.defer();// (I will add a full code)
example jsfiddle
angular.module('app', [])
.controller('ctrl', function(dataService, $scope) {
var vm = this;
$scope.ProductItems = [];
dataService.getProducts()
.then(function(data) {
$scope.ProductItems = data;
});
})
.factory('dataService', ['$http', '$q', function($http, $q) {
function getProducts() {
var deferred = $q.defer();
$http.get('https://httpbin.org/get')
.then(function successCallback(response) {
if (typeof response.data === 'object') {
// let say we get list of Products
deferred.resolve([{
id: 1,
name: 'Product1'
}, {
id: 2,
name: 'Product2'
}, {
id: 3,
name: 'Product3'
}]);
} else {
// invalid response
return "Invalid data";
}
}, function errorCallback(response) {
return deferred.reject("Invalid data");
})
return deferred.promise;
}
return {
getProducts: getProducts
};
}]);
I think one of the problems you have is already mentioned #Ahmad Mobaraki ,and another one is in your dataService, the promise object returned by $http.get('/api/json/products.json') is already consumed by then() function, so what you need to do is to create a new promise and return it.
Code example:
//service
angularStoreApp.factory('dataService', ['$http', '$q', function ($http, $q) {
return {
getProducts: function () {
var defered = $q.defer();
$http.get('/api/json/products.json')
.then(function successCallback(response) {
if (typeof response.data === 'object') {
defered.resolve(response.data);
} else {
// invalid response
defered.reject("Invalid data");
}
}, function errorCallback(response) {
defered.reject("Invalid data");
});
return defered.promise;
}
};
}]);
//Controller
angularStoreApp.controller('SearchCtrl', ['$scope', 'dataService', function ($scope, dataService) {
$scope.ProductItems = [];
dataService.getProducts()
.then(function (data) {
$scope.ProductItems = data;
});
}]);
hope this can help you :)

How to get the length of an array without ngRepeat

I'm trying to count the items in an array without using ng-repeat (I don't really need it, i just want to print out the sum).
This is what I've done so far: http://codepen.io/nickimola/pen/zqwOMN?editors=1010
HTML:
<body ng-app="myApp" ng-controller="myCtrl">
<h1>Test</h1>
<div ng-cloak>{{totalErrors()}}</div>
</body>
Javascript:
angular.module('myApp', []).controller('myCtrl', ['$scope', '$timeout', function($scope) {
$scope.tiles= {
'data':[
{'issues':[
{'name':'Test','errors':[
{'id':1,'level':2},
{'id':3,'level':1},
{'id':5,'level':1},
{'id':5,'level':1}
]},
{'name':'Test','errors':[
{'id':1,'level':2,'details':{}},
{'id':5,'level':1}
]}
]}
]}
$scope.totalErrors = function() {
if ($scope.tiles){
var topLevel = $scope.tiles.data
console.log (topLevel);
return topLevel[0].issues.map(function(o) {
return o.errors.length
})
.reduce(function (prev, curr){
return prev + curr
})
}
}
}]);
This code works on codepen, but on my app I get this error:
Cannot read property '0' of undefined
and if I debug it, topLevel is undefined when the functions is called.
I think it is related to the loading of the data, as on my app I have a service that looks like this:
angular.module('services', ['ngResource']).factory('tilesData', [
'$http', '$stateParams', function($http, $stateParams) {
var tilesData;
tilesData = function(myData) {
if (myData) {
return this.setData(myData);
}
};
tilesData.prototype = {
setData: function(myData) {
return angular.extend(this, myData);
},
load: function(id) {
var scope;
scope = this;
return $http.get('default-system.json').success(function(myData) {
return scope.setData(myData.data);
}).error(function(err) {
return console.error(err);
});
}
};
return tilesData;
}
]);
and I load the data like this in my controller:
angular.module('myController', ['services', 'ionic']).controller('uiSettings', [
'$scope', '$ionicPopup', '$ionicModal', 'tilesData', function($scope, $ionicPopup, $ionicModal, tilesData) {
$scope.tiles = new tilesData();
$scope.tiles.load();
$scope.totalErrors = function() {
debugger;
var topLevel;
topLevel = $scope.tiles.data;
console.log(topLevel);
return topLevel[0].issues.map(function(o) {
return o.errors.length;
}).reduce(function(prev, curr) {
return prev + curr;
});
};
}
]);
but I don't know what to do to solve this issue. Any help will be really appreciated. Thanks a lot
The $http.get() method is asynchronous, so you can handle this in your controller with a callback or a promise. I have an example using a promise here.
I've made an example pen that passes back the sample data you use above asynchronously.This mocks the $http.get call you make.
I have handled the async call in the controller in a slightly different way to what you had done, but this way it works with the .then() pattern that promises use. This should give you an example of how you can handle the async code in your controller.
Note as well that my service is in the same module as my controller. This shouldn't matter and the way you've done it, injecting your factory module into your main module is fine.
angular.module('myApp', [])
//Define your controller
.controller('myCtrl', ['$scope','myFactory', function($scope,myFactory) {
//call async function from service, with .then pattern:
myFactory.myFunction().then(
function(data){
// Call function that does your map reduce
$scope.totalErrors = setTotalErrors();
},
function(error){
console.log(error);
});
function setTotalErrors () {
if ($scope.tiles){
var topLevel = $scope.tiles.data
console.log (topLevel);
return topLevel[0].issues.map(function(o) {
return o.errors.length
})
.reduce(function (prev, curr){
return prev + curr
});
}
}
}])
.factory('myFactory', ['$timeout','$q',function($timeout,$q){
return {
myFunction : myFunction
};
function myFunction(){
//Create deferred object with $q.
var deferred = $q.defer();
//mock an async call with a timeout
$timeout(function(){
//resolve the promise with the sample data
deferred.resolve(
{'data':[
{'issues':[
{'name':'Test','errors':[
{'id':1,'level':2},
{'id':3,'level':1},
{'id':5,'level':1},
{'id':5,'level':1}
]},
{'name':'Test','errors':[
{'id':1,'level':2,'details':{}},
{'id':5,'level':1}
]}
]}
]})
},200);
//return promise object.
return deferred.promise;
}
}]);
Have a look : Link to codepen
Also, have a read of the $q documentation: documentation

error while using a factory to return data to controller

I am newbie to angular and I am fetching data from json file using a service and then returning the data to controller. When i click the button the controller method is not getting executed and there are no errors in console.log. what am i missing here?
My service code:
Service.js
app.factory('MovieService', function ($http) {
var url = "js/data.json";
var data;
return {
getData: function() {
return $http.get(url)
.success(function(response) {
data = response;
return data;
})
.error(function (error){
console.log("error");
})
}
};
});
Controller.js
app.controller('MainController1', ['$scope', '$log','$location','MovieService', function($scope,$log,$location,MovieService) {
console.log("click");
var getData = function() {
// This service's function returns a promise
MovieService.getData()
.then(function(data) {
// promise fulfilled
console.log("controller data");
$scope.custdata = data;
console.log($scope.custdata);
}, function(error) {
// promise rejected, could log the error with:
console.log("error");
});
};
}])
index.html
<div class="main" ng-controller="MainController1 as main">
<input type="button" name="getdata" value ="Get data" ng-click="main.getData ()"></input>
</div>
data
[
{
"Id": "1",
"Name": "Harry Potter"
},
{
"Id": "2",
"Name": "Jurassic Park"
}
]
You need to bind controller function on scope.
$scope.getData = function() { }"
instead
var getData = function() { }
and call it in template like
ng-click="getData ()"
You are using the controller as alias syntax.
In this case, your controller functions that need to be accessed from the view should be assigned to this.
So, define your function as a property of this and not as an independent function - like so:
this.getData = function () {...}
You are using var getData which will make the function a local function and not expose it.
Few things should be notice:-
1) You should use this instead of var to bind the function to the controller in controller as syntax:-
this.getData = function() {//your logic}
2) You are wrapping promise twice first in success() or error() then in another then() function instead do it like this:-
In service:-
getData: function() {
return $http.get(url);
}
In controller:-
MovieService.getData()
.then(function(response) {
// promise fulfilled
console.log("controller data");
$scope.custdata = response.data;
console.log($scope.custdata);
}, function(error) {
// promise rejected, could log the error with:
console.log("error");
});
3) <input> should not close like </input> it is not having closing tag.
Hope it help :)
PLUNKER

Adding a new item with $recource in Angular JS

I have created a simple app to store an event to my database. It is working fine but with a wordaround.
I use the following method:
controllers.controller('CalendarAddController', function($scope, $routeParams, Event) {
$scope.addEvent = function() {
$scope.ev=new Event();
$scope.ev.title = $scope.event.title;
$scope.ev.$save().then(function(res) { console.log("success");})
.catch(function(req) { console.log("error saving obj"); })
.finally(function() { console.log("always called") });
}
});
After submitting my form, the function addEvent is called.
If I print the value of $scope.event, it has the right values.
However, when I call .$save() on it, I get the error "TypeError: $scope.event.$save is not a function."
But when I create a new object and assign the values to it, it works fine.
Why isn't it working directly? Always creating a dummy object does not seem to be best practice I suppose.
The service I created
services.factory( 'Resource', [ '$resource', function( $resource ) {
return function( url, params, methods ) {
var defaults = {
update: { method: 'put', isArray: false },
create: { method: 'post' }
};
methods = angular.extend( defaults, methods );
var resource = $resource( url, params, methods );
resource.prototype.$save = function() {
if ( !this.id ) {
return this.$create();
}
else {
return this.$update();
}
};
return resource;
};
}]);
services.factory( 'Event', [ 'Resource', function( $resource ) {
return $resource( '/api/calendar/:id' );
}]);
Update: basic example
Using this example everything works front-end but in my rest API the data is not passed.
<!DOCTYPE html>
<html lang="en-US">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-route.min.js"></script>
<script type="text/javascript">
var app = angular.module('app', ['ngRoute','ngResource'])
.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl : 'pages/home.html',
controller : 'mainController'
})
});
app.factory('Event', function($resource) {
return $resource('/test/api/:id');
});
app.controller('mainController', ['$scope', '$routeParams', 'Event',
function($scope, $routeParams, Event) {
function handleSuccess(data) {
alert("success");
}
function handleError(data) {
alert("error");
}
var event = new Event();
event.title = "a title";
Event.save(event, handleSuccess, handleError);
}]);
</script>
<body>
<div ng-app="app">
<div ng-view></div>
</div>
</body>
</html>
For the back-end I created a very basic script which writes the request to a file:
It always says "array()" so no data is passed.
<?php
$myFile = "testFile.txt";
$fh = fopen($myFile, 'w') or die("can't open file");
$stringData = print_r($_REQUEST,true);
fwrite($fh, $stringData);
fclose($fh);
?>
Where do you initialize $scope.event or is it automatically created by angular? If this is an plain JavaScript object you can't call .$save, but you can use the Event service itself - no need to create a new instance:
Event.create($scope.event)

Sync Data From a Service in Directive and on Server

I have the following
Service:
angular.module('app')
.factory('UserFactory', function ($http) {
function profile () {
return $http.get('/gimme')
.then(function success (response) {
return response;
});
};
var user = {
profile: profile
};
return user;
It is used in a controller as follows:
Controller
angular.module('app')
.controller('HeaderCtrl', function ($scope, UserFactory) {
$scope.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
$scope.user = UserFactory.profile().then(function (response) {
$scope.user = response.data;
});
$scope.change = function () {
$scope.user.name = 'New Name'
}
}
If I call the change() method in a directive which uses HeaderCtrl, what is the best way to make sure that that change, which temporarily changes the user.name, actually changes it on my server as well? In other words, how would I trigger the put request (I am assuming some function needs to be called on the Factory, but I am not sure the best way to make sure it is called or where to put the function call in the controller).
Thanks
Here's an example extending the code you provided, using free JSONPlaceholder API. I think example itself is enough of an answer?
HTML
<body ng-controller="Ctrl as vm">
<div>data: {{ vm.todo | json }}</div>
<div>response: {{ vm.response | json }} </div>
<hr>
<button type="button" ng-click="vm.change('my new title')">Change title</button>
</body>
JavaScript
app.controller('Ctrl', function(TodoService) {
var vm = this;
var id = 1;
TodoService.getPost(id).then(function(response) { // Get
vm.todo = response.data;
});
vm.change = function(val) {
vm.todo.title = val;
TodoService.putPost(vm.todo).then(function(response) { // Put
vm.response = response.status + ' ' + response.statusText;
}).catch(function(error) {
vm.response = angular.toJson(error);
});
};
});
app.factory('TodoService', function($http) {
var endpoint = 'http://jsonplaceholder.typicode.com/todos/';
var todoService = {};
todoService.getPost = function(id) {
return $http.get(endpoint + id).then(function(response) {
return response;
});
};
todoService.putPost = function(todo) {
return $http.put(endpoint + todo.id, todo).then(function(response) {
return response;
});
};
return todoService;
});
Related plunker here http://plnkr.co/edit/VBvVen

Resources