One dimensional array of strings being parsed to 2d by angular resource - json

The following JSON response from the server
[
"hello",
"world"
]
is being parsed into a 2d array by this ngResource service
myService.factory('Name', function($resource){
return $resource(site_url+'api/accounts/:accountId/names/', {}, {
list: {method:'GET', params:{}, isArray:true}
});
});
called like so
$scope.names = Name.list({accountId:$scope.account.id}, function(e){
console.log(e);
});
traces to
[{"0":"h","1":"e","2":"l","3":"l","4":"o"},{"0":"w","1":"o","2":"r","3":"l","4":"d"}]
Any hints?

TLDR; ngResource expects an object or an array of objects in your response.
When isArray is set to true in the list of actions, the ngResource module iterates over each item received in the response and it creates a new instance of a Resource. To do this Angular performs a deep copy between the item received and the Resource class, which gives us an object with special methods ($save, $delete and so on)
Check the source here.
Internally angular uses angular.copy to perform the deep copy and this function only operates with objects and arrays, when we pass a string, it will treat it like an object.
Strings in JS can behave as arrays by providing sequential access to each character. angular.copy will produce the following when passed a string
angular.copy('hi',{}) => {0:'h', 1:'i'}
Each character becomes a value in an object, with its index set as the key. ngResource will provide a resource with properties 0 and 1.
Your choices are:
Use the lower level $http service
$http.get('/res').success(function(data){
$scope.test = data;
});
Return an array of objects in your json response
[{'data': "hello"}, {'data': "world"}]
Intercept the response and change your data
If you cannot modify the data the server sends back and want to use ngResource you will need to transform the response. Read how to do it here

I have been struggling with this as well. Here is my solution by adjusting the service slighly by using query
var app = angular.module('testApp', ['ngResource']);
app.factory('Name', function($resource, $sce) {
var path = "test.json";
return $resource(path, {}, {
query: {
method: 'GET',
isArray: false
}
})
});
app.controller('testController', function($scope, Name) {
$scope.result;
$scope.getResult = function() {
Name.query(function(data) {
$scope.result = data;
});
};
$scope.getResult();
});
HTML:
<!DOCTYPE html>
<html ng-app="testApp">
<head>
<link href="style.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="testController">
<h1>{{result.surname}}</h1>
</body>
</html>
and the JSON file:
{
"name": "Homer",
"surname": "Simpson",
"Town": "Springfield"
}
and also working Plunker if interested: http://plnkr.co/edit/SwqlZyqZ4zfcpaLxaf39
Hope this help someone ...

Related

Unable to retrieve JSON array with AngularJS $resource

I've been trying retrieve values from JSON and so far, been unsuccessful. It does get called on the front-end when I refresh the page, but the information is not passing to the next method. I think the issue might be down to the promises.push... line, as I've tried to debug the method underneath and the information is not being passed on at all.
AngularJS:
var promises = [];
promises.push(SpringDataRestService.get({"collection": "subjects"}).$promise);
// Require each of these queries to complete before continuing
$q.all(promises).then(function (data) {
// Grab the first result
$scope.available = data[0].subjects;
$scope.selected = [];
// If this is an update, get the second result in set
if (data.length > 1) {
// For each permission that is assigned to this role, add ID (name) to selected
for (var i = 0; i < data[1].data.subjects.length; i++) {
var perm = data[1].data.subjects[i];
$scope.selected.push(perm.name);
}
}
$scope.tableEditOptions = new NgTableParams({}, {
dataset: $scope.available
});
$scope.available, 'name');
}).catch(function (data) {
// ERROR
});
JSON:
[
{
"name": "FWGWG",
"description": "WGWGWG",
"lockId": 0
},
{
"name": "QFQFQF",
"description": "QFQFQFQ",
"lockId": 0
}
]
I'm confident as well my for loop is wrong due to assigning the values as well, since I don't think it should be data.subjects, but I understand these threads are only 1 issue per question. Any help would be greatly appreicated.
Use the query method for arrays:
var promise = SpringDataRestService.query({"collection": "subjects"}).$promise;
promise.then(function (dataArr) {
console.log(dataArr);
//...
}).catch(function (errorResponse) {
console.log(errorResponse);
});
With the REST services, the get method returns a JavaScript object and the query method returns a JavaScript array.
From the Docs:
$resource Returns
A resource "class" object with methods for the default set of resource actions optionally extended with custom actions. The default set contains these actions:
{
'get': {method: 'GET'},
'save': {method: 'POST'},
'query': {method: 'GET', isArray: true},
'remove': {method: 'DELETE'},
'delete': {method: 'DELETE'}
}
...
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.
For more information, see
AngularJS $resource Service API Reference

