I am new to Backbone. We have developed some POC.
We have one button on view. On every button click below function will be called and making server call to get the data.
Every time we are creating new view by passing collection to the view.
Is this a correct way to create view more than one at one time?
renderData : function(pageInfo, inputModel) {
MySpinner.start();
//Fetch the data
var deferedData = Application.request("app:generateData"inputModel);
$.when(deferedData).done(function(myCollection) {
var myView = new MyView({
collection : myCollection
});
//Display the view.
Application.mainViewRegion.show(myView);
Application.stop();
});
}
Have you considered using Marionette's CollectionView and resetting its collection using the response?
From the docs:
When the collection for the view is "reset", the view will call render on itself and re-render the entire collection.
Related
I would like to know if it is possible to completely change the model, not only it's values but it's actual reference on a View that´s already been initialized. I have seen this question but it only refers to model values.
I have created a reusable view (Which renders a Google Map) and it is used by other different views, not by creating the reusable module as a child view but navigating to a URL which shows a full screen map, which is an instance of my view.
This map View, receives a model on initialization which is later modified within this view, and since it is the same model reference it also updates the view's model that invoked (requested the navigation to) the map, I am invoking all views from my router and it is aware of which views are created and holds references to all of them so i can share models between view this way.
var myRouter= Backbone.Router.extend({
routes : {"viewA(/)" : "invokeViewA"
"viewA/map" : "invokeViewAMap"
//... same with ViewB
},
//...
invokeViewAMap : {
if (mapViewInstance){
//if the map already exists i want to update its model with the viewAinstance.model
} else{
//there is no map view yet so let's create it and send the model i need updated later on
mapViewInstance = new AddressFinderMapView({model : viewAInstance.model});
}
},
invokeViewBMap {
//similar to the above but using viewBInstance's model
}
});
var AddressFinderMapView = Backbone.View.extend({
//initialize function
//render function
//init google map
events : {"click button" : "updateModelWithAddress"},
updateModelWithAddress : function(){
//Here is where i need to update the model values, of the model that this view has received, so far it works with the model that was sent on initialization
this.model.set({
//latitude and longitude from the map
})
}
});
Additional thoughts:
I can stop reusing this Map View instance and create more instances, but that would defeat the purpose of calling Google Maps just once, at the end of the day, the map is used to select a location and return it to the view that invoked it.
I used to have an event being triggered from the Map View, so the other views would listen and update their own models, but since different views can live at the same time, it would update all models that were listening which is not what i wanted
I could send the current route on the trigger along with the latitud and longitude, and let each view filter whether it's their model that must be updated, but this feels more like a hack rather than an structured solution.
Just assign the new modal to the view instance's model property, like:
viewInstance.model = newModelInstance;
I'm trying to make an application with Backbonejs and this is the first time I use a Front-end Javascript framework, except for JQuery.
I didn't yet understand how the rendering works.
My Example:
render: function() {
var events = this.collection.fetch({
success: function (model, response) {
console.log("Response is " + response);
var events = model.toJSON();
console.log(events.length);
console.log(model.toJSON());
return model.toJSON();
},
error: function(){
console.log("Errore during data fetch");
}
});
this.$el.html(this.template({events:this.collection.toJSON()}));
console.log("Event list: " + events.length);
},
The code above is the render callback of my view.
Inside the success collection fetch I get the data in json format from my API and I successfully log it on the console, but outside the fetch I don't have this data anymore and my view collection seems to be just an empty Backbone object.
Can somebody explain what I'm doing wrong and how rendering works ?
Enrico :)
Well, what render basically does it just inject some html code into the view's el (element).
It could be done by using template engine such as handlebars or mustache or just using the generic backbone (actually underscore) template, as you did in your code.
What we usually do, is initializing the view the way that it listens to the model it is attached to, or collection in your case.
It is not a very good practice to fetch data within render, as it should only use the already fetched data.
So what you could do, is to initialize your view this way:
var View = Backbone.View.extend({
initialize : function(){
this.listenTo(this.collection, "change", this.render);
},
redner : function(){
this.$el.html(this.template({events:this.collection.toJSON()}));
}
});
In this code, the view listens to changes occurring on the collection attached to it. When I say changes, I mean changes to any of the models in the collection, including when the collection is fetched for the first time.
So the listenTo method will fire the render method everytime there's a change in your collection, and by that the whole view will re-render.
This obviously happens when the collection is fetched for the first time.
And for the fetching itself? once youv'e binded the change event to the view, you can fetch it anywhere in your app, not necessarily inside the view.
I have a webapp based on Backbone.js with a list. The entries of the list are coming from a REST API. This list (JSON array) updates from time to time. I want to update my list in the frontend too, without reloading the page.
I thought about using a poller to update the file list with every new object returned by the API. However, the poller is not the problem here, I first need a function to add just the new models to the file list.
The API returns a JSON list, based on this model:
Xff = Backbone.Model.extend({
defaults: {
id: null,
name: "",
language: "en",
timestamp: 0,
status: null,
progress: 10,
duration: 0
}
});
This is the collection. restUri points to the REST API and with /files it gets the complete file list.
XffCollection = Backbone.Collection.extend({
model: Xff,
comparator: function(a, b) {
return (a.get("timestamp") > b.get("timestamp") ? -1 : 1);
},
url: restUri + "files"
});
This is the AppView object. It uses the XffCollection, as explained above.
app = new AppView({
collection: new XffCollection()
});
AppView is a regular backbone view...
AppView = Backbone.View.extend({ .... })
Using app.collection.fetch() I can fire the request (visible in firebug), but the list is not updated. I also have a addAll() function, but then it just appends the new file list to the old file list.
The addAll:
addAll: function() {
this.collection.chain().sort().each(this.addOne, this);
}
This is the addOne:
addOne: function(xff) {
var v = new XffView({model: xff});
this.xffViews.push(v);
$("#xffs").append(v.render().el);
}
How can I add just the new entries to the list?
UPDATE
While kindasimples anwser works now, the filelist in the frontend is not sorted anymore using the comparator defined in the collection (with the timestamp). Using addAll() in the bottom of the comparator, the list is sorted.
To provide additional information, here are more parts of the overall backbone code: http://pastebin.com/rR39x3Y1
From the backbone.js docs:
collection.sort([options])
Force a collection to re-sort itself. You don't need to call this under normal circumstances, as a collection with a comparator will sort itself whenever a model is added. To disable sorting when adding a model, pass {sort: false} to add. Calling sort triggers a "sort" event on the collection.
But it does not sort itself. Also calling app.collection.sort() right after the fetch does not help.
UPDATE 2
I fixed it by sorting the array in the API before returning it. That's not how it was meant to be but at least it works now.
You have the right idea. addOne() will render individual items when you do your initial setup after a fetch to populate items. You can add a listener to the collection events to add the new items. Collection.Fetch does what you want by adding new models to the collection and leaving the old in tact (as long as you don't pass the {reset:true} flag as a parameter)
So, on your view add the listener to the initialize hook
initialize: function() {
this.listenTo(this.collection, "add", this.addOne)
}
You will probably want to define the idAttribute on your Xff Model so that backbone can identify new items properly.
I have a basic backbone collection of models.
The view I'm working within displays information about the model, allowing edits.
In the render of my view I capture the model based on a passed in 'id'.
render: function() {
this.model = myCollection.get(this.options.passedInId);
// do the render...
}
I then have a click event which updates the model and calls the render to re-render with the updates
updateModel: function() {
var me = this;
this.model.set('someFlag', true);
this.model.save(this.model.toJSON(), {
success: function(model, resp) {
me.render();
}
}
My problem is when it comes back through the render the second time the get from the collection returns a different instance of the model (I can see a different cId on it) which does not contain my changed "someFlag" property. Therefore my edits don't show up when the view is re-rendered. I know there might be a more effecient way of handling this but my question is why does this occur? Shouldn't the model fetched from the collection include the edits I made on that model?
Only other thing is the "myCollection" in this example may have been reset between the initial get and the next get after the edit, but the id is still present and it finds a model just one without any of the updates.
My issue was the collection was being reset between the render method and the updateModel method.
This causes the model to get out of sync with the collection to correct the problem all I needed to do was bind on the reset and make sure my model gets updated with the "new" version. I added this to my render.
var me = this;
this.collection.on('reset', function () {
me.model = this.get(me.model.id);
};
I'm going through the process of learning Backbone.js and I've come across a few things that look like they work... but behind the scenes, they might be causing some problems. One such issue is design patterns for swapping views.
I have a menu on the left (and corresponding view) that contains a list of users. Each user, when clicked, will display their corresponding list of movies to the right into another view. I do this by getting the model of the clicked user, building a new Movie collection based on that model, and rendering a new Movie View (to the right). If I click on another user, it does the same thing: it gets the user model, builds a new Movie collection, and renders a new Movie View to the right, replacing the DOM element. This looks fine -- but I'm worried about all of the new objects/bindings that are being created, and potential issues that could arise. So, what are my alternatives?
(1) Should I be trying to have the view redraw when the collection changes? How do I do this if I'm creating new collections?
(2) Or should I be unbinding everything when another user is clicked?
Here is my userlist view:
Window.UsersList = Backbone.View.extend({
el: $("#users"),
initialize: function(){
this.collection.bind('reset', this.render, this);
},
render: function(){
var users = [];
this.collection.each(function(user){
users.push(new UsersListItem({model: user}).render().el);
},this);
$(this.el).html(users);
return this;
}
});
In my UsersListItem view I capture the click event and call show_user_movies in my controller:
show_user_movies: function(usermodel){
// fetchMovies() lazy-loads and returns a new collections of movies
var movie_collection = usermodel.fetchMovies();
movie_list = new MovieList({collection: movie_collection, model: usermodel});
movie_list.render();
},
Thanks for any suggestions.
Just re-use the same MovieList view along with it's associated collection, using reset(models) to update the models in the collection, which should re-render the view. You can use the same pattern you have above with your MovieList view binding to the collection's reset event and re-rendering itself at that time. Note that usermodel.fetchMovies() doesn't follow the backbone asynchronous pattern of taking success/error callbacks, so I don't think the code works as is (maybe you simplified for the purpose of this question), but the point is when the new set of models arrives from the server, pass it to movie_list.collection.reset and you're good to go. This way you don't have to worry about unbinding events and creating new views.
show_user_movies: function(usermodel){
// fetchMovies() lazy-loads and returns a new collections of movies
movie_list.collection.reset(usermodel.fetchMovies().models);
},