I'm currently going through a backbone.js tutorial and instead of using the suggested REST service I'm using one from my company as a real-world example. The problem is that the tutorial uses a very simple JSON return from the REST server like this:
{
"name":"Brian"
"age":52
},
"name":"Mary"
"age":"27"
}
... etc.
My own data contains arrays of this type:
{
"records":20,
"customers": [{name:"Simon", age:27},{name:"Mary", age:28}... etc.]
}
I want to get at 'customers' in this case. I believe I can use parse: within a Model for this but this tutorial uses only a Collection and renders that out to the template. Can I do this with just a Collection? Or should I make a Model and use parse:?
You can use a collection -- just override Collection.parse. This is the function that Backbone calls to convert the raw AJAX response into model attributes. In your case, you just need it to return response.customers instead of the raw response:
var MyCollection = Backbone.Collection.extend({
parse: function(response) {
return response.customers;
}
});
Related
I am very new in backbone js.
I am trying to filter some specific key and values in backbone js model extend here is the code below.
var items = ["open","close"];
var ReportModel = Backbone.Model.extend({
url: function() {
return tab+".json";
}
});
where tabe is dynamic json file name.In my json file many key value pair are there but I want to load only those key which is mentioned in items list.
I saw some where using parse function but that a;so did not work out.Please do let me know how to filter the specific keys form json using the backbone.
I also tried creating a dict from json and pass it to model like.
var ReportModel = Backbone.Model.extend({
"open":{.......}
});
but there I am getting issue.
throw new Error('A "url" property or function must be specified');
Please help me out with this.
You are missing some steps to be succesfull on your task.
First a note about the error: Backbone expects a string on the url property while you're passing a function. If you want to use a function to return your url dinamically use urlRoot.
Now onto the real coding:
since you talk about a json file that has multiple key value, maybe you should declare your model as a key-value object, and then create a Backbone.Collection that will wrap your models.
A Backbone.Collection expose a lot of utilities that can help us modeling the results, in this case by using the where() function of our collection you will be able to filter the data after you have retrieved from the remote file.
Alternatively to filter your collection if you need more control over the function you can always call the undescore function filter() .
Please refer to the official documentation of underscore and backbone, as you will find a lot of functions that can help you and most of them have an example that shows how to use them.
Now that we have everything lets create our Backbone.Collection that will wrap your already defined model:
var ReportCollection = Backbone.Collection.extend({
model: ReportModel,
urlRoot: function(){
return 'yoururl.json';
}
});
now if you want to filter the result you can simply fetch the collection and perform a filter on it:
var myReports = new ReportCollection();
//call the fetch method to retrieve the information from remote
myReports.fetch({success: function(){
//the collection has been fetched correctly, call the native where function with the key to be used as a filter.
var filteredElements = myReports.where({my_filter_key : my_filter_value});
});
in your filteredElements you will have an array of object made up of all the model that matched the key/value passed to the where function.
If you need a new Collection from that you just need to pass the result as argument: var filteredCollection = new ReportCollection(filteredElements);
You can use _.pick() in the parse method as shown below:
var items = ["open", "close"];
var ReportModel = Backbone.Model.extend({
url: function() {
return tab + ".json";
},
parse: function(response) {
return _.pick(response, items);
}
});
I'm developing a RESTful API for a Quiz app, which is going to be built with Backbone.js and Marionette. I'm quite new to backbone and was wondering what de best URL structure would be. I have the following resources:
Answer,
Question which contains Answers,
Question Group which contains Questions,
Quiz which contains Question Groups.
Two possible URL structures come to mind:
GET /quizzes/:id
GET /quizzes/:id/questiongroups
GET /quizzes/:id/questiongroups/:id
GET /quizzes/:id/questiongroups/:id/questions
GET /quizzes/:id/questiongroups/:id/questions/:id
GET /quizzes/:id/questiongroups/:id/questions/:id/answers
or:
GET /quizzes/:id
GET /quizzes/:id/questiongroups
GET /questiongroups/:id
GET /questiongroups/:id/questions
...
Now, I have been trying to use both of these options. With the first one, I can't figure out how to define the collections as a property of the parent models in Backbone so that I can use fetch() on them. The problem with the second option is a bit different: as I understand it, Backbone derives the url for a model from its collection, but the collection is a child of another resource, whereas the url for getting a single resource uses another collection, namely the global set of resources.
I'm pretty sure I'd have to override url() in both cases. I tried some things but didn't come up with anything useable at all. Also, I'd rather not override every single url()-model in the app, changing the API structure to suit the preferences of Backbone seems like a better option to me.
Any pointers as to what seems the right way to do it with Backbone would be great!
Thanks
If questiongroups can only appear in a single quiz, then the first option (the hierarchical one) is an obvious choice. To comply with RESTful conventions, you might want to consider using singular nouns instead: /quiz/:id/questiongroups/:id/question/:id/answer/:id
To solve your fetching problem, I would recommend using nested backbone models as per this answer: https://stackoverflow.com/a/9904874/1941552. I've also added a cheeky little parentModel attribute.
For example, your QuizModel could look something like this:
var Quiz = Backbone.Model.extend({
urlRoot: '/quiz/', // backbone appends the id automatically :)
defaults: {
title: 'My Quiz'
description: 'A quiz containing some question groups.'
},
model: {
questionGroups: QuestionGroups,
},
parse: function(response){
for(var key in this.model){
var embeddedClass = this.model[key];
var embeddedData = response[key];
response[key] = new embeddedClass(embeddedData, {
parse:true,
parentModel:this
});
}
return response;
}
});
Then, your QuestionGroups model could have the following url() function:
var QuestionGroups = Backbone.Model.extend({
// store metadata and each individual question group
url: function() {
return this.parentModel.url()+'/questiongroup/'+this.id;
}
});
Alternatively, if you don't need to store any metadata, you could use a Backbone.Collection:
var QuestionGroups = Backbone.Collection.extend({
model: QuestionGroup,
url: function() {
return this.parentModel.url()+'/questiongroup/'+this.id;
}
});
I'm afraid I haven't tested any of this, but I hope it can be useful anyway!
I have a collection (an object list) in database. I can fetch it like: collectionModel.fetch()
But then user changes something on that collection. When user clickes on save button, the whole collection list must be update in database. I thought maybe i can delete() the old one first and then create() it with new one but i could'n achive it. I can't use the update() method because in this case i should find which collection elements has changed but i want to update whole list. How can i do that? Thanks for help.
Do you have a REST api in front of that database? That's how Backbone is made to work with. When your JavaScript code runs model.save(); a PUT request is made to your api for that model.
You question is about saving the whole collection, for that if you want to remain within the default implementation of Backbone you will have to go over all the models in the collection and call save for each of them.
If you want to make one single request to your server you will have to implement a custom method inside your collection. Something like:
MyCollection = Backbone.Collection.extend({
saveAll: function() {
var data = this.toJSON();
return Backbone.$.ajax({
data: { objects: data },
url: '/url/in/your/server/to/update/db'
});
}
});
That's going to send the array of all models in your collection converted to JSON to your server.
Again, you want to have a RESTful API on the server side if you want to make your life with Backbone easy.
If you want to reset collection you have to specify "reset" attribute.
collectionList.fetch({
reset: true,
...
});
But I think it's better to just update it:
collectionList.fetch({
remove: false,
update: true,
merge: true,
...
});
This is a very old question, but I had another approach so I thought I'd post it.
Sometimes my collections have a lot of data and the server doesn't get it all. I solved this by using one of the underscore methods that backbone collections have, invoke (also relies on jquery):
MyCollection = Backbone.Collection.extend({
update: function(callback) {
// Invoke the update method on all models
$.when.apply($, this.invoke('update')).then(() => {
// After complete call the callback method (if passsed)
if(callback) {
callback();
}
});
}
});
You can use it by calling collection.update() when the collection has models in it. A similar method can be used for creating or deleting collections, and this should be modifiable to catch errors but I didn't account for that.
I'm building a web application that uses Django for the back-end, and Backbone for a user-responsive front-end.
When the user carries out a search (or loads more results for an existing search) I retrieve results via Ajax, using a partial template served by Django.
This works fine, but I'd also like to update the search filters so that the user cannot refine for querysets with zero results.
For example: if the user searches for cars between $1000-$3000, and no blue cars are available, I want to grey out the "blue" colour selector in the search form. So as well as returning the results, I also return an JSON object with the available colours.
My question is this: I know how to inject the results into an HTML element of the page, and I know how to return JSON in the template fragment. But how can I access that JSON from inside Backbone?
This is my Backbone model. It listens for search events, when it performs an Ajax request, and sets its 'results' property to the template fragment returned by Ajax:
var SearchModel = Backbone.Model.extend({ ..
performSearch: function() {
$.get(this.get("querystr"), function(html) {
self.set("results", html);
// TODO: How to extract JSON results
});
} ...
});
The template fragment returned by Django over Ajax contains both HTML and JSON:
<script type="text/javascript">
var queryset_propeties = JSON.parse('{{queryset_properties|jsonify|escapejs}}');
</script>
<ul id="results">
{% for results in results %}
<li>{{ result }}</li>
{% endfor %}
</ul>
I also have some Backbone code that listens for changes to the results property of the search model, and updates the front-end:
var SearchResultsView = Backbone.View.extend({ ...
initialize: function() {
this.model.on("change:results", this.displayResults, this);
this.render();
} ...
displayResults: function(model, results) {
this.$el.html(self.model.get('results'));
// TODO: Use queryset_properties object to update search filters?
console.log('queryset_properties', queryset_properties);
} ...
});
My problem is that the queryset_properties object is not up to date, and I don't know how to access it from inside the Backbone code.
I have thought of the following:
Make two $.get calls, one to an HTML file and one to a JSON file. I'd like to avoid this because of the extra HTTP overhead.
Only return JSON via Ajax, and use this to update all the HTML in Backbone, rather than returning the template from Django. I want to avoid this though, because by returning HTML, the Django application works for non-JS users (like search bots), which is important.
Somehow extract the JSON object from the returned HTML, and set it as a Backbone model attribute that I can then use in the usual way - but how to do this, short of using a regex or something equally messy?
Any ideas?
My question is this: I know how to inject the results into an HTML
element of the page, and I know how to return JSON in the template
fragment. But how can I access that JSON from inside Backbone?
Instead of having those $.get requests, use Backbone fetch. It has the appropriate success, and error callbacks just like jQuery's $.get.
In order to do it the Backbone way, first you have to set up your model's URL:
var SearchModel = Backbone.Model.extend({
url: this.get("querystr")
});
after initializing your model call fetch:
var sm = new SearchModel({querystr: "http://example.com?car"});
sm.fetch({ // calls model's url
success: function (model) {
// you can access the returned json object here by doing:
model.get('price'); // assuming your json looks like { price: 500 }
},
error: function (model) {
}
});
Documentation
Short tutorials
Using Backbone.js I need to handle an array of strings returned from a JSON web service. Should I create a Backbone Collection to fetch this list? Here is the data I get from the web service:
["Question Designer","Adaptive Question Designer","Clickable image map","Essay","Fill in the blanks","Maple-graded","Matching","Mathematical formula","Multipart question","Multiple choice","Multiple selection","Numeric","Palette-based symbolic editor","True/false","Other"]
I've created this simple Collection to fetch the data:
var questionTypesCollection = Backbone.Collection.extend({
url: function() {
return apiBase + '/questions/types';
}
});
But when I try to fetch() the collection, I get this error:
Uncaught TypeError: Cannot use 'in' operator to search for 'id' in Question Designer
It looks like Backbone is trying to parse the strings as a model instead of seeing that it's just a raw string. How can I get the data into a Collection so I can use it in a View?
If you just need the strings, your best bet might be to just let jQuery (or Zepto--whatever has the $) handle the heavy lifting:
var names = [];
$.get(apiBase + '/questions/types', {}, function(result){
names = result;
})
After the fetch completes, the names variable will be populated with the results of your query.
This doesn't make a lot of sense, since backbone collection is designed to be, well, a collection of models.
However, you could override the parse method with your own parser.