Adding into json object with AngularJS - javascript

Let's say I have a animals.json file with the following content:
{
"animals": {
"elephant": {
"size": "large"
},
"mouse": {
"size": "small"
}
}
}
And I'm adding that data to the scope of my controller:
animalsApp.controller('animalsCtrl', function($scope, $http){
$http.get('../../animals.json').success(function(data){
$scope.animals = data.animals;
});
});
Which works perfectly, however let's say I need to get some data from an API that I need to add to $scope.animals, which has the following data:
{
"animal_name": "Leonardo"
}
Which is returned when I go to the api with the jsons data:
http://api.someapi.com/animals/{animals.elepahant} // returns above json
Pretend {animals.elaphant} is the results I get when i loop my json, get a value from it, and get the data from a remote api with the query being a variable of mines, add results to my json and return that new modified json in $scope.animals.
So the final json would look like:
{
"animals": {
"elephant": {
"size": "large",
"name": "Leonardo"
},
"mouse": {
"size": "small",
"name": "Vader"
}
}
}
How can I achieve this?

Normally you would have an array of animals and loop through that array.
Since you have an object the principle is the same. It is important to use a closure for the loop. Will use angular.forEach() to create that closure.
Using for loops by themselves do not create a closure and can be problematic since the loop finishes long before the request responses are returned
$http.get('../../animals.json').success(function(data){
$scope.animals = data.animals;
angular.forEach(data.animals, function(v, animal_type){
var url = 'http://api.someapi.com/animals/' + animal_type;
$http.get(url).success(function(resp){
v.name = resp.animal_name;
});
});
});
There are also alternative ways to write this using chained promises.
I kept it simple for now

Related

Why am I getting an error, "ReferenceError: categories is not defined" in AngularJS?

In my understanding, $scope.categories is already defined. Then why am I getting this error and not able to access data from the Json file?
Here is my controller:
(function(){
app.controller('productsCtrl', ['$scope','$cookies', '$http', function($scope,$cookies,$http){
$http.get("controllers/data.json").then(function (response) {
$scope.categories = response.data;
});
$scope.specials = [categories[0].laptops[1], categories[1].accessories[0]];
}]);
})();
Here is my Json file:
[
{
"laptops": [
{
"name": "Asus Laptop",
"price": 300
},
{
"name": "HP Notebook",
"price": 200
}
]
},
{
"accessories": [
{
"name": "WD Hard Drive",
"price": 100
},
{
"name": "WD Blue SSD",
"price": 700
}
]
}
]
You have assigned response data into $scope.categories, you need to use $scope.categories instead of categories and also you should add into $scope.specials once http call completed like
(function(){
app.controller('productsCtrl', ['$scope','$cookies', '$http', function($scope,$cookies,$http){
$scope.specials = [];
$http.get("controllers/data.json").then(function (response) {
$scope.categories = response.data;
$scope.specials.push($scope.categories[0].laptops[1]);
$scope.specials.push($scope.categories[1]. accessories[0]);
});
}]);
})();
There's actually a few issues here. First is you never define a variable named categories, you have a property of the $scope object named that, so you need to access $scope.categories or the response.data setting it directly.
Second, you have a race condition issue. You are trying to access the values of categories outside the promise, meaning potentially before the get request has returned any data. When you use get().then() like you are, the code after the request doesn't wait for the request to finish before it runs, so whatever is faster runs first. Because one of the two operations running is accessing an external endpoint and the other is local javascript code, the flow is almost guaranteed to be this:
send get request to "controllers/data.json"
set $scope.specials - causing your undefined error
set $scope.categories when get request promise resolves
You need to access the categories inside the promise to guarantee that it actually has been defined at the point you are trying to access it:
$http.get("controllers/data.json").then(function (response) {
$scope.categories = response.data;
$scope.specials = [$scope.categories[0].laptops[1], $scope.categories[1].accessories[0]];
});
It's also generally a bad idea to hard code indexes like this, if the data changes you run into a possible index out of bounds error.

How to define angularjs variables inside script