Backbone: fetching from URL in router gets undefined, but it works when collection gets JSON from a variable

From a JSON stored in a variable I can get the name of the current id from a router function called show: function(id). However, when I fetch collection from an URL instead of using a JSON variable I get an undefined TypeError.
console.log(this.collection.get(id).get('name'));
What I have seen is that when I use a JSON variable the show function works fine, but when I fetch from URL, show function executes after fetch succeed.
What I am doing wrong? Why fetching from URL gets undefined? How can I make it work?
The following code is fictional, it only shows the relevant part of my code. See the two cases at the end of the code block.
jsFiddle here
// Data 1 with variable
var heroes = [
{"id": "1", "name": "Batman"},
{"id": "2", "name": "Superman"},
];
// Data 2 from url: http://example.com/heroes.json
[
{"id": "1", "name": "Batman"},
{"id": "2", "name": "Superman"},
];
HeroesCollection = Backbone.Collection.extend({
model: HeroesModel,
url: 'http://example.com/heroes.json'
});
HeroesRouter = Backbone.Router.extend({
// I use two shows to graphic this example
routes: {
'': 'index',
':id': 'show'
},
initialize: function(options) {
this.collection = options.collection;
this.collection.fetch();
// this.collection.fetch({async:false}); this fixes my problem, but I heard it is a bad practice
},
index: function() {
},
show: function(id) {
console.log(this.collection.get(id).get('name'));
// Case #1: When Collection loads from a Variable
// id 1 returns: 'Batman'
// Case #2: When Collection fetchs from URL, id 1 returns:
// TypeError: this.collection.get(...) is undefined
}
});
// Case #1: collection loads JSON from a variable
var heroesCollection = new HeroesCollection(heroes);
// Case #2: collection loads JSON with fetch in router's initialize
// var heroesCollection = new HeroesCollection();
var heroesRouter = new HeroesRouter({collection: heroesCollection});
How about this? It's been awhile, but this seems like a better approach to what you are trying to achieve. The basic concept is that once you navigate to your show route, it will execute show. This method will create a new, empty collection, and then fetch the data for it. Along with that, we pass in a success method (as François illustrated) which will execute when the request is finished with the JSON (which creates a collection of Heros).
I believe the reason you were running into the issue with the remote data is that you were trying to access this.collection before it was populated with data from the request.
You have to remember the request is asynchronous, which mean code execution continues while the request is processing.
HeroesCollection = Backbone.Collection.extend({
model: HeroesModel,
url: 'http://example.com/heroes.json'
});
HeroesRouter = Backbone.Router.extend({
routes: {
'': 'index',
':id': 'show'
},
index: function() {
},
show: function(id) {
this.herosCollection = new HerosCollection();
this.herosCollection.fetch({
success: function(collection, response, options) {
console.log(this.get(id).get('name'));
}
});
}
});
you need to trigger the router 'show' function when the collection has ended to load.
this.collection.fetch({async:false}); fixes your problem because the whole javascript code is waiting (async:false) the ajax call to be ended before going further.
The other and best solution is to wait that your collection is fetched before you try to use the results.
Basically:
MyCollection.fetch({
success: function(model, reponse) {
// do wtv you want with the result here or trigger router show method...
}
});

angularjs get data from Json using $resourse

Good afternoon! Learning Angularjs. Below is the structure of the project.
I have a service that reads data from json
var WebKrServices = angular.module('WebKrServices', ['ngResource']);
WebKrServices.factory('DataPlant', ['$resource',
function($resource){
return $resource('plants/:plantId.json', {}, {
query: {method:'GET', params:{plantId:'plants'}, isArray:true}
});
}]);
And Controller
var WebKrControllers = angular.module('WebKrControllers', []);
WebKrControllers.controller('PlantsCtrl', ['$scope', 'DataPlant',
function($scope, DataPlant) {
$scope.plants = DataPlant.query();
}]);
which transmits this information to the html
<div ng-repeat="plant in plants">
<h2 class="text-center">{{plant.name}}</h2>
</div>
And, in fact question. In html I see data from json, and the controller when accessing the plants I see an empty object?
for (var p in plants) {
. . .
}
How to get data from the plants in the controller?
Thank you all for your answers.
Cause it is asynchronous call. After $scope.plants = DataPlant.query();, plants remain undefined until data arrives (Well, it is not exactly undefined, you can inspect it in debugger). When data arrives - $scope.plants get resolved and html is updated. To run some code after data arrives, use callbacks:
$scope.plants = DataPlant.query(function(response) {
console.log($scope.plants);
}, function (response) {
console.log('Error');
});

