Using a backbone collection I am trying to fetch JSON from a page. However browsing to this page does give me a JSON, using the collection.fetch does not.
Looking in Firebug I see a:
"GET http://survey/api/list_surveys 200 OK 4ms"
This text however is in red and the Response tab is empty.
The Model:
var SurveyList = Backbone.Model.extend({
defaults: {
"sid": "",
"title": '',
"surveyUrl": ""
},
initialize: function() {
console.log('MODEL AANGESPROKEN');
}
});
The collection:
var Surveys = Backbone.Collection.extend({
model: BGGZ.SurveyList,
url: 'http://survey/api/list_surveys'
});
The JSON:
[{
"sid":"12345",
"surveyls_title":"test 1",
"survey_url":"http://survey/index.php?newtest=Y&sid=12345"
},
{
"sid":"54321",
"surveyls_title":"Test 2",
"survey_url":"http://survey/index.php?newtest=Y&sid=54321"
}]
Does anyone has a solution?
I already tried a parse in the collection, but this didn't help.
Might this has something to do with json with padding?
If so How can I resolve this?
So I found the solution here.
As this is a remote server I am getting the json from I can use JSONP.
In the collection.fetch() Backbone uses jQuery's $.ajax() method to get the data.
So you can place your $.ajax settings in the fetch:
myCollection = new Surveys();
myCollection.fetch({
dataType: "jsonp",
success: function(data) {
console.log(data);
},
error: function() {
console.log('error');
}
});
Now this will not work if your API doesn't expect a JSONP.
JSONP will give a callback parameter to your API. So did your API call first looked like this:
http://survey/api/list_surveys
with JSONP it will now look like this:
http://survey/api/list_surveys?callback=jQuery12654876544
Your API should not return the standard JSON, because jQuery / backbone is expecting the data in a callback function.
if the JSON first looked like this:
{
"sid":"12345",
"surveyls_title":"test 1",
"survey_url":"http://survey/index.php?newtest=Y&sid=12345"
}
you must now add the API to this callback function:
jQuery12654876544({
"sid":"12345",
"surveyls_title":"test 1",
"survey_url":"http://survey/index.php?newtest=Y&sid=12345"
})
Now you're done.
Related
I'm setting up a project using django-tastypie REST API and AngularJS. I'm fine with reading things from the json file through angular, but I cannot find a decent tutorial that would show me how to make even a simple CRUD application that isn't saving all the information in an object or whatever, but is manipulating the database through the tastypie api. Can any of you show me a tutorial of such sort or maybe just show me some sample code for this?
Thank you.
Use $resource - A factory which creates a resource object that lets you interact with RESTful server-side data sources.
Let's say you have Django model Book, and tastypie resource named BookResource. It's URL is /api/v1/book/. As you know, this URL actually is a resource, that means you can manipulate data in your Book model with GET, POST, DELETE, etc. requests.
You can "map" the Angular $resource to this API resource in a way:
someModule.factory('bookResource', ['$resource', function($resource) {
var apiResourceUrl = "/api/v1/book/:bookId/";
// id - your model instance's id or pk, that is represented in API resource objects.
var resource = $resource(apiResourceUrl, {bookId: '#id'}, {
all: {
method: 'GET', params: {}, // GET params that will included in request.
isArray: true, // Returned object for this action is an array (miltiple instances).
},
get: {
method: 'GET',
},
// [Define custom save method to use PUT instead of POST.][2]
save: {
/* But, the PUT request requires the all fields in object.
Missing fields may cause errors, or be filled in by default values.
It's like a Django form save.
*/
method: 'PUT',
},
// [Tastypie use POST for create new instances][3]
create: {
method: 'POST',
},
delete: {
method: 'DELETE',
},
// Some custom increment action. (/api/v1/books/1/?updateViews)
updateViews: {
method: 'GET',
params: {"updateViews": true},
isArray: false,
},
});
}]);
someModule.controller('bookCtrl', ['$scope', '$routeParams', 'bookResource',
function ($scope, $routeParams, bookResource) {
if ("bookId" in $routeParams) {
// Here is single instance (API's detail request)
var currentBook = bookResource.get({bookId: $routeParams.bookId}, function () {
// When request finished and `currentBook` has data.
// Update scope ($apply is important)
$scope.$apply(function(){
$scope.currentBook = currentBook;
});
// And you can change it in REST way.
currentBook.title = "New title";
currentBook.$save(); // Send PUT request to API that updates the instance
currentBook.$updateViews();
});
}
// Show all books collection on page.
var allBooks = bookResource.all(function () {
$scope.$apply(function(){
$scope.allBooks = allBooks;
});
});
// Create new
var newBook = new bookResource({
title: "AngularJS-Learning",
price: 0,
});
newBook.$save();
}]);
Angular's docs provide more information how to make usage of resource really incredibly.
Here is the problem with urls. As I remember, Angular will send request to /api/v1/books/1 (without slash in the end) and you'll get 404 from tastypie. Let me check this.
[2] http://django-tastypie.readthedocs.org/en/latest/interacting.html#updating-an-existing-resource-put
[3] http://django-tastypie.readthedocs.org/en/latest/interacting.html#creating-a-new-resource-post
I am using the select2 plugin and I need to to use the transport-function to perform the ajax request on my own, because I need to set API Keys in the request-header. But as soon as I do this, select2 responses the results correctly and also formats and displays it like I want, but the results shown are not selectable. I can neither click at them, nor navigate to them with the arrow-keys, nor is there any mouseover effect when I go over them with the mouse.
Here's some code (I want to show suggestions for usernames):
ajax: {
data: function (term, page) {
return {
Searchtext: term
};
},
transport: function(queryParams){
// Api.Ajax delivers { Ajax: *the $.ajax object*, RequestId: *some requestId* }
var req = Api.Ajax("/api/suggestion/share/" + queryParams.data.Searchtext, "POST", { UserOnly: true }, queryParams.success);
return req.Ajax;
},
// parse the results into the format expected by Select2.
results: function(resp, page) {
return {
results: resp.Data,
more: false
};
}
},
Like I said, as soon as I use my own Ajax-function by implementing the transport-function, the results in the dropdown list are all shown, but not selectable.
Is this a bug, or am I doing something wrong?
#thnew This Answer will show you how to set request headers without requiring the transport function.
I am using a Model like this:
var SidebarCategory = Backbone.Model.extend({
urlRoot: 'sidebar',
defaults : {
title : '',
items: ''
}
});
And fetching this JSON via GET:
{"id":"foo","title":"TITLE","items":"bar baz"}
But the Model itself does not take the incoming data.
var foo = new SidebarCategory({id: 'foo'});
foo.fetch();
console.log(foo.toJSON());
Just taking the id and ignore the rest.
Did I miss something?
What could be the problem?
As #CD pointed it out , the fetch is asynchronous so you have 2 options for a call back like this :
foo.fetch({
success : function(data){
console.log(JSON.stringify(data));
}
});
OR
foo.fetch();
foo.on('reset',function(data){
console.log(JSON.stringify(data));
},this);
You could use either one of the above to deal with the asynchronous call.
fetch performs an asynchronous HTTP (Ajax) request, so you should pass fetch a success callback:
foo.fetch({
success: function(){
console.log(foo.toJSON());
}
});
I'm using jquery.couchdb.js to query my CouchDB database. The view I want to query has both map and reduce functions within. When sending the basic query as shown below:
$(document).ready(function() {
view_name = db_name+'/jobs_by_mod_stat'
options = {'group': 'true' , 'reduce': 'true' };
mod_stat = {};
$db.view(view_name , {
success: function(data) {
console.log(data)
for (i in data.rows) {
console.log(data.rows[i].value);
}
},
error: function(e) {
alert('Error loading from database: ' + e);
}
});
});
I see a sensible log for the data, indicating the query has been successful. However, changing the line:
$db.view(view_name , {
To
$db.view(view_name , options, {
I don't get a success outcome from the Ajax query, but an error message is not shown either. Firebug shows the query being sent, and the JSON data returned looks sensible:
{"rows":[
{"key":["template","completed"],"value":2},
{"key":["template","running"],"value":2},
{"key":["template","waiting"],"value":6}
]}
But the success function is not entered. Any ideas why I'm seeing this behaviour, I did wonder if it's a bug in jquery.couch.js (I have couchdb 1.1.0).
Cheers.
I've had a bit of trouble myself with the list function, until I went and looked through the source code of jquery.couch.js (the online documentation I found at http://bradley-holt.com/2011/07/couchdb-jquery-plugin-reference/ seems to be outdated).
Basically, the parameters for view and list are different, the list having an extra parameter for the options, instead of having everything under the same parameter as with views.
View:
$.couch.db('yourdb').view('couchapp/' + viewName, {
keys: ['keys here'],
success: function (data) {
}
});
List:
$.couch.db('yourdb').list('couchapp/' + listName, viewName, {
keys: ['keys here']
}, {
success: function (data) {
}
});
I have a rails controller which sends a mash-up of models as a global json object. Something like this
{
dogs : { species: {}, ...},
cats : { food: {}, ...},
foxes : { },
...,
...
}
On my client side, I have all these entities neatly segregated out into different backbone models and backbone collections.
On some onchange event, I need to send a mashup of some model attributes back to the server as a HTTP POST request and the server sends a response which again spans values across a few models.
How do I setup Backbone.sync to deal with such an ajax scenario? I do not want to change the rails backend because its quite a steady implementation. Or do I make vanilla $.ajax requests through jQuery in one of my backbone views and handle it in a callback on ajax success/failure?
I think there are a couple of ways to do this via backbone. I think I'd start out with a model to represent the mashup:
var MashupModel = Backbone.Model.extend({
});
Then you can pass in any models like you would normally (or a collection for that matter):
var my_mash = new MashupModel({
dog: dogModel.toJSON(),
cat: catModel.toJSON(),
foxes: foxCollection.toJSON()
});
// do stuff if you need...
Then do what you want when the response comes back like normal:
my_mash.save({}, {
success: function(model, response) {
// do stuff here to put new data into the proper models / collections
},
error: function() { alert("I FAIL!"); }
});
That's all well and good... however, I think it would be better to push the above down into the MashupModel object instead of at the request level. Again, several ways:
var MashupModel = Backbone.Model.extend({
initialize: function(attrs) {
// can't remember the actual code, but something along the lines of:
_.each( attrs.keys, function(key) {
this.set(key, attrs.key.toJSON();
});
},
save: function(attrs, opts) {
var callback = opts.success;
opts.success = function(model, response) {
// do your conversion from json data to models / collections
callback(model, response);
};
// now call 'super'
// (ala: http://documentcloud.github.com/backbone/#Model-extend)
Backbone.Model.prototype.set.call(this, attrs, opts);
}
});
Or you could override toJSON (since backbone calls that to get the attrs ready for ajax):
// class definition like above, no initilize...
...
toJSON: function() {
// again, this is pseudocode-y
var attrs = {};
_.each( this.attributes.keys, function() {
attrs.key = this.attributes.key.toJSON();
});
return attrs;
}
...
// save: would be the same as above, cept you'd be updating the models
// directly through this.get('dogs').whatever...
Now, you can just do:
var my_mash = new MashupModel({
dog: dogModel,
cat: catModel,
foxes: foxCollection
});
// do some stuff...
my_mash.save({}, {
success: function(model, response) {
// now only do stuff specific to this save action, like update some views...
},
error: function() { alert("I FAIL!"); }
it would be possible, but may be difficult to modify backbone.sync to work with this structure. i'd recommend going with plain old jquery $.ajax requests. then on success, pull the info apart and populate your collections.
$.get("/whatever", function(data){
catCollection.reset(data.cats);
dogCollection.reset(data.dogs);
// etc
});
data = {};
data.cats = catCollection.toJSON();
data.dogs = dogCollection.toJSON();
// etc
$.post("/whatever", data);