Angular $http service does not load json file - javascript

Hello angular experts!
I used this custom directive for a table (implementation) but when I try to use $http service load that json array from a file, json is not loaded into $scope.items, I am a beginner in angular and on fairly advance javascript thus I need some help from you.
controller initialization
fessmodule.controller('ptiListController', function($http, $scope, $filter) {
$http service call
$http.get('data/ptis/ptis.json').then(function(response) {
$scope.items = response.data;
}
);
browser console error
TypeError: Cannot read property 'length' of undefined
at Scope.$scope.groupToPages (http://localhost:8000/app/modules/app/phone/scripts/pti-list-controller.js:75:49)
at Scope.$scope.search (http://localhost:8000/app/modules/app/phone/scripts/pti-list-controller.js:68:16)
at new <anonymous> (http://localhost:8000/app/modules/app/phone/scripts/pti-list-controller.js:117:12)
at invoke (http://localhost:8000/app/lib/js/angular.js:4185:17)
at Object.instantiate (http://localhost:8000/app/lib/js/angular.js:4193:27)
at http://localhost:8000/app/lib/js/angular.js:8462:28
at link (http://localhost:8000/app/lib/js/angular-route.js:975:26)
at invokeLinkFn (http://localhost:8000/app/lib/js/angular.js:8219:9)
at nodeLinkFn (http://localhost:8000/app/lib/js/angular.js:7729:11)
at compositeLinkFn (http://localhost:8000/app/lib/js/angular.js:7078:13) <div ng-view="" class="ng-scope">
so what I have changed from the fiddle is:
instead of:
$scope.items = [
{"id":1,"name":"name 1","description":"description 1","field3":"field3 1","field4":"field4 1","field5 ":"field5 1"},
{"id":2,"name":"name 2","description":"description 1","field3":"field3 2","field4":"field4 2","field5 ":"field5 2"},
{"id":3,"name":"name 3","description":"description 1","field3":"field3 3","field4":"field4 3","field5 ":"field5 3"}
];
i have changed to this:
$http.get('data/ptis/ptis.json').then(function(response) {
$scope.items = response.data;
}
);
and also, I have tried using the service call as:
$http.get('data/ptis/ptis.json').success(function(data) {
$scope.items = data;
});
and got the same behavior.
Thank you in advance!

I believe you are using the $http.get wrong. Try $http.JSONP this pattern:
$scope.items = {}; // <-- initialize empty object
$http.jsonp('/someJSONUrl').
success(function(data) {
// this callback will be called asynchronously
// when the response is available
$scope.items = data; // <-- fill object with data
});
You can't use $scope.items before it holds some data. That's why you have to initialize it first, as empty object/array then fill it with data and angular magic should do the rest :)

I just do something like this as mention in document and it work:
$http.get('someUrl').
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Here the sample:
angular.module('myApp', [])
.controller('JustCtrl', function($scope, $http) {
$scope.ptis = [];
// Simple GET request example :
$http.get('https://gist.githubusercontent.com/idhamperdameian/239cc5a4dbba4488575d/raw/0a2ea4c6c120c9a8f02c85afcf7a31941ef74d3a/ptis.json').
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
$scope.ptis = data;
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="JustCtrl">
<span ng-repeat="p in ptis">
{{p.name}}, {{p.description}}, etc...<br>
</span>
</div>
Or you may prefer to this demo.

Related

Calling another function in ng-repeat

I am using AngularJs 1.5 RC build.
I have a view wherein I am using ng-repeat to iterate over a collection , like so
<tr ng-repeat="user in users">
<td>{{user.JobID}}</td>
<td>{{getManager(user.userID)}}</td>
<td>{{user.StatusDesc}}</td>
<td>{{user.StartedAt}}</td>
</tr>
The idea here is to use the getManager function to get the name of the manager for each and every user in the users collection.
As an aside , I have to use this approach since the API is not returning me all the information.
This is how the getManager function looks like now.
$scope.getManager = function($id) {
return "John Doe";
}
The entire controller looks as follows
var app = angular.module('userapp', ['ui.bootstrap']);
app.controller('MainController', ['$scope','$http', function($scope, $http) {
$scope.getUsers = function() {
$http.get("http://localhost/getUsers").
success(function(data, status, headers, config) {
$scope.users = data.resource;
}).
error(function(data, status, headers, config) {
// log error
console.error("An error as encountered. Error follows:");
console.error(data);
});
}
$scope.getManager= function($id) {
return "John Doe";
}
}]);
So in my page view, I am getting "John Doe" as a manager for all my users.
Problem
The problem begins whenever I try to get the real manager for a user. So if i replace my dummy getManager with the following function
$scope.getManager = function($id) {
$http.get("http://localhost/user/manager/"+$id).
success(function(data, status, headers, config) {
return (data.resource[0].ManagerName);
}).
error(function(data, status, headers, config) {
// log error
console.error("An error as encountered. Error follows:");
console.error(data);
});
}
AngularJs starts complaining and fails with the following
https://docs.angularjs.org/error/$rootScope/infdig?p0=10&p1=%5B%5D
Can you please let me know what might be happening here.
Please note , I am an Angular noob, hence your patience will be well appreciated.
Thanks
You should call ajax in that way inside {{}} interpolation. It will get called on each digest cycle and will throw $rootScope/infdig error.
So I'd suggest you to call the getManager method as soon as you retrieve a data from server. Then after getting data from a server you need to call getManager method just by passing UserId(look I change getManager implementation to return managerName by returning data). After getting managerName you need to bind that manager name to user object & use {{user.ManagerName}} on the HTML.
Markup
<tr ng-repeat="user in users">
<td>{{user.JobID}}</td>
<td>{{user.managerName}}</td>
<td>{{user.StatusDesc}}</td>
<td>{{user.StartedAt}}</td>
</tr>
Code
$scope.getUsers = function() {
$http.get("http://localhost/getUsers")
.success(function(data, status, headers, config) {
$scope.users = data.resource;
angular.forEach($scope.users, function(user){
(function(u){
$scope.getManager(u.UserID).then(function(name){
u.ManagerName = data;
})
})(user);
})
})
};
$scope.getManager = function($id) {
return $http.get("http://localhost/user/manager/"+$id).
then(function(response) {
var data = response.data;
return (data.resource[0].ManagerName);
},function(error) {
console.error("An error as encountered. Error follows:");
});
};
Side Note
Don't use .success & .error function on $http calls as they are
deprecated.
This is a really bad practice. If you have N users, you will send N queries to fetch every data. You should return this data in http://localhost/getUsers response.
You can try:
$scope.getManager = function($id) {
return $http.get("http://localhost/user/manager/"+$id)
...
}

Getting Data From Service

Here is my controller and service:
var app = angular.module('myApp', ['ui.bootstrap']);
app.service("BrandService", ['$http', function($http){
this.reloadlist = function(){
var list;
$http.get('/admin.brands/getJSONDataOfSearch').
success(function(data, status, headers, config) {
list = data;
}).
error(function(data, status, headers, config) {
});
return list;
};
}]);
app.controller('BrandsCtrl', ['$scope','$http','$controller','BrandService', function($scope, $http, $controller, BrandService) {
$scope.brands = BrandService.reloadlist();
angular.extend(this, $controller("BrandCtrl", {$scope: $scope}));
}]);
I searched for this issue and tried answers of questions but I couldn't get solution. I am new at angular so can you explain with details; why I couldn't get the data from service to controller this way ?
The return used for data is for the callback of your function.
You must use the promise returned by $http like this.
In your service return the promise :
return $http.get('/admin.brands/getJSONDataOfSearch').
success(function(data, status, headers, config) {
return data;
}).
error(function(data, status, headers, config) {
});
Use then() on the promise in your controller :
BrandService.reloadlist()
.then(function (data){
$scope.brands = data;
});
It's not angular, it's the Javascript. The function you put in this.reloadlist does not return any value. It has no return at all, so the value returned will be undefined. The success handler does return something, but it will be run long after reloadlist finished working.
Besides what #fdreger already pointed out (missing return value), $http.get(...) is an async method. The return value is a promise not the actual value.
In order to access the value you need to return it from reloadlist like this:
this.reloadList = function() {
return $http.get('/admin.brands/getJSONDataOfSearch');
// you need to handle the promise in here. You could add a error handling here later by catching stuff...
}
and in the controller you can add it to the $scope like this:
BrandService
.reloadlist()
.then(function(res) {
$scope.brands = res.data;
});
The callback passed to then() is called as soon as the HTTP request has successfully completed, this makes the call asynchronous.
Besides the angular documentation for promises the article on MDN is a good read too.

Angular $resource data is available to view but not in the code

I'm an angular noob and am really frustrated with a particular problem.
I have a $resource returning data from the server, which contains key/value pairs i.e. detail.name, detail.email etc.
I can access this information on the view using {{detail.name}} notation, but I cannot access it in the code, which is driving me nuts, as I need this data to do stuff with.
How can I access it in the backend?
here's the code generating the data:
mydata = Appointment.get({id: $stateParams.id}, function(data){
geocoder.geocode(data);
$scope.detail = data;
});
on the view I have the following:
<address class="text-left">
{{detail.address_1}}</br>
{{detail.city}}</br>
{{detail.postcode}}</br>
</address>
</hr>
<p> {{detail.lat}}</p>
<p> {{detail.lng}}</p>
<p> {{center}}</p>
this is all ok.
however, if I add console.log($scope.detail.lat) in the $resource callback i get undefined.
Here is the resource definition:
angular.module('MyApp')
.factory('Appointment', function($resource){
return $resource('/api/admin/:id', { id: "#_id" }, {
query: {method:'GET', isArray:true},
update: { method:'PUT' }
});
})
and the geocoder factory if anyone is interested:
angular.module('MyApp')
.factory('geocoder', ['$http','$state', function($http, $state){
var geocoder ={};
geocoder.geocode = function (formData){
var myformData = {};
var address = formData.address_1+', '+formData.city+', '+formData.postcode;
var key = 'AIzaSyACVwB4i_6ujTrdjTMI-_tnsDrf6yOfssw';
$http.get('https://maps.googleapis.com/maps/api/geocode/json?address='+address+'&key='+key).
success(function(results, status, headers, config) {
var results = results.results[0];
formData.lat = results.geometry.location.lat;
formData.lng = results.geometry.location.lng;
myformData = formData;
return myformData;
// this callback will be called asynchronously
// when the response is available
}).
error(function(results, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
return geocoder;
}])
Can anyone help?
You could do utilize the promise return from the $resource object & then utilize that promise in controller.
Factory
angular.module('MyApp')
.factory('Appointment', function($resource){
return $resource('/api/admin/:id', { id: "#_id" }, {
query: {method:'GET', isArray:true},
update: { method:'PUT' }
});
})
Controller
mydata = Appointment.get({id: $stateParams.id}).$promise;
mydata.then(function(data){
geocoder.geocode(data);
$scope.detail = data;
});

AngularJS $watch not updating my output

I have an HTML which looks like -
<div ng-controller="PostsCtrl">
<ul ng-repeat="post in posts" style="list-style: none;">
<li style="padding: 5px; background-color: #f5f5f5;">
<h4>
{{post.postTitle}}
</h4>
<div class="post-details" ng-show="showDetails">
<p>{{post.postContent}}</p>
</div>
</li>
</ul>
</div>
Now the data is being populated from a JSON based REST URL and being displayed. I also have a form that will be adding new post to the database-
<form data-ng-submit="submit()"
data-ng-controller="FormSubmitController">
<h3>Add Post</h3>
<p>
Title: <input type="text" data-ng-model="postTitle">
</p>
<p>
Content: <input type="text" data-ng-model="postContent">
</p>
<p>
Tags: <input name="postTags" data-ng-model="postTags" ng-list
required>
</p>
<input type="submit" id="submit" value="Submit" ng-click="loadPosts()" /><br>
</form>
I basically want to achieve two things -
1. As soon as i add new post it shows up in the list of posts above.
2. As soon as i manually add a new post in the backend, front end automatically updates.
Is it possible to achieve both using angular and if yes how will i be able to do that.
Below is my controller code, which as of now is showing me existing posts as well as letting me add new post to DB.
<script>
var app = angular.module("MyApp", []);
app.controller("PostsCtrl", function($scope, $http) {
$http.get('http://localhost:8080/MyApp/posts')
.success(function(data, status, headers, config) {
$scope.posts = data;
}).error(function(data, status, headers, config) {
console.log("Error in fetching the JSON data.");
});
$scope.$watch('posts', function(newVal, oldVal){
console.log('changed');
alert('hey, myVar has changed!');
}, true);
/*$scope.$watch('posts', function() {
alert('hey, myVar has changed!');
console.log("test log");
$scope.$digest();
});*/
});
app.controller('FormSubmitController', [ '$scope', '$http',
function($scope, $http) {
$scope.loadPosts = function() {
$http.get('http://localhost:8080/MyApp/posts')
.success(function(data, status, headers, config) {
$scope.posts = data;
alert(JSON.stringify(data));
//$scope.posts_updated = data;
}).
error(function(data, status, headers, config) {
console.log("Error in fetching the JSON data.");
});
}
$scope.list = [];
$scope.submit = function() {
var formData = {
"postTitle" : $scope.postTitle,
"postContent" : $scope.postContent,
"postTags" : $scope.postTags,
"postedBy" : "admin"
};
var response = $http.post('addPost', formData);
response.success(function(data, status, headers, config) {
console.log("na");
});
response.error(function(data, status, headers, config) {
alert("Exception details: " + JSON.stringify({
data : data
}));
});
//Empty list data after process
$scope.list = [];
};
} ]);
</script>
Any help on this will be really appreciable.
1: on your success of post, you can just push the added object into your posts list. This will trigger the two-way-binding, and the object will "automatically" appear in your ng-repeater.
$scope.posts.push(element);
2: This one is a bit tricky, since angular is a client-side application, it doesn't recognize what happens on the server-side. What you have to do to make this work is to look at websockets (like SignalR or similar) that can make a push to your client whenever something gets added. This also depends on that your "manual" insert is done using a programatically method. Doing it directly from database-changes is going to be alot more painfull
Initialize $scope.posts before invoking $http request
$scope.posts = [];
Since you are using $http service, it should automatically repaint ng-repeat when new data found. So you don't need be to worried about it
Very important thing is that you don't need to call $digest when you use $http service. Using $digest blindly is a very bad practice and is major performance issue. In the end of $http service angular automatically call $digest so you don't need to call again

AngularJS parse JSON

I just started with learning Angular and now i'm busy with an web application that shows some records i fetched from a JSON. The JSON looks like:
"results": [
{
"easy": false,
"id": 1,
"title": "title",
}
]
i am parsing that on this way (seems correct to me)
var app = angular.module("DB", []);
app.controller("Controller", function($scope, $http) {
$http.defaults.headers.common["Accept"] = "application/json";
$http.get('api_url').
success(function(data, status, headers, config) {
$scope.thing = data.results;
});
});
So now that i am in this JSON file i need to get the ID (in this case its 1) and with that ID i need to do a new request api.com/game/{id} to get more detailed information about the result from the first file.
What is the best way to do that?
$http.get('api.com/game/' + $scope.thing.id, function(...){ });
Point to note, you do not have to manually parse JSON with angular. It will do that for you. So data.results already has the object representing your response.
i think it is good idea if you do like this:
var app = angular.module("DB", []);
app.controller("Controller", function($scope, $http) {
$http.defaults.headers.common["Accept"] = "application/json";
$http.get('api_url').
success(function(data, status, headers, config) {
$scope.thing = data.results;
$scope.id=data.results[0].id;
gameInfo();
});
});
var gameInfo=function(){
$http.get('api.com/game/'+$scope.id).
success(function(data, status, headers, config) {
$scope.newThing = data;
});
}
Also take a look at ngResource which is a module that gives you more fine-grained control over HTTP requests. It does parameter replacement among other things (custom interceptors, etc.)
https://docs.angularjs.org/api/ngResource/service/$resource

Categories

Resources