I have a service that returns JSON, assume this is the response:
{
"model": 48870,
"id": 20
}
I can do this:
$scope.response = Service.get();
And it will assign the JSON response to $scope.response. The issue here is I cannot directly access model or id via $scope.response.model or $scope.response.id, respectively. My workaround for this situation is to define a function in the service's success callback and fetch model or id from data. But this is not very practical considering $scope.response contains the entire JSON object and should in theory (and the sake of clean code) let me access its children.
Two questions here:
Is it a limitation of AngularJS representation of models?
How can I point to children (even grandchildren) without having to define a success callback for each call?
Once the asynchronous HTTP call returns, it is safe to access response's properties directly.
See this short example
app.controller('MainCtrl', function($scope, $resource) {
$scope.data = $resource('data.json').get(function() {
$scope.loaded = true;
//now it's safe to access $scope.data's properties:
$scope.data.foo = $scope.data.id + ':' + $scope.data.model;
});
});
UI bindings will work automatically.
I think what you are looking for is described here:
http://docs.angularjs.org/tutorial/step_05
Related
According to this question, i just have to set {cache: true} in $http.get(). My question is how do I check if an URL is already cache when I perform $http.get()?
When using $http.get() with {cache: true} option, it will automatically cache http GET requests using angular's built in $cacheFactory.
Assuming you're using the default configuration for caching, those are cached in a cache object with the name '$http'.
To access the cache object in your controller, you'll need to inject the $cacheFactory service and access it like this:
angular.module('cacheExampleApp', []).
controller('CacheController', ['$scope', '$cacheFactory', '$log', function($scope, $cacheFactory, $log) {
var url = 'http://jsonplaceholder.typicode.com/posts/1';
var httpCacheObj = $cacheFactory.get('$http');
var urlCache = httpCacheObj.get(url)
if (urlCache && urlCache[1]) {
$log.debug('request already cached.');
} else {
$log.debug('request is not cached.');
}
$http.get(url, {cache: true}).then(function(response){
// do something with the response...
});
};
}]);
Some notes:
Angular will not make an http request if a request is already cached, but the mechanism is still the same (using $http.get().then() to access it).
$cacheFactory.get('$http') is the default cache object for cached $http.get() requests.
The url for each requests is the key stored in the cache object.
When accessing a key in the cache object an Array is returned. I did not find documentation for it, but seems like each key in the array can contain 4 values if already cached:
urlCache[0] -> the response code (for example 200)
urlCache[1] -> the response body as string. If the response is in json format, it can be converted back to a JSON object by using JSON.parse(urlCache[1]);
urlCache[2] -> An object holding a key-value list of headers and their values.
urlCache[3] -> a String representing the meaning of the response code (for example 'OK' status 200).
see jsfiddle example
Been wrestling with API stuff all day, and decided to use Restanglar. Really having issues getting the data out, and into $scope.
I understand that it won't just be the JSON that is returned from the API, and has a bunch of other internal methods etc. But when I get the data out, I can see it buried somewhere in the debugging with console.log, but I can't seem to get it into $scope to use it in my view which was working fine previously.
How can I get that data out into my $scope, and therefore my view?
Model
angular.module('horse', ['restangular'])
.config(function(RestangularProvider) {
RestangularProvider.setBaseUrl('http://url/api');
RestangularProvider.setResponseInterceptor(
function(data, operation, what) {
if (operation == 'getList') {
return data[what];
}
return data;
});
});
Controller
angular
.module('horse')
.controller("IndexController", function ($scope, Restangular) {
$scope.horse = null;
$scope.showSpinner = true;
Restangular.all('horse').getList().then(function(horse) {
$scope.horse = horse;
console.log($scope.horse);
});
});
API response
{"error":false,"horse":[{"id":"1","name":"horse 2"},{"id":"2","name":"horse 2"}]}
Edit 1
Restangular response
[Object, Object, route: "horse", getRestangularUrl: function, getRequestedUrl: function, addRestangularMethod: function, clone: function…]
Edit 2
I have also tried this - https://github.com/mgonto/restangular#using-values-directly-in-templates
$scope.horse = Restangular.all('horse').getList().$object;
Which just results in an empty array being output. I have also tried removing the setResponseInterceptor and modifying the structure of the api to result the data array directly without the meta stuff (error, etc), no joy :(
The data seems to be coming through. I notice you're using Steroids, have you checked the markup and not just the console?
Make sure you set the scope spinner to false, to ensure that the spinner is hidden when the data comes through.
$scope.ShowSpinner = false;
Assuming that what you have shown as "API response" is what's getting outputted from the console.log in your controller, it seems that all you need to do is set your scope model the the property "horse" in the response data like this:
$scope.horse = horse.horse;
Since that reads pretty oddly, you should change the param name of the .then callback to data, which would be a much more agnostic and standard param name. If you make that change you can set your horse data to your scope model from inside your callback like this:
$scope.horse = data.horse;
If I misunderstood your question let me know. Hope this is helpful.
I have a json endpoint that returns a list of objects. The first view is a list with just some basic info but if you select an item I want to show more details. Should I just construct and object in my factory and have another method to get the details? so
getItems() // returns list data
getItem(id)// goes through the object already retrieved and returns just the data?
I don't want to hit the server again for each item but I'm not sure of the best way to do this in Angular. It doesn't appear the Model is made for this in Angular.
Also, assuming Item has additional information that I may need to make another server request. What's the best way to store this locally so if I already fetches these details once I don't need to get it again?
If you want to exchange data between 2 controllers, you can use a provider
App.provider('Data', function() {
//Properties declaration
this.info = '';
//Setters
this.setInfo = function(info) {
this.info = info;
};
//Getters
this.getInfo = function() {
return this.info;
};
controller 1
App.controller(
'controller1',
['$scope', 'Data',
function($scope, Data) { Data.setInfo('here you put what you want') }]);
controller which get back information
App.controller(
'controller2',
['$scope', 'Data',
function($scope, Data) { Data.getInfo() }]);
More likely, you'll want to use either a factory or a service:
https://docs.angularjs.org/api/auto/service/$provide#factory
https://docs.angularjs.org/api/auto/service/$provide#service
In both cases, you provide a function which Angular will call once and store the return reference, then provide that reference to any controller or other Angular-managed object that requests it. The difference between a factory and service is that a service function is expected to be a constructor function (it's called using the "new" keyword) whereas a factory is a just invoked as a regular function.
i am trying to consume this webservice (http://services.tvrage.com/feeds/show_list.php) from TVRage using Angularjs.
I can 'connect' to the service (using firebug I see GET show_list.php STATUS 200 OK) but when i try to print any data from the response I get none.
This is the code that i use:
var TV_Episodes = angular.module('TV_Episodes', ['ngResource']);
TV_Episodes.controller('GetAllEpisodes', function($scope, $resource) {
var dataService = $resource('http://services.tvrage.com/feeds/show_list.php');
$scope.data = dataService.get();
console.log($scope.data());
});
any ideas on how I can just console.log the the response?
UPDATE 1:
After some more trying i found out that that i get the following error as a response from TVRAGE.
"XMLHttpRequest cannot load http://services.tvrage.com/feeds/show_list.php. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access."
therefor i tweaked my code so
var dataService = $resource('http://services.tvrage.com/feeds/show_list.php?key=xxxx',{},{headers: { 'Access-Control-Allow-Origin': '*' }});
but i still get the same error as before.
$resource.get() returns a promise, which means you are likely printing to the console prior to the data being retrieved. Instead use the appropriate callback function:
$scope.data = dataService.get(function() { console.log($scope.data); });
The get method is asyncronous. When it is called it returns immediately with a reference to an object (or array, if specified - but not a promise as indicated in MWay's answer). Then, later, that same reference is updated with the data that is returned from the server on success. Here's the relevant part from the documentation:
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most cases one never has to write a callback function for the action methods.
As fast as the request might be, it won't resolve until the event loop comes around again. The resource is helpfully designed to free you up from having to worry about writing callbacks. If you need to though, the get method takes callback function parameters that will be invoked when the request resolves and the data is ready.
var TV_Episodes = angular.module('TV_Episodes', ['ngResource']);
TV_Episodes.controller('GetAllEpisodes', function($scope, $resource) {
var dataService = $resource('http://services.tvrage.com/feeds/show_list.php');
$scope.data = dataService.get(function () {
console.log($scope.data());
});
});
Or, you can access the promise used for processing the request by using *$promise", which is a property on empty instance object returned from get.
I am using AngularJS Services in my application to retrieve data from the backend, and I would like to make a loading mask, so the loading mask will start just before sending the request. but how can I know when the request ends?
For example I defined my servive as:
angular.module('myServices', ['ngResource'])
.factory('Clients', function ($resource) {
return $resource('getclients');
})
.factory('ClientsDetails', function ($resource) {
return $resource('getclient/:cltId');
})
So I use them in my controller as:
$scope.list = Clients.query();
and
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
});
So the question would be, how to know when the query and get requests ends?
Edit:
As a side note in this question I've been using using angularjs 1.0.7
In AngularJS 1.2 automatic unwrapping of promises is no longer supported unless you turn on a special feature for it (and no telling for how long that will be available).
So that means if you write a line like this:
$scope.someVariable = $http.get("some url");
When you try to use someVariable in your view code (for example, "{{ someVariable }}") it won't work anymore. Instead attach functions to the promise you get back from the get() function like dawuut showed and perform your scope assignment within the success function:
$http.get("some url").then(function successFunction(result) {
$scope.someVariable = result;
console.log(result);
});
I know you probably have your $http.get() wrapped inside of a service or factory of some sort, but you've probably been passing the promise you got from using $http out of the functions on that wrapper so this applies just the same there.
My old blog post on AngularJS promises is fairly popular, it's just not yet updated with the info that you can't do direct assignment of promises to $scope anymore and expect it to work well for you: http://johnmunsch.com/2013/07/17/angularjs-services-and-promises/
You can use promises to manage it, something like :
Clients.query().then(function (res) {
// Content loaded
console.log(res);
}, function (err) {
// Error
console.log(err);
});
Another way (much robust and 'best practice') is to make Angular intercepting your requests automatically by using interceptor (see doc here : http://docs.angularjs.org/api/ng.$http).
This can help too : Showing Spinner GIF during $http request in angular
As left in a comment by Pointy I solved my problem giving a second parameter to the get function as following:
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
}, function(){
// do my stuff here
});