i am using simple get method
var self= this;
self.items = [];
var fetchGraphs = function() {
return $http.get('/api/graph').then(
function(response) {
self.items = response.data;
alert(self.items);
}, function(errResponse) {
console.error('Error while fetching notes');
});
};`
to get the following JSON data.
{
"Msg": "Running",
"excMsg": null,
"array1": {
"key1": 32,
"key2": 10,
"key3": 24
},
"array2": {
"key4": 42,
"key5": 20,
"key5": 22
}}
Now I have to draw graph for array1 and array2. for that i have to declare the values from get method inside script itself.but its giving me error for
"can not read key1 property."
Can anyone please tell me how to declare array variables that I get from JSON in the another script file
You cannot "declare" variables direct. You must use method or subscribe to event. The easy solution scope.emit and etc. See here:
Working with $scope.$emit and $scope.$on
If you want to use fetchGraphs for example (your method).
fetchGraphs().then(function(result) {
// do something with result array
});
Edit:
If you post more code people here can help you effective.
You have to use then method from fetchGraph or else return data from success method of $http.
My recommendation, you can keep watch on the self.items. so whenever it change you can re-draw your graph.
$scope.$watch(
"self.items",
function () {
//re-draw your graph
$scope.$apply(); // not needed always, just a precaution
}
);

twitter typeahead 0.9.3 remote return URL and json object

in bootstrap 2, I used the following code to post a json object,
$('#typeahead').typeahead({
source: function (query, process) {
var URL = 'http://localhost:8080/autocomplete/search/';
var query = {"t:jsonStringField": {
"name": "model",
"value": "fusion"
},
"t:jsonStringFilter": [
{"name": "year","value": "2009"},
{"name": "make","value": "ford"}
]
};
return $.getJSON(URL,
{ query: JSON.stringify(query)},
function (data) {
return process(data);
});
}
});
Now in twitter tyeahead 0.9.3 they have done away with the source concept and moved to a remote concept, but unfortunately I do no know how to work with it.
$(".typeahead").typeahead({
remote : {
url: 'http://localhost:8080/autocomplete/search/',
replace: function(uri, query) {
var query = {"t:jsonStringField": {
"name": "model",
"value": "fusion"
},
"t:jsonStringFilter": [
{"name": "year","value": "2009"},
{"name": "make","value": "ford"}
]
};
return uri + JSON.stringify(query);
},
filter: function(response) {
return response.matches;
}
return process(resultList);
}
}
Unfortunately it doesn't work, how do I just post the JSON object rather than appending it to the string? Thanks.
In your original code you use $.getJSON. This will send a request (and expects json as result) to: http://localhost:8080/autocomplete/search/?query=%7B%22t%3AjsonStringField%22%3A%7B%22name%22%3A%22model%22%2C%22value%22%3A%22fusion%22%7D%2C%22t%3AjsonStringFilter%22%3A%5B%7B%22name%22%3A%22year%22%2C%22value%22%3A%222009%22%7D%2C%7B%22name%22%3A%22make%22%2C%22value%22%3A%22ford%22%7D%5D%7D
To do the same for Twitter's Typeahead call your replace function of your remote data should return a valid url. In your function the ?query= part of the url is missing.
You will have to set: url: 'http://localhost:8080/autocomplete/search/?query=',
You also will have to urlencode you json input maybe.
Note: you will not need the line return process(resultList); You will have to use the filter function to convert your json results to valid data:
The individual units that compose datasets are called datums. The
canonical form of a datum is an object with a value property and a
tokens property.
you could use templates to style your dropdown results, see: http://twitter.github.io/typeahead.js/examples/
By default, the dropdown menu created by typeahead.js is going to look
ugly and you'll want to style it to ensure it fits into the theme of
your web page.
You will need the additional CSS from https://github.com/jharding/typeahead.js-bootstrap.css to style the default dropdown for Bootstrap.

AngularJS / Restangular routing "Cannot set property 'route' of undefined"

I have a AngularJS-based frontend using restangular to fetch records from a Django backend I've built.
I'm making a call for a client list with the following:
var app;
app = angular.module("myApp", ["restangular"]).config(function(RestangularProvider) {
RestangularProvider.setBaseUrl("http://172.16.91.149:8000/client/v1");
RestangularProvider.setResponseExtractor(function(response, operation) {
return response.objects;
});
return RestangularProvider.setRequestSuffix("/?callback=abc123");
});
angular.module("myApp").controller("MainCtrl", function($scope, Restangular) {
return $scope.client = Restangular.all("client").getList();
});
Chrome is showing the backend returning data with an HTTP 200:
abc123({
"meta": {
"limit": 20,
"next": "/client/v1/client/?callback=abc123&limit=20&offset=20",
"offset": 0,
"previous": null,
"total_count": 2
},
"objects": [{
"id": 1,
"name": "Test",
"resource_uri": "/client/v1/client/1/"
}, {
"id": 2,
"name": "Test 2",
"resource_uri": "/client/v1/client/2/"
}]
})
But once that happens I'm seeing the following stack trace appear in Chrome's console:
TypeError: Cannot set property 'route' of undefined
at restangularizeBase (http://172.16.91.149:9000/components/restangular/src/restangular.js:395:56)
at restangularizeCollection (http://172.16.91.149:9000/components/restangular/src/restangular.js:499:35)
at http://172.16.91.149:9000/components/restangular/src/restangular.js:556:44
at wrappedCallback (http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js:6846:59)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js:6883:26
at Object.Scope.$eval (http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js:8057:28)
at Object.Scope.$digest (http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js:7922:25)
at Object.Scope.$apply (http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js:8143:24)
at done (http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js:9170:20)
at completeRequest (http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js:9333:7) angular.js:5754
I did a breakpoint on line 395 in in restangular.js:
L394 function restangularizeBase(parent, elem, route) {
L395 elem[config.restangularFields.route] = route;
The first time it hits the breakpoint elem is just an object and route has the value of client.
The second time the breakpoint is hit elem is undefined and route has the value of client.
Any ideas why elem would be undefined the second time around?
When requesting lists, Restangular expects the data from the server to be a simple array. However, if the resulting data is wrapped with result metadata, such as pagination info, it falls apart.
If you are using Django REST Framework, it will return results wrapped like this:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "Foo"
},
{
"id": 2,
"name": "Bar"
}
]
}
To translate this, you need to create a response extractor function. It's easiest to specify in the module config:
angular.module('myApp', ['myApp.controllers', 'restangular']).
config(function(RestangularProvider) {
RestangularProvider.setBaseUrl("/api");
// This function is used to map the JSON data to something Restangular
// expects
RestangularProvider.setResponseExtractor(function(response, operation, what, url) {
if (operation === "getList") {
// Use results as the return type, and save the result metadata
// in _resultmeta
var newResponse = response.results;
newResponse._resultmeta = {
"count": response.count,
"next": response.next,
"previous": response.previous
};
return newResponse;
}
return response;
});
});
This rearranges the results to be a simple array, with an additional property of _resultmeta, containing the metadata. Restangular will do it's thing with the array, and it's objects, and you can access the _resultmeta property when handling the array as you would expect.
I'm the creator of Restangular.
The restangularizeBase function is called first for your collection and then for each of your elements.
From the StackTrace, the element is OK, but once the collection is sent to restangularizeBase, it's actually undefined. Could you please console.log response.objects? Also, please update to the latest version.
Also, for the default request parameter, you should be using defaultRequestParams instead of the requestSuffix. requestSuffix should only be used for the ending "/"
Let me know if I can help you some more!

Iteration in handlebar using backbone

I'm using backbone and handlebars for templating and i'm new to this.
My current json is in the below format and the code works fine.
[
{
"id": "10",
"info": {
"name": "data10"
}
},
{
"id": "11",
"info": {
"name": "data11"
}
}
]
But when i change my json structure to something like shown below i'm having difficulty in getting things to be populated.
{
"total_count": "10",
"dataElements": [
{
"id": "10",
"info": {
"name": "data10"
}
},
{
"id": "11",
"info": {
"name": "data11"
}
}
]
}
How can i populate name, info and total_count keeping the current code structure ?
JSFiddle : http://jsfiddle.net/KTj2K/1/
Any help really appriciated.
A few things that you need to do in order for this to work.
Replace Backbone's core 'reset' on your collection with a custom one that understands the data you are passing to it. For example:
reset: function (data) {
this.totalCount = data.total_count;
Backbone.Collection.prototype.reset.call(this, data.dataElements);
}
Now when you reset your collection, it will pull the total_count out of the object you are resetting it with, and use Backbone's core reset with the dataElement array. Keep in mind you may have to do a similar thing with 'parse' if you're intending on pulling this from the server.
I'd recommend that (if your example looks anything like the real code you're working with) you reset your collection before getting to rendering.
var dataCollectionList = new dataCollection();
dataCollectionList.reset(jsonData);
var App = new AppView({model : dataCollectionList});
Now in your view's "render" method you can grab the 'totalCount' property off the collection -
render : function() {
//Should spit the total count into the element, just as an example
this.$el.append(this.model.totalCount);
//or console.log it
console.log(this.model.totalCount);
return this;
}
Voila. Side note - as someone who works with Backbone a lot, it drives me nuts when people set an attribute of something like "model" (i.e. peopleModel, itemModel, etc) and it ends up being a backbone collection. It's much clearer to name it after what it is - though some MVC purists may disagree a bit.
Also, in this code block:
_.each(this.model.models, function (myData) {
$(this.el).append(new ItemView({model:myData}).render().el);
}, this);
You don't need to do _.each(this.model.models.......). Since you're working with a collection, the collection has a built in 'each' method.
this.model.each(function (myData) { ..... } , this);
Quite a bit cleaner.

Categories

Resources