How to output json data in controller in angularjs - angularjs

I am learning angularjs and i want to ouput some json data on console.
I am doing something like this
$scope.events = events.query();
but when i print on the console
console.log($scope.events);
it gives me the output like
Array []
how can i print the data like this
[
{"id":18,"file":{"url":"/uploads/playlist/file/18/01_-_MashAllah.mp3"},"event_id":23,"created_at":"2015-11-11T10:33:52.000Z","updated_at":"2015-11-11T10:33:52.000Z","name":"01 - MashAllah.mp3"},
{"id":19,"file":{"url":"/uploads/playlist/file/19/02_-_Laapata.mp3"},"event_id":19,"created_at":"2015-11-11T10:50:01.000Z","updated_at":"2015-11-11T10:50:01.000Z","name":"02 - Laapata.mp3"}
]
below is my whole code
.controller('ShowEventsCtrl', ['$scope','events', function($scope,events) {
$scope.events = events.query();
console.log($scope.events);
}]);
services
angular.module('myApp')
.factory('events', ['$resource',function($resource) {
return $resource('/events', {},{
query: { method: 'GET', isArray: true },
create: { method: 'POST' }
})
}])
and when i print the data in html like this
<div ng-repeat="playlist in playlists">
{{playlist}}
</div>
I get the correct output so i but how to output it in console.

Because your events.query() method is asynchronous, you need to log stuff after the action is resolved. Easiest way I can think of is
$scope.events = events.query(function(events) {
console.log(events);
});
From $resource
The action methods on the class object or instance object can be invoked with the following parameters:
HTTP GET "class" actions: Resource.action([parameters], [success], [error])
...
Success callback is called with (value, responseHeaders) arguments
I'm guessing your confusion comes from this feature of $resource
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.
You see the correct data in your template because once the data is returned from the server, a scope digest is triggered and your template is updated.

Related

Angularjs $resource query method working but get method not working

Here is my factory method:
.factory('lettersFactory', ['$resource', function ($resource) {
var url = "";
if(ionic.Platform.isAndroid()){
url = "/android_asset/www/";
}
return $resource(url + 'data/letters.json');
}])
And here is the controller:
.controller('LettersCtrl', ['$scope','lettersFactory', '$stateParams', '$state', '$ionicPopover', function($scope, lettersFactory, $stateParams, $state, $ionicPopover) {
$scope.letters = lettersFactory.query();
$scope.letter = lettersFactory.get({number:parseInt($stateParams.letterId, 10)});
}])
And here is the Error message:
Error in resource configuration for action object. Expected response to contain an array but got an GET (Request: data/letters.json {4})
And my letter.json is an array like this:
[
{"number":1,
"title": "title",
"content": "content"},
{"number":1,
"title": "title",
"content": "content"}
]
Thanks
If the response should not be an array then you need set the isArray as false in query property.
'query': {method: 'GET', isArray: false }
Refer to the document.https://docs.angularjs.org/api/ngResource/service/$resource
Or you can pass the json as array from the controller.
The default method set for $resource contains these actions1:
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
In your case the get method is failing because the data from the XHR is an array and the method expects an object.
The query method succeeds because the data from the XHR is an array and the method expects an array.
Use the get method for object data; use the query method for array data.
Update
how do you think I can use the query method in this situation to get a particular object from the array data?
One approach is to use the $promise property of the returned resource object:
$scope.array = lettersFactory.query();
$scope.array.$promise.then(function(resourceArray) {
$scope.item = resourceArray[0];
});
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.
The Resource instances and collections have additional properties:
$promise: the promise of the original server interaction that created this instance or collection.
On success, the promise is resolved with the same resource instance or collection object, updated with data from server.
On failure, the promise is rejected with the http response object, without the resource property.
For more information, see AngularJS $resource API Reference

$resource:badcfg Error in resource configuration for action `query`. Expected response to contain an object but got an array

I have following Service, the query always returns an array.
.factory('ClosingDocService', ['$resource', function ($resource) {
return $resource("http://localhost:5001/api/ClosingDoc/:id",
{ id: "#id" },
{
'query': { method: 'GET' },
'save': { method: 'POST', transformRequest: angular.identity, headers: { 'Content-Type': undefined } }
});
}])
However, the following call always got the error?
ClosingDocService.query({ category: model.category }).$promise
.then(function (x) { });
Using fiddler shows the following correct url has been called and the values were returned.
http://localhost:5001/api/ClosingDoc?category=XXX
Error:
angular.js:13920 Error: [$resource:badcfg] Error in resource configuration for action query. Expected response to contain an object but got an array
I found a solution - adding isArray: true. But why?
'query': { method: 'GET', isArray: true },
This occurs due to how $resource object methods are handled.
From the docs on $resource:
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.
Essentially, unless you provide isArray: true, the $resource object method pre-allocates an Object for the data that is to be returned. When the data being returned actually ends up being an array, that data can't be populated into the pre-allocated object.
I had the same issue too. What worked for me was the following fix
Changing the query to get as follows:
ClosingDocService.get({ category: model.category }).$promise
.then(function (x) { });

ngResource custom model actions

