I have the below that is setup and working properly.
require(['models/profile'], function (SectionModel) {
var sectionModel = new SectionModel({id: merchantId, silent: true});
sectionModel.fetch({
success: function (data) {
$('#merchant-name').html(data.attributes.merchantName);
}
});
});
But it will only work in one instance. I am wondering how to correctly edit the above code to allow multiple instances.
<h3 id="merchant-name"></h3>
The content is generated within 'Save' function.
merchantName:$('#merchantName').val(),
What you want to do is set up the rest of the components for the Backbone application. The beauty of Backbone.js is it's ability to separate collections, models and views so your logic stays in a proper place.
You'll want to use an AJAX call to retrieve your models from the server using a Collection. Then, use the collection's reset function.
Here's an example of how you might fetch a collection of models from the server.
var MyCollectionType = Backbone.Collection.extend({
getModelsFromServer:function()
{
var me = this;
function ajaxSuccess(data, textStatus, jqXHR)
{
me.reset(data);
}
$.ajax(/* Insert the ajax params here*/);
}
});
var collectionInstance = new MyCollectionType({
model:YourModelTypeHere
});
collectionInstance.getModelsFromServer();
Then, to render each one, you'll want to make a View for each model, and a Collection View. There are a lot of resources though on learning basic Backbone.js and I feel that you might benefit from looking at a few of those.
Keep in mind that Backbone collections will by default merge models with the same id. 'id' usually references a model in the backend of an application, so make sure each id is actually what you want it to be. I work with an application that has a non-Restfull back end, and so ID's are never transferred to the front end.
There are some excellent resources available to begin starting with Backbone.js.
https://www.codeschool.com/courses/anatomy-of-backbonejs
(This is a free course up to a point, and a great starter.)
http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-backbone-js/
http://javascriptissexy.com/learn-backbone-js-completely/
Related
I have an app model and apps have an id and name.
this.resource("apps", function() {
this.route('show', { path: ':app_id' });
});
I'd like to make the show view show metrics about the app, but the query is pretty intense, so I don't want to include it in the call to the index view (let's say this is a table).
I'm not sure if this is possible with ember-data because the app would already be in the store with the simplified payload and not be re-requested for the show view to get the metrics.
Then my head went to making metrics a completely different model accessible from apps/1/metrics and then making it another model and everything.
But if I sideload the data, i have to provide ID references to the metrics for a particular app. And it's hasOne so there's not really IDs as there would be for a database backed model.
What's the best way to load in additional data about a model or expand the information supplied in the show view?
The backend is Rails and this is an ember-cli project.
The easiest way is to retrieve the data in the Route's afterModel handler:
var ShowRoute = Ember.Route.extend({
model: function(params) {
// Load the model and return it.
// This will only fire if the model isn't passed.
},
afterModel: function(model, transition) {
// Load the rest of the data based on the model and return it.
// This fires every time the route re-loads (wether passed in or via model method).
}
});
Consider the Backbone app listed below. When its started, it creates a restaurants collection and view of local restaurants in the users area. The user can later change their search area and then update the entire collection by submitting a search form.
The Problem: How can I reference the collection in the search method if it isn't related to the view?
Currently, I'm setting the restaurant collection and view as properties of the App view itself so that it can be easily referenced from within the method, but this seems in appropriate because the view isn't solely related to that collection.
Is it better to reference the collection as I have or is it better to reference it as App.collection, as backbone does when you in create views realted to collections with Backbone.View.extend({collection: mycollection})?
App view:
var App = new (Backbone.View.extend({
...
events: {
submit: "search",
},
start: function(){
App.render();
App.restaurants = new App.Collections.Restaurants();
App.restaurantsView = new App.Views.Restaurants({collection: App.restaurants});
App.restaurants.fetch({});
},
search: function(e){
e.preventDefault();
var payload = $( App.targets.searchForm ).serializeObject();
App.restaurants.fetch({data: payload, processData: true,});
},
...
}):
Those are big questions: How to structure a Backbone application, how to define and maintain module boundaries, and how to decouple modules while allowing notifications where needed, like between search and restaurants?
Big-picture answer: Look at something like Marionette. It's a framework built on top of Backbone, it makes it easy to follow a lot of best practices, and it does some heavy lifting for you. David Sulc has an excellent book about Marionette that covers all of those topics & more. Highly recommended.
Immediate-problem answer: Directly referencing objects outside of a single module (like search) is a bad idea. It couples together concerns that should be kept separate. At a minimum, think of adding an application-level event bus: search can trigger a search event, and the restaurant code can listen for that event and act on its collection in response. You could use something like the backbone.wreqr library's EventAggregator class, which is tailor-made for this kind of thing. In a pinch, you could use an instance of Backbone.Events to roll your own:
App.vent = _.extend( {}, Backbone.Events );
Trigger an event from search code like so:
App.vent.trigger('search', payload);
And in your start function, register to listen for that event:
App.vent.on('search', function(payload) {
App.restaurants.fetch({data: payload, processData: true,});
});
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 have a collection that can potentially contain thousands of models. I have a view that displays a table with 50 rows for each page.
Now I want to be able to cache my data so that when a user loads page 1 of the table and then clicks page 2, the data for page 1 (rows #01-50) will be cached so that when the user clicks page 1 again, backbone won't have to fetch it again.
Also, I want my collection to be able to refresh updated data from the server without performing a RESET, since RESET will delete all the models in a collection, including references of existing model that may exist in my app. Is it possible to fetch data from the server and only update or add new models if necessary by comparing the existing data and the new arriving data?
In my app, I addressed the reset question by adding a new method called fetchNew:
app.Collection = Backbone.Collection.extend({
// fetch list without overwriting existing objects (copied from fetch())
fetchNew: function(options) {
options = options || {};
var collection = this,
success = options.success;
options.success = function(resp, status, xhr) {
_(collection.parse(resp, xhr)).each(function(item) {
// added this conditional block
if (!collection.get(item.id)) {
collection.add(item, {silent:true});
}
});
if (!options.silent) {
collection.trigger('reset', collection, options);
}
if (success) success(collection, resp);
};
return (this.sync || Backbone.sync).call(this, 'read', this, options);
}
});
This is pretty much identical to the standard fetch() method, except for the conditional statement checking for item existence, and using add() by default, rather than reset. Unlike simply passing {add: true} in the options argument, it allows you to retrieve sets of models that may overlap with what you already have loaded - using {add: true} will throw an error if you try to add the same model twice.
This should solve your caching problem, assuming your collection is set up so that you can pass some kind of page parameter in options to tell the server what page of options to send back. You'll probably want to add some sort of data structure within your collection to track which pages you've loaded, to avoid doing unnecessary requests, e.g.:
app.BigCollection = app.Collection.extend({
initialize: function() {
this.loadedPages = {};
},
loadPage: function(pageNumber) {
if (!this.loadedPages[pageNumber]) {
this.fetchNew({
page: pageNumber,
success: function(collection) {
collection.loadedPages[pageNumber] = true;
}
})
}
}
});
Backbone.Collection.fetch has an option {add:true} which will add models into a collection instead of replacing the contents.
myCollection.fetch({add:true})
So, in your first scenario, the items from page2 will get added to the collection.
As far as your 2nd scenario, there's currently no built in way to do that.
According to Jeremy that's something you're supposed to do in your App, and not part of Backbone.
Backbone seems to have a number of issues when being used for collaborative apps where another user might be updating models which you have client side. I get the feeling that Jeremy seems to focus on single-user applications, and the above ticket discussion exemplifies that.
In your case, the simplest way to handle your second scenario is to iterate over your collection and call fetch() on each model. But, that's not very good for performance.
For a better way to do it, I think you're going to have to override collection._add, and go down the line dalyons did on this pull request.
I managed to get update in Backbone 0.9.9 core. Check it out as it's exactly what you need http://backbonejs.org/#Collection-update.