I'm starting out with backbone and I'm trying to create a simple view that alerts whenever my model changes. Right now the initialize function in the view is being called, but the render function is not being called when my model changes (my model is being changed).
I've attempted two ways of binding to the change event (in the initialize function and the events property). I feel like I'm missing something obvious.
The #jsonPreview id exists in the html.
// Create the view
var JSONView = Backbone.View.extend({
initialize: function(){
this.bind("change", this.render);
},
render: function() {
alert("change");
},
events:
{
"change":"render"
}
});
// Create the view, and attach it to the model:
var json_view = new JSONView({ el: $("#jsonPreview"), model: documentModel });
Thanks in advance.
It looks like you are binding to the change event on the view rather than the view's model. think you need to bind to the model event something like this:
initialize: function(){
this.model.bind("change", this.render);
},
Related
I've been diving into the scary stuff recently.. :) scary stuff being the source of popular js frameworks like backbone.js, angular.js, vue.js and so on.
I'll take Backbone as an example here. I am trying to figure out how is a model attached to the view?
Here is the code and if someone could just point out the part where this is happening, would be awesome!
https://github.com/jashkenas/backbone/blob/master/backbone.js
Actually, the part I don't understand is that there is not innerHTML called anywhere, so how is the element being populated with the data?
Backbone is not Angular, it doesn't bind model to html for you, also it does not actually renders views for you, you have to implement render methods in your views and call them when you find appropriate. In fact, I think it might be confusing to developer coming from 2-way binding frameworks. Backbone is all about giving all the control to the developer, but you have to do all the work yourself.
Minimal model => view flow example would be something like
var MyModel = Backbone.Model.extend({
defaults: {
test: 'test',
},
initialize: function (options) {
console.log(this.get('test'));
},
});
var MyView = Backbone.View.extend({
el: '#your-el',
initialize: function (options) {
this.template = _.template('<div><%= test %></div>');
this.listenTo(this.model, 'change', this.render);
},
render: function () {
// rendering happens here, innerHTML, $.html(), whichever you prefer
// and you can pass model data as this.model.toJSON()
// or you can pass an object with data
// also, you probably will need a templating library or use
// bundled underscore _.template(html, data) method to render blocks of html
// for example, with underscore
this.$el.html(this.template(this.model.toJSON()));
return this; // for chaining
},
});
var myModel = new MyModel();
var myView = new MyView({
model: myModel,
});
myModel.set('test', 'newValue'); // view should render after this call
Check the list of built-in events at backbonejs.org.
I am using backbone.js to create a page. My code contains many models and views. I wonder if it is possible to destroy a view and then redraw it without refreshing the page, and if so, what is the best way to do it.
$(document).ready(function() {
var myHomeCollectionView = new MyHomeCollectionView({});
});
var MyHomeCollection = Backbone.Collection.extend({
model: MyHome
});
var MyHomeCollectionView = Backbone.View.extend({
el: "#home",
initialize: function(options){
_.bindAll(this, 'render');
this.collection = new MyHomeCollection();
/-- Rest initialize the code --/
},
render: function(){
/-- Render code --/
}
})
this is a sample code of my view..
Yes. It is certainly possible. The main benefit of a JS framework is being able to change the content of the page without refreshing it.
I am not sure why you want to destroy the view, that is usually not necessary.
If you simply want to re-render the same view, you usually just listen for an event then call render. Take a look at the example below of re-rendering your view based on when the collection reloaded.
var MyHomeCollectionView = Backbone.View.extend({
el: "#home",
initialize: function(options){
_.bindAll(this, 'render');
this.collection = new MyHomeCollection();
// re-render view when collection is reloaded
this.listenTo(this.collection, 'reset sync', this.render);
/-- Rest initialize the code --/
},
render: function(){
/-- Render code --/
}
})
Or you can replace a view with another view. You can do this by simply rendering another view into the same element. Check out this jsfiddle for a very simple example of this: http://jsfiddle.net/1g1j7afa/2/.
If you want to get more advanced, you can check out Marionette LayoutView. It is a nice way to handle the adding/replacing of sub views.
The goal
Call UserModel within AuthenticationView with Backbone + Sprockets.
The problem
I just don't know a good way to do that.
The scenario
This is my view (assets/js/views/AuthenticationView.js):
var AuthenticationView = Backbone.View.extend({
el: $('.authentication-form'),
events: {
'keyup input[name=email]' : 'validationScope',
'keyup input[name=password]' : 'validationScope',
'submit form[data-remote=true]' : 'authenticate'
},
render: function() {
},
authenticate: function() {
// Here I'm going interact with the model.
}
});
And that's my model (assets/js/models/UserModel.js):
var UserModel = Backbone.Model.extend({
url: '/sessions'
});
The question
How can I make the interaction between the view and the model?
Remember: they're in separated files.
Getting the constructors together would be step 1 -- separate files don't matter, you can use Browserify/requirejs or just throw these things in global scope. From there, since passing an object into a view constructor with the property name 'model', automatically assigns the value to the view's this.model. So if we have an initialize method in our view, we can see:
initialize: function (options) {
console.log(this.model); // User instance
this.model.on('update', function () {});
}
And so we can pass in an instantiated model into the view via an object's model property:
var model = new UserModel();
var view = new AuthenticationView({ model: model });
http://www.joezimjs.com/javascript/lazy-loading-javascript-with-requirejs/ here is great article about js modules lazy loading with examples used backbonejs and requirejs
I am learning JavaScript MVC application development using Backbone.js, and having issues rendering model collection in the view. Here's what I want to do:
After the page finishes loading, retrieves data from the server as model collection
Render them in the view
That's all I want to do and here is what I have so far:
$(function(){
"use strict";
var PostModel = Backbone.Model.extend({});
var PostCollection = Backbone.Collection.extend({
model: PostModel,
url: 'post_action.php'
});
var PostView = Backbone.View.extend({
el: "#posts-editor",
initialize: function(){
this.template = _.template($("#ptpl").html());
this.collection.fetch({data:{fetch:true, type:"post", page:1}});
this.collection.bind('reset', this.render, this);
},
render: function(){
var renderedContent = this.collection.toJSON();
console.log(renderedContent);
$(this.el).html(renderedContent);
return this;
}
});
var postList = new PostCollection();
postList.reset();
var postView = new PostView({
collection: postList
});
});
Problem
As far as I know, Chrome is logging the response from the server and it's in JSON format like I want it. But it does not render in my view. There are no apparent errors in the console.
The server has a handler that accepts GET parameters and echos some JSON:
http://localhost/blog/post_action.php?fetch=true&type=post&page=1
[
{
"username":"admin",
"id":"2",
"title":"Second",
"commentable":"0",
"body":"This is the second post."
},
{
"username":"admin",
"id":"1",
"title":"Welcome!",
"commentable":"1",
"body":"Hello there! welcome to my blog."
}
]
There are 2 potential problems with your code.
The event listener callback should be registered before calling the collection.fetch(). Otherwise, you might miss the first reset event as it might be triggered before the listener is registered.
The reset event is not enough to ensure that the view will re-render every time the collection gets updated.
Also, note that it is a good practice to use the object.listenTo() form to bind events as it will ensure proper unregistration when the view is closed. Otherwise, you may end up with what is known as Backbone zombies. Here is a solution.
this.listenTo( this.collection, 'reset add change remove', this.render, this );
this.collection.fetch({ data: { fetch:true, type:"post", page:1 } });
Note how you can register multiple events from the same object by separating them with whitespace.
change
this.collection.bind('reset', this.render, this);
to
this.collection.bind('sync', this.render, this);
The problem is you perform reset only once, in the beginning. And at that time you don't have anything to render. The next time, when you fetch your collection, reset event doesn't fire, because you fetch collection without option {reset: true}.
Change this line
this.collection.bind('reset', this.render, this);
to
this.listenTo(this.collection, 'reset', this.render);
When fetching your collection, the reset event is not fired by default anymore. (I believe since version 1.0)
In order to have Backbone fire the reset event when the collection has been fetched, you now have to call the fetch method like so:
this.collection.fetch({reset: true});
How can I know which attribute of the view model is changed in the render function? (In the render function, "e" is the model, but I need only the attribute which is changed.) I need to know this to know which template to use. Or is there another method to do this?
window.Person = Backbone.Model.extend({});
window.Njerzit = Backbone.Collection.extend({
model: Person,
url: '/Home/Njerzit'
});
window.PersonView = Backbone.View.extend({
tagName: 'span',
initialize: function () {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
},
render: function (e) {
//if model name is changed, I need to render another template
this.template = _.template($('#PersonTemplate').html());
var renderContent = this.template(this.model.toJSON());
$(this.el).html(renderContent);
return this;
}
});
I believe the changedAttributes function is what you're looking for
changedAttributesmodel.changedAttributes([attributes])
Retrieve a hash of only the model's attributes that have changed. Optionally,
an external attributes hash can be passed in, returning the attributes
in that hash which differ from the model. This can be used to figure
out which portions of a view should be updated, or what calls need to
be made to sync the changes to the server.
or to check if a specific attribute has changed use the hasChanged function
hasChangedmodel.hasChanged([attribute])
Has the model changed since the last "change" event? If an attribute is passed, returns true
if that specific attribute has changed.
var nameChanged = this.model.hasChanged("name");
From Backbone Docs
You can bind to change:name if you only want to notify if the name has changed: http://documentcloud.github.com/backbone/#Model-set