I am still kind of new to backbone and I have a question. I am currently working on a previously created backbone view. This view is responsible for all the saving within the application. The class is becoming exceedingly large and I wanted to split out some of the events/methods/function to their own "sub class", if that is possible. For example, I have a group of events and functions that are responsible for a specific task. I would like to move those event and functions to their own js page, in a sub folder. The problem is that the events are being called. Here is a general overview of how the class is setup:
var myClass= myClassBaseClass.extend({
events: {
.... all my events here,
'click .eventOneButton': 'eventOne',
},
initialize: function (options) {
//initialize stuff here
},
postRender: function (renderOptions) {
//post render here
},
preRender: function (renderOptions, html) {
return html;
}, template: function (renderOptions) { //template stuff },
//...etc.
//my events
//eventOne was here, now its moved to its own file
});
My new file is in a subfolder, and looks like this:
var myClassSubClass= myClass.extend({
eventOne: function(e){
//event stuff here
}
});
So, is what am I doing wrong? Why wont the event get caught in the second file?
thanks
jason
Related
I am trying to implement endless scrolling with Backbonejs. My view initializes a collection and calls fetch fetch function.
My view
var app = app || {};
app.PostListView = Backbone.View.extend({
el: '#posts',
initialize: function( ) {
this.collection = new app.PostList();
this.collection.on("sync", this.render, this);
this.collection.fetch();
this.render();
},
render: function() {
/*render posts*/
}
});
In my page I added the following code. It checks if the the user at the bottom of the page. If yes then it checks if the view is initialized. If yes then call that view fetch function of the view's collection object.
var app = app || {};
$(function() {
var post_view;
$(window).scroll(function() {
if(($(window).scrollTop() + $(window).height() == getDocHeight()) && busy==0) {
if(!post_view){
post_view = new app.PostListView();
} else {
post_view.collection.fetch();
}
}
});
});
So far this code is working. I am not sure if this is the right approach or not?
It's not a bad option; it works, and Backbone is making that collection available for you. But there's a couple of other options to consider:
Move that collection.fetch into a method getMoreItems() inside your PostListView, and call it within your else block. That way you're encapsulating your logic inside the view. Your app is more modular that way, and you can make your scrolling smarter without updating the rest of your app.
Move the scroll listener inside your PostListView. I'd probably put this within your PostListView's initialize function. Again, this reduces dependencies between the various parts of your app - you don't have to remember "Whenever I create a PostListView, I must remember to update it on scroll." By setting that event listener within the PostListView itself, all you have to do is create it. Backbone's general philosophy is to have small, independent components that manage their own state; moving the event listener inside would fit with that.
I'm building a little quiz game using backbone.js
Once you get to the end of the quiz you have the option to start again.
How can I achieve this? I've tried recalling the initialize function, as well as the first function that gets fired when you start the game. This is just returning errors. The two calls are success but a subsequent function for rending each question is failing.
I think I need to empty my model/collection. This is my first outing with Backbone, I'm still trying to get my head around how every works.
Any help, greatly appreciated.
You could use the backbone router (don't forget to start it):
So in your game view, you'd have an event which triggered when the start again button was clicked. This event would trigger a function which would redirect the user to a newgame route. You could also setup a function in the router to close your old view. This is important to avoid zombie views
e.g. view
//initialize code
//...
events: {
'click .startAgainButton':'restart'
}
restart: function(e) {
e.preventDefault();
window.location.href = '#/new_game';
}
//rest of view code...
e.g. router
//router code
routes: {
"new_game":"reset"
},
trackView: function (next) {
if (this.current) this.current.close();
this.current = next;
},
//kill zombie views
close: function () {
this.undelegateEvents();
this.$el.off();
this.$el.children().remove();
},
reset: function() {
this.trackView(new View());
}
I had a view where I had a very basic list of registered users on the site, and some option like create, edit and delete all together on the same Backbone view and was working.
Since this is a learning exercise for me, now I wanted to decouple the main view form the details view or create view, so first thing I did was creating a new view for creating a user, which is a very basic form with 4 fields on it.
This is the code on the main view that calls the create user view
userCreate: function () {
event.preventDefault();
var createView = new viewAdminCV();
createView.render();
}
and this is the render function on the viewAdminCV() view
el: $('#frameDetails'),
render: function() {
this.$el.html(templateUserCreate);
}
There is no error if I execute this code, however nothing get rendered. If I replace the line
this.$el.html(templateUserCreate);
with
$('#frameDetails').html(templateUserCreate);
it works perfectly, but I want to understand why the $el is not working in this case, because I have it working in other views.
Appreciate you help.
In case is needed, the complete code is in this link
https://github.com/GabrielBarcia/UsersMod/blob/iss3/public/js/views/admin_CV.js
Basically, a you're finding the #frameDetails element "too early". Its easy to forget that when you use what they call a "jQuery Element" instance, you're really running a function. That means in your code, it's trying to find #frameDetails at the moment that you run "extend" on that Backbone.View. What you want is to find the element at new viewAdminCV() (I apologize if i sound confusing...).
BUT to fix this, there are three ways. One is traditionally, you just need to play in the literal string without the jQuery wrapper, then Backbone will find it:
el: '#frameDetails',
render: function() {
this.$el.html(templateUserCreate);
}
OR
you can wrap that with a handler
el: function(){
return $('#frameDetails')
},
render: function() {
this.$el.html(templateUserCreate);
}
OR an even slicker move, is you inject it from your "admin.js" file. Then you don't declare the "el" property in your viewAdminCV class:
userCreate: function () {
event.preventDefault();
$('#btnUserCreate').prop( 'disabled', true );
var createView = new viewAdminCV({ el: $('#frameDetails') });
createView.render();
}
You already have el as an object.
el: $('#frameDetails')
So you don't need to use $el while rendering.
Use:
el: $('#frameDetails'),
render: function() {
this.el.html(templateUserCreate);
}
I am working on a relatively big application which is like some sort of app collection.
All of my apps got a bootstrapping view which loads the base layout and the nested views.
I now started to implement a singleton pattern to the views:
var SomeView = Backbone.View.extend({});
if (SomeView._instance) return SomeView._instance;
SomeView._instance = new SomeView();
return SomeView._instance;
Now I mentioned that when I switch between different apps (views) the event system is broken. This is actually quite logic finally we remove the view out of the document. However I have some sort of resistance against always building up the views new. This is quite ineffective: Everything has to get reloaded (data), and rebuilt.
So is there a way to rebind events to a cached views or is this whole idea bad and I should accept that views have to get rebuilt?
Update:
define(['jquery', 'underscore', 'backbone', 'views/settings/profile'], function($, _, Backbone, ProfileSettingsView) {
var ContentView = Backbone.View.extend({
tagName: "div",
className: "content well",
initialize: function() {
this.on('change:section', this.change, this);
this.views = {};
this.views.profile = new ProfileSettingsView();
},
render: function() {
this.$el.empty();
return this;
},
events: {
"click": "click"
},
// the router triggers this one here
change: function(query) {
console.log(query);
// if I uncomment this then nothing is rendered at all
//this.$el.detach();
var el;
if (query === 'profile') {
el = this.views.profile.render().el;
} else {
this.$el.empty();
}
if (el) {
this.$el.empty().append(el);
}
},
click: function(e) {
console.log('clicked, content should disapear');
}
});
if (ContentView._instance) return ContentView._instance;
ContentView._instance = new ContentView();
return ContentView._instance;
});
I am a bit confused about how I can use jQuery's detach().
I looked at the demo in the official docs and found out that it is not enough to call .detach() on a jQuery object. .detach returns a new object which looks like a jQuery one and contains the events bound. The hard thing about this is that I have to save this returnment of detach() somewhere and I have to now from who it's coming. And now I don't have any look through. I will now search for some Backbone.View example using detach() but I think it is to specific....
Update2:
Yes! I found a workaround: Instead of saving the events and then reinserting it in to the DOM. We can just call this.delegateEvents() to rebind all events. This truly is just a workaround and I would be happy if somebody could provide me an example :)
Personally, I prefer to rebuild my views.
However, I know a lot of people that prefer to re-use them. In that case, follow the instructions in this blog post from Tim Branyen: http://tbranyen.com/post/missing-jquery-events-while-rendering
I'd would like to do something like backbonejs events, define an events object within the controller, something like this:
var UserController = function() {
this.events = {
'click .display': 'displayUsers',
'click .delete': 'deleteUsers'
}
this.displayUsers = function() {
//display users
}
this.deleteUsers = function() {
//remove users
}
}
After that, do something with requirejs and load the controller on demand and append the events, the code is almost ready but the events is something i can't resolve. Someone can help with any idea to do this?.
My idea is take the events object after controller class is loaded and send it to an event manager or something to create the events on the fly.