How to have a custom value in JSON with JQueryi UI Autocomplete?

I'm using the JQuery UI autocomplete plugin (cached version) with JQuery-UI 1.11.1
Due to some server-side changes in the JSON I am using as source, I need to adapt my code.
Here is an example of my JSON:
[{
"name": "Varedo"
}, {
"name": "Varena"
}, {
"name": "Varenna"
}, {
"name": "Varese"
}]
produced by an URL with this style:
[url]/?name=vare
Since the GET variable is different from the default one ("term"), I already adapted my code for the custom request as suggested here:
$(function () {
var cache = {};
$("#searchTextField").autocomplete({
minLength: 3,
source: function (request, response) {
var term = request.term;
if (term in cache) {
response(cache[term]);
return;
}
$.getJSON("[url]", {
name: request.term
}, function (data, status, xhr) {
cache[term] = data;
response(data);
});
}
});
});
However I need to also adapt the code in order to use a custom JSON value (the default is "value" http://api.jqueryui.com/autocomplete/#option-source) which is in my case is "name" (as you can see from the JSON).
How can I do that?
At the moment this is what I get from the autocomplete:
So I guess I am somehow giving as response JS Objects and not strings.
Thanks in advance.
Currently you're saving the response as it is into your cache object, which is not valid format for jQuery UI autocomplete. You should convert the data into proper format digestable for autocomplete.
Either you should pass an array of strings, or an array of objects having label and value properties.
Since the response only contains name properties, you can convert it into an array of strings using jQuery map() method and save it in cache variable as follows:
$("#searchTextField").autocomplete({
minLength: 3,
source: function (request, response) {
var term = request.term;
if (term in cache) {
response(cache[term]);
return;
}
$.getJSON("[url]", {
name: request.term
}, function (data, status, xhr) {
cache[term] = $.map(data, function (obj) { // returns array of strings
return obj.name
});
// return the new array rather than original response
response(cache[term]);
});
}
});

AngularJS - How to data bind an object within a JSON object

Using the included http post, I should get back the JSON object below. I want to take the LeagueDictionary data in the JSON below and create an object so I can use it in a for each loop on my client, but I can't wrap my head around how to structure that code in the http call.
{
"Id": 0,
"UserName": null,
"NickName": null,
"Email": "email#company.com",
"Password": null,
"Admin": false,
"Validated": false,
"Key": "oOE0QbOhjK17pNeKDPEFti5On27R3b",
"LeagueDictionary": {
"1": "League #1",
"2": "League #2"
}
}
using this call:
$scope.getLeagues = function() {
$http({
method: 'POST',
url: 'http://xxxxxxxxxxxxxxxxxx',
data: ???,
})
}
If someone give me a nudge on how to data bind that particular part of the JSON, I'd appreciate the help. I'm not sure how to strip the LeagueDictionary section out and make an object out of it.
You could set up a service that gets your data, so you can get $http and such out of your controller. Say...
app.factory('DataService', function ($http) {
return {
get: function () {
return $http.get('data.json'); // post & url goes here
}
};
});
In your controller, use then to access the response data after promise has been resolved (catch() and finally() are available, too).
app.controller('MainCtrl', function ($scope, DataService) {
DataService.get().then(function (response) {
$scope.leagueDictionary = response.data.LeagueDictionary;
});
});
Related HTML template would be
<body ng-controller="MainCtrl">
<div ng-show="leagueDictionary">
<span ng-repeat="(key,val) in leagueDictionary">
{{ val }} <br>
</span>
</div>
</body>
Using your data this gives you
See example plunker here http://plnkr.co/edit/j6bmmQ
You can just access the LeagueDictionary property of the response, and then iterate over that in ng-repeat. Obviously I don't know exactly what your scopes look like, but this should get you started:
//JS
$http.post('/someUrl', { 'foo': 'bar' }).success(function(data) {
myController.myModel.leagueDictionary = data.LeagueDictionary;
});
//HTML
<tr ng-repeat="(leagueNum, leagueName) in leagueDictionary">