I have many Views whos work with templates. The Rendering with the Views work perfectly, now into my Router i'm seeking to a way to trigger an Event when all Views rendered!
I used js loader like LAB.js but nothing work!
After all rendered i enter the event into the firebug console and it's work!
How and Here can i place my event so that it's trigger when all views rendered!
**My Event : **
$('div[id ^="solfg_"]').mobilyblocks();
**Router : **
(function () {
window.AppRouter = Backbone.Router.extend({
routes: {
"" : "init"
},
init: function(){
this.solfsmodel = new Solfs();
this.solfsmodel.fetch();
this.solfsView = new SolfsView({model: this.solfsmodel});
}
});
var app_router = new AppRouter;
Backbone.history.start();
}(jQuery));
Thank you
update : same problems
I found the solution just use $.when().then() from jquery, really amazing that i never saw this jquery function.
*My Solution : *
(function () {
window.AppRouter = Backbone.Router.extend({
routes: {
"" : "run"
},
initialize: function(){
this.solfsmodel = new Solfs();
this.solfsView = new SolfsView({model: this.solfsmodel});
},
run: function(){
$.when(
this.solfsmodel.fetch();
).then(function(){
*$('div[id ^="solfg_"]').mobilyblocks();*
});
}
});
var app_router = new AppRouter;
Backbone.history.start();
}(jQuery));
You can use the success callback of fetch method if you just need to wait that the collection is fetched (source: http://backbonejs.org/#Collection-fetch).
It's preferable to use Backbone.js method before use other librairies.
So your code should look like:
(function () {
window.AppRouter = Backbone.Router.extend({
routes: {
"" : "run"
},
initialize: function(){
this.solfsmodel = new Solfs();
this.solfsView = new SolfsView({model: this.solfsmodel});
},
run: function(){
this.solfsmodel.fetch({
success: function () {
$('div[id ^="solfg_"]').mobilyblocks();
}
);
}
});
var app_router = new AppRouter;
Backbone.history.start();
}(jQuery));
Related
I have two backbone views defined in two separate files namely:
LandingView.js
define([
'jquery',
'underscore',
'backbone',
'marionette',
'text!templates/landing/landingTemplate.html',
'text!templates/invitations/invitationsTemplate.html',
'views/invitations/InvitationsView',
], function ($, _, Backbone, Marionette, landingTemplate, invitationsTemplate, InvitationsView) {
var LandingView = Backbone.View.extend({
el : $("#landing"),
id : 'landing',
transition : 'slide',
initialize : function () {
this.GetNotificationsCounts();
},
events : {
'click #invitations' : 'onInvitations',
},
render : function () {
var that = this;
$('.menu li').removeClass('active');
$('.menu li a[href="#"]').parent().addClass('active');
this.$el.html(landingTemplate);
},
cleanup: function() {
this.undelegateEvents();
$(this.el).empty();
},
onInvitations : function () {
//do something
},
GetProfile: function (userLogin) {
// do something
},
GetNotificationsCounts: function () {
// do something
},
formatAccountName: function () {
//do something
}
});
return LandingView; });
Then there is another file InvitationsView.js
define([
'jquery',
'underscore',
'backbone',
'marionette',
'views/landing/LandingView',
'text!templates/invitations/invitationsTemplate.html',
], function ($, _, Backbone, Marionette, LandingView, invitationsTemplate ) {
var InvitationsView = Backbone.View.extend({
el : $("#invitations"),
id : 'invitations',
transition : 'slide',
initialize : function () { debugger;
this.$el.attr('data-transition', this.transition);
this.currentUserLogin = currentUserLogin;
var that = this;
},
events : {
},
render : function () { debugger;
$('.menu li').removeClass('active');
$('.menu li a[href="#"]').parent().addClass('active');
this.GetUserInvitationDetails();
this.$el.html(invitationsTemplate);
},
cleanup: function() {
this.undelegateEvents();
$(this.el).empty();
},
GetUserInvitationDetails: function () {
var landingView = new LandingView();
currentUserName= landingView.formatAccountName();
curUser = currentUserName.replace("\\", "^").replace("^", "%5E");
var profilemsg = landingView.GetProfile(currentUserName);
},
});
return InvitationsView;});
Now I need to call the formatAccountName and GetProfile functions defined in the first JS to the second JS. I am unable to do that. I get errors.
When I try
var landingView = new LandingView();
currentUserName= landingView.formatAccountName();
This also fails. Can somebody help me in this regard and tell me how can I achieve this
Your current approach of calling the formatAccountName method works. The following jsfiddle shows this:
http://jsfiddle.net/v4h11qac/
The problem is likely caused by another error that has not been handled correctly, resulting in the code not being run. You should fix the existing errors and the method call should work as expected.
Orignal Answer:
You could call the method directly on the prototype object:
LandingView.prototype.formatAccountName();
If you need to pass through a new context you can use the call or apply method as below:
LandingView.prototype.formatAccountName.call(context);
A better approach might involve creating a helper module that can be shared by both views.
var viewHelpers = {
formatAccountName: function(account) {
// ...
}
};
var ViewOne = Backbone.View.extend({
render: function() {
var formattedName = viewHelpers.formatAccountName(this.model);
// ...
}
};
var ViewTwo = Backbone.View.extend({
render: function() {
var formattedName = viewHelpers.formatAccountName(this.model);
// ...
}
};
You could also use a system bus, however that may be a little too heavy for such a use case. If you want to take a look at that path, then Backbone.Radio provides a request/response pattern which could be used to fulfill this requirement.
You could use a global event dispatcher that you declare in some kind of main-js-file like this:
var vent = _.extend({}, Backbone.Events);
Then in your view, listen for an event and run function.
vent.on('your:event',this.function_to_run_from_other_view,this);
Dispatch the event like this from the other view.
vent.trigger('your:event',args)
I am using a plugin for dropdowns found here: http://patrickkunka.github.io/easydropdown/
I've got it working in Backbone but I had to activate it manually and make sure it runs after the render is complete. It works when I refresh the page but if i leave the page and then come back to it the plugin does not take effect. The render function is running when each time so i dont know why it wont work when im navigating normally.
render: function() {
setTimeout(function(){
$(function(){
var $selects = $('select');
$selects.easyDropDown({
cutOff: 5,
wrapperClass: 'dropdown',
onChange: function(selected){
// do something
}
});
});
}, 0);
console.log("Rendering");
this.$el.html(template());
return this;
}
Here is my router code:
return Backbone.Router.extend({
initialize: function() {
// Render the layout view only once and simple change the contents of #content
// as per the desired route
var $body = $('body');
var layoutView = new LayoutView({ el: $body }).render();
this.$el = $("#content", $body);
this.currentView = null;
// Init the subrouters
this.bookRouter = this.addSubRouter(BookRouter, "books");
this.quoteRouter = this.addSubRouter(QuoteRouter, "quotes");
this.employeeRouter = this.addSubRouter(EmployeeRouter, "employees");
this.saleRouter = this.addSubRouter(SaleRouter, "sales");
// When the route changes we want to update the nav
this.bind("route", _.bind(this.updateNav, this));
},
// These are the base routes
// Other routes can be attached by creating subroutes
routes: {
// viewIndex is the main site index
// All other routes are handled by sub-routers
"": "viewIndex",
"upload": "upload",
"export": "export",
"test": "test",
},
// Add a sub route at the given route and listen for events
addSubRouter: function(subRouterClass, route) {
var router = new (subRouterClass)(route, { createTrailingSlashRoutes: true });
router.on("view", _.bind(this.switchView, this));
router.on("route", _.bind(function(route, section) {
this.trigger("route", route, section);
}, this));
return router;
},
// Change from this.currentView to newView
switchView: function(newView) {
// Do we need to remove the old view?
if (this.currentView) {
this.currentView.remove();
}
this.currentView = newView;
// Add the new view
this.$el.append(newView.render().$el);
newView.addedToDOM();
},
updateNav: function(route, section) {
// Get hold of the nav element
var $nav = $("#nav");
// Clean up the route string
route = route.replace("route:", "");
// Remove the currently active item
$(".active", $nav).removeClass("active");
// Apply .active to any navigation item that has a matching data-route attribute
$("[data-route=\"" + route + "\"]", $nav).addClass("active");
},
viewIndex: function () {
var view = new IndexView();
this.switchView(view);
},
upload: function (){
var view = new UploadIndexView();
this.switchView(view);
},
export: function() {
var view = new ExportIndexView();
this.switchView(view);
},
test: function() {
var view = new TestIndexView();
this.switchView(view);
}
});
});
I want to clean up my views in Backbone, before loading new views.
For that, I have searched and added a prototype property to the Backbone object called close:
Backbone.View.prototype.close = function () {
this.$el.empty();
//this.$el.unbind();
this.undelegateEvents();
console.log('close');
};
P.S. the empty() does not seem to work, neither does unbind -- undelegateEvents works.
Now, I cannot seem to figure out hot to call the close function on my router:
define([
'jquery',
'underscore',
'backbone',
'views/userList',
'views/addUser'
], function ($, _, Backbone, UserList, AddUser) {
var Router = Backbone.Router.extend({
routes: {
'' : 'home',
'new' : 'addUser'
}
});
var initialize = function () {
var router = new Router();
router.on('route:home', function () {
userList = new UserList();
userList.render();
});
router.on('route:addUser', function () {
addUser = new AddUser();
addUser.render();
});
Backbone.history.start();
}
return {
initialize : initialize
}
});
Any ideas?
First of all, don't use close, just override the standard remove method:
var SomeBaseView = Backbone.View.extend({
remove: function () {
this.$el.empty(); // Instead of this.$el.remove();
this.stopListening();
this.undelegateEvents();
return this;
}
});
and then derive all your views from SomeBaseView. Messing around with Backbone.View.prototype should be avoided.
Then, stop using global variables like this:
userList = new UserList();
in your route handlers.
Then, start keeping track of which view is currently open so that you can call remove on it before throwing up the next view. A router more like this perhaps:
var Router = Backbone.Router.extend({
routes: {
'' : 'home',
'new' : 'addUser'
},
home: function() {
if(this.currentView)
this.currentView.remove();
this.currentView = UserList();
this.currentView.render();
},
addUser: function() {
if(this.currentView)
this.currentView.remove();
this.currentView = AddUser();
this.currentView.render();
}
});
You'd drop the router.on calls since you don't need them with this approach.
So for some reason navigate won't work in one of my views. I'm doing everything in one file for now, so that may be the problem. Also I know the code is horrible, I'm just messing around with backbone right now.
EDIT: I put a console.log() in MarketingPage's function route and it never gets called, so there must be something wrong with the view.
Also, this is the error I'm getting from chrome dev tools:
Error in event handler for 'undefined': IndexSizeError: DOM Exception 1 Error: Index or size was negative, or greater than the allowed value.
at P (chrome-extension://mgijmajocgfcbeboacabfgobmjgjcoja/content_js_min.js:16:142)
at null.<anonymous> (chrome-extension://mgijmajocgfcbeboacabfgobmjgjcoja/content_js_min.js:18:417)
at chrome-extension://mgijmajocgfcbeboacabfgobmjgjcoja/content_js_min.js:1:182
at miscellaneous_bindings:288:9
at chrome.Event.dispatchToListener (event_bindings:390:21)
at chrome.Event.dispatch_ (event_bindings:376:27)
at chrome.Event.dispatch (event_bindings:396:17)
at Object.chromeHidden.Port.dispatchOnMessage (miscellaneous_bindings:254:22)
Here's my code:
/*global public, $*/
window.public = {
Models: {},
Collections: {},
Views: {},
Routers: {
},
init: function () {
console.log('Hello from Backbone!');
}
};
var App = Backbone.Router.extend({
routes: {
'': 'index',
'register': 'route_register',
},
index: function(){
var marketing_page = new MarketingPage();
},
route_register: function(){
var register_view = new RegisterView();
}
});
window.app = new App();
var User = Backbone.Model.extend({
url: '/user',
defaults: {
email: '',
password: ''
}
});
var MarketingPage = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
var template = _.template($("#marketing-page").html());
$('.search-box').after(template);
},
events: {
'dblclick': 'route'
},
route: function(e){
e.preventDefault();
console.log("In route");
window.app.navigate('register', {trigger: true});
this.remove();
}
});
var RegisterView = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function(){
var template = _.template($("#register-template").html());
$('.search-box').after(template);
}
});
$(document).ready(function () {
Backbone.history.start();
});
When I type host/#register into the browser directly, the register view gets rendered, but no matter what I do the click event won't seem to work...
Since the handler function route isn't being called, it's likely that the event delegation isn't working.
One thing to note is that the event handling that is set up in a Backbone View is scoped to only that view's el. I don't see where yours is set up explicitly, so it might be creating an empty div, then handling events inside that empty div (which you don't want).
One trick I use for quick prototypes is to set the view's el with a jQuery selector pointing to something that exists on the page already, then in the render, show it with a .show().
Since you're not really doing that, here's one thing you could try. What we're doing is setting the $el content and then calling delegateEvents to make sure that the events and handlers are being bound.
var MarketingPage = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
this.$el.html(_.template($("#marketing-page").html()));
$('.search-box').after(this.$el);
this.delegateEvents();
},
events: {
'dblclick': 'route'
},
route: function(e){
e.preventDefault();
console.log("In route");
window.app.navigate('register', {trigger: true});
this.remove();
}
});
Backbone.js views delegateEvents do not get bound (sometimes)
http://backbonejs.org/#View-delegateEvents
I am probably missing something easy or doing something wrong, but I am trying this and can't get it to fire the function...
var Home = Backbone.View.extend({
indexAction: function() {
console.log('index');
},
render: function() {
console.log('render');
}
});
Home.indexAction();
All I get is this error:
Uncaught TypeError: Object function (){return i.apply(this,arguments)}
has no method 'indexAction'
You created the view type but did not create an instance.
You need to instantiate a view of type Home now:
var h = new Home();
h.indexAction();
Also, it might be better to rename Home as HomeView, so you know it's a view which can be instantiated.
var HomeView = Backbone.View.extend({
indexAction: function() {
console.log('index');
},
render: function() {
console.log('render');
}
});
var home = new HomeView();
example on backbone docs