I'm using ngResource to handle my models in my Ionic/Angular app and I'm having trouble figuring out how/if I can make custom actions on the resource.
I'm storing my model instances in local storage and when I update a record, I want to update the local storage as well. I have this working, but I'm having to copy and paste code for multiple instances and would like to keep it DRY.
LogEntry.update($scope.timeLog, function(data) {
// update local storage
for ( var i = 0; i < logEntries.length; i++) {
if(logEntries[i].id == $scope.timeLog.id){
logEntries[i] = $scope.timeLog;
}
};
localStorageService.set('LogEntries', logEntries);
});
Here is a situation where I update a record, and after the promise returns I update local storage. I would like to make this repeatable, how I envision it being possible (based on other things I've seen in other frameworks and other languages) is something like:
LogEntry.update($scope.timeLog, function(data) {
// update local storage
LogEntry.updateLocalStorage($scope.timeLog);
});
My resource looks like:
.factory('LogEntry', function(config, $resource) {
return $resource(config.apiUrl + 'logentries/:id/', {}, {
'update': {
method:'PUT',
params: { id: '#id' }
}
});
})
Maybe I'm missing something in the docs, but it's pretty short and I'm not seeing a way to do this. Is something like LogEntry.updateLocalStorage($scope.timeLog); possible to store with ngResource, or do custom actions like that need to come from somewhere else? I'd like to keep my model-related actions together if possible.
You could use the transformResponse method in your resource definition. It's kind of a hack since you don't actually need to alter the response, but it allows you to preform actions with the returned data:
{function(data, headersGetter)|Array.<function(data, headersGetter)>}
transform function or an array of such functions. The transform function takes the http response body and headers and returns its transformed (typically deserialized) version. By default, transformResponse will contain one function that checks if the response looks like a JSON string and deserializes it using angular.fromJson. To prevent this behavior, set transformResponse to an empty array: transformResponse: []
https://docs.angularjs.org/api/ngResource/service/$resource
.factory('LogEntry', function (config, $resource) {
return $resource(config.apiUrl + 'logentries/:id/', {}, {
'update': {
'method': 'PUT',
'params': {
'id': '#id'
},
'transformResponse': function (data, header) {
// do stuff with data
return data;
}
}
});
})
That way it will always execute when you get a response but you could also use the transformRequest method, which will always fire on request regardless if you're getting a response.
{function(data, headersGetter)|Array.<function(data, headersGetter)>}
transform function or an array of such functions. The transform function takes the http request body and headers and returns its transformed (typically serialized) version. By default, transformRequest will contain one function that checks if the request data is an object and serializes to using angular.toJson. To prevent this behavior, set transformRequest to an empty array: transformRequest: []
https://docs.angularjs.org/api/ngResource/service/$resource
Which you choose depends on your usecase. Using transformRequest you could always save to localstorage, even when remote is down.

AngularJS: Resource factory and callback function

Hi being new to Angular i'm having difficulty seeing how to handle data when using $resource in a factory. I'm trying to move from $http requests to $resources in my factory.
First i had (simplified code):
.factory('MetadataService', function($http) {
$http({
method: 'OPTIONS',
url: 'http://myurl'
}).success(function(data) {
customized_data = do_some_complex_handling_on_data(data)
callback(customized_data);
});
});
When i try to use a $resource in my factory, it seems that i have to call the do_some_complex_handling_on_data() method in my controller:
.factory('MetadataService', function($resource) {
return($resource('http://myurl', {}, {
metadata: {method: 'OPTIONS'}
}));
});
# controller:
var metadata = do_some_complex_handling_on_data(MetadataService.metadata());
Since i'm gonna use the factory in a lot of controllers for different sections in my application (that's why i made a factory in the first place), i would like to have my factory return the data as i need it. And not have to customize the data after the factory returns it.
question: How do i let my factory call the do_some_complex_handling_on_data() function instead of the controller?
You can use response transformer that $http service provides. A transformer is used to transform the response of $http before it is delivered to the end client.
By default there is a single transformer register that convert json string to json object. You can append your own transformer to this collection and it will be called with the response json object. In your transformer function you can then call any function you want that can transform the data.
metadata: {
method: 'OPTIONS'
transformResponse: appendTransform($http.defaults.transformResponse,
function(value) {
return do_some_complex_handling_on_data(value);
})
}
function appendTransform(defaults, transform) {
// We can't guarantee that the default transformation is an array
defaults = angular.isArray(defaults) ? defaults : [defaults];
// Append the new transformation to the defaults
return defaults.concat(transform);
}
I have taken this code from the docs here
Also read documentation on "Default Transformations" in $http service

Passing a path with '/' to $resource or $http factory

Is there a way to pass a parameter containing / to Factory? Want to accomplish something like
.factory('MyData', ['$resource', function ($resource) {
return $resource('http://1.2.3.4/:urlFragment', {
urlFragment : '' // default empty
}, {
getData : {
method : 'GET'
},
And calling it
$scope.scopeVar = MyData.getData({urlFragment : '/some/path/to/data'});
Looking at the network console, I see that / are replaced with %2.
Can I encode the passed parameter inside Factory? (Using $http or $resource).
Or in general, how can I execute any functions on parameters inside factory?
No, you can't really get access to the url inside of your factory because $resource automatically handles it. But thankfully Angular gives you a way to get access to the url before it is called by using the $resource directly. Looking at the docs here, one of the actions you can supply in your $resource declaration is a transformRequest property.
return $resource('http://1.2.3.4/:urlFragment', {urlFragment: ''}, {
getData: {method: 'GET', transformRequest: function(data, headers){
// make your modifications here to either data or headers
}}
});
Although I haven't actually run this code, I believe that should allow you to do what you want. Let me know if it doesn't.

Resources