How to get current route on (first) page load?
myRouter = new app.Router();
Backbone.history.start();
myRouter.on("route", function(route, params) {
console.log(route);
});
This works on every route but not the first one (or page refresh).
The reason it doesn't work is that you are binding an event after the event was triggered.
This would solve the problem:
myRouter = new app.Router();
myRouter.once("route", function(route, params) {
console.log(route);
});
Backbone.history.start();
But a better way would be that in your router, you use listenToOnce.
app.Router = Backbone.Router.extend({
routes: { /* ... */ },
initialize: function() {
this.listenToOnce(this, 'route', this.onFirstRoute);
},
onFirstRoute: function(route, params) {
console.log(route);
}
});
It will trigger once, then it'll unbind itself.
Or, if you only want to know which route:
myRouter = new app.Router();
Backbone.history.start();
console.log(Backbone.history.getFragment()); // or .getPath()
Related
I am trying to make simple app in backbone .Actually I want to call methods when url change .I want to learn how backbone routing works .I studied routing but my alert is not display when I change the url .
When I append the url with this “#contacts” but it is not calling this method
listContacts: function () {
alert("route")
console.log("route to list contacts was triggered");
ContactsApp.List.Controller.listContacts();
}
Can we move to next page or view while click to any item of list .Actually I want to move one page to another after click of row using backbone routing ?
here is my code
http://plnkr.co/edit/yN16uPgk0dcJAcrApewH?p=preview
app.module("ContactsApp", function (ContactsApp, app, Backbone, Marionette, $, _) {
// console.log(app)
ContactsApp.Router = Marionette.AppRouter.extend({
appRoutes: {
"contacts": "listContacts"
}
});
var API = {
listContacts: function () {
alert("route")
console.log("route to list contacts was triggered");
ContactsApp.List.Controller.listContacts();
}
};
ContactsApp.on("start", function () {
new ContactsApp.Router({
controller: API
});
});
});
any update using marionette?
You need to use Backbone Router.
var app = new Marionette.Application();
app.addRegions({
'main': '#main1'
});
var Router = Backbone.Router.extend({
routes: {
'item(/*id)': 'item',
'*params': 'home'
}
});
app.router = new Router();
// Home route
app.router.on('route:home', function() {
var page = new HomeView({...})
app.main.show(page);
});
// Item route
app.router.on('route:item', function(id) {
var page = new ItemView({id:id, ...})
app.main.show(page);
});
...
app.start();
Backbone.history.start({pushState: true});
Here is your modified code.
I'm going through examples with routing from David Sulc's book Backbone.Marionette.js: A Gentle Introduction
https://leanpub.com/marionette-gentle-introduction
ContactManager.navigate = function (route, options) {
options || (options = {});
Backbone.history.navigate(route, options);
};
ContactManager.getCurrentRoute = function () {
return Backbone.history.fragment;
};
ContactManager.on("initialize:after", function () {
if (Backbone.history) {
Backbone.history.start();
if (this.getCurrentRoute() === "") {
ContactManager.trigger("contacts:list");
}
}
As you can see if the history fragment is empty, it will trigger the contacts:list event which will render the list of contacts. However, it doesn't redirect at all, and I've found out that fragment is preset to "contacts" somehow, so the event doesn't get fired at all. It also happened to me once that initially the fragment was empty and got everything rendered, and url changed properly, but upon refresh fragment was still "contacts" and again nothing was rendered.
ContactsApp.Router = Marionette.AppRouter.extend({
AppRoutes: {
"contacts": "listContacts"
}
});
ContactManager.on("contacts:list", function () {
ContactManager.navigate("contacts");
API.listContacts();
});
This is the code that handles the event. What seems to be the problem? Thanks.
I think there is some missing code. I would expect to find something like this in the router:
var myController = {
listContacts: function () {
ContactManager.trigger("contacts:list");
}
};
ContactsApp.Router = Marionette.AppRouter.extend({
controller: myController,
appRoutes: {
"contacts": "listContacts"
}
});
Note that appRoutes starts with a lowercase a.
Now the route contacts will call the controller's listContacts method and trigger the ContactManager.on("contacts:list"... callback, running the appropriate API method.
I've implemented "change page" in my one page application with Backbone.js. However, I'm not sure if my Router should contain so much business logic. Should I consider go with Marionette.js to implement such functionality and make my Router thin? Should I worry about destroying Backbone models and views attached to "previous" active page/view when I change it (in order to avoid memory leaks) or it's enough to empty html attached to those models/views.
Here is my Router:
App.Router = Backbone.Router.extend({
routes: {
'users(/:user_id)' : 'users',
'dashboard' : 'dashboard'
},
dashboard: function() {
App.ActiveView.destroy_view();
App.ActiveViewModel.destroy();
App.ActiveViewModel = new App.Models.Dashboard;
App.ActiveViewModel.fetch().then(function(){
App.ActiveView = new App.Views.Dash({model: App.ActiveViewModel });
App.ActiveView.render();
});
},
users: function(user_id) {
App.ActiveView.destroy_view();
App.ActiveViewModel.destroy();
App.ActiveViewModel = new App.Models.User;
App.ActiveViewModel.fetch().then(function() {
App.ActiveView = new App.Views.UsersView({model: App.ActiveViewModel});
App.ActiveView.render();
});
}
});
Another approach:
Create an AbstractView
Having an AbstractView declared and then extending your other application specific View's from AbstractView has many advantages. You always have a View where you can put all the common functionalities.
App.AbstractView = Backbone.View.extend({
render : function() {
App.ActiveView && App.ActiveView.destroy_view();
// Instead of destroying model this way you can destroy
// it in the way mentioned in below destroy_view method.
// Set current view as ActiveView
App.ActiveView = this;
this.renderView && this.renderView.apply(this, arguments);
},
// You can declare destroy_view() here
// so that you don't have to add it in every view.
destroy_view : function() {
// do destroying stuff here
this.model.destroy();
}
});
Your App.Views.UsersView should extend from AbstractView and have renderView in place of render because AbstractView's render will make a call to renderView. From the Router you can call render the same way App.ActiveView.render();
App.Views.UsersView = AbstractView.extend({
renderView : function() {
}
// rest of the view stuff
});
App.Views.Dash = AbstractView.extend({
renderView : function() {
}
// rest of the view stuff
});
Router code would then change to :
dashboard: function() {
App.ActiveViewModel = new App.Models.Dashboard;
App.ActiveViewModel.fetch().then(function(){
new App.Views.Dash({model: App.ActiveViewModel }).render();
});
}
Can't figure out what's going wrong with my Backbone router. Can anyone spot a mistake in the following block of code? The index route is working fine but the classes route isn't ever triggering (e.g. when I navigate to a URL like localhost/classes/test)
var app = app || {};
$(function() {
app.Router = Backbone.Router.extend({
routes: {
'' : 'index',
'classes/:id' : 'classes'
},
initialize: function() {
this.classList = new app.ClassCollection();
},
index: function() {
this.menuView = new app.ClassCollectionView({collection: this.classList});
},
classes: function(id) {
console.log("hello")
var _class = new app.ClassModel({id: id});
this.classView = new app.ClassPageView({model: _class});
}
});
router = new app.Router();
Backbone.history.start({pushState: true});
})
If everything looks in order, there's probably a bug somewhere else in my code.
Backbone.router is extending hashbang navigation.
so
localhost/#classes/test
should lead to your method. ALSO! pay attention that emty route should be at the end of the routes list.
It's like else if construction, if route matches "" (default # ?!) it will never match other routes
by default the route will work with the hash try localhost/#classes/test
I have the following situation:
app.js: Singleton Marionette.Application() where I define a nav, a footer, and a main region. In the initializer I construct Marionette.Contoller's and attach them to the app's this.controller object for later control. I might not construct all the Controller's here, just the ones I want to Eagerly Load. Some are Lazy Loaded later. I also instantiate a Backbone.Router here, and pass in a reference to my app object:
var theApp = new TP.Application();
theApp.addRegions(
{
navRegion: "#navigation",
mainRegion: "#main",
footerRegoin: "#footer"
});
theApp.addInitializer(function()
{
// Set up controllers container and eagerly load all the required Controllers.
this.controllers = {};
this.controllers.navigationController = new NavigationController({ app: this });
this.controllers.loginController = new LoginController({ app: this });
this.controllers.calendarController = new CalendarController({ app: this });
this.router = new Router({ app: this });
});
**Controller.js: this is a general use controller that handles view & model intsantiation and eventing. Each Controller owns its own Marionette.Layout, to be filled into the App.mainRegion. Each Controller binds to the layout's "show" event to fill in the layout's regions with custom views. Each Controller offers a getLayout() interface that returns the controller's associated layout.
Marionette.Controller.extend(
{
getLayout: function() { return this.layout; },
initialize: function()
{
this.views.myView = new MyView();
...
this.layout.on("show", this.show, this);
...
},
show: function()
{
this.layout.myViewRegion.show(myView);
}
});
router.js: the router uses the app singleton to load a Controller's layout into the App's main region:
...
routes:
{
"home": "home",
"login": "login",
"calendar": "calendar",
"": "calendar"
},
home: function ()
{
var lazyloadedController = new LazyLoadController();
this.theApp.mainRegion.show(lazyLoadController.getLayout());
},
login: function (origin)
{
this.theApp.mainRegion.show(this.theApp.controllers.loginController.layout);
}
As it is, everything works fine except for reloading the same layout / controller twice. What happens is that the DOM events defined in the LoginView do not re-bind on second show. Which is easily solved by moving the LoginView initialization code into the "show" event handler for that Controller:
LoginController = Marionette.Controller.extend(
{
...
show: function()
{
if (this.views.loginView)
delete this.views.loginView.close();
this.views.loginView = new LoginView({ model: this.theApp.session });
this.views.loginView.on("login:success", function()
{
});
this.layout.mainRegion.show(this.views.loginView);
}
Now everything works fine, but it undoes part of the reason I created Controller's to begin with: I want them to own a View and its Models, create them once, and not have to destroy & recreate them every time I switch layouts.
Am I missing something? Is this not how I should be using Layouts? Isn't the whole point of Layouts and Regions that I can switch in & out Views at will?
Obviously I wouldn't jump back to LoginController/Layout often, but what about between a HomeController/Layout, CalendarController/Layout, SummaryController/Layout, etc... in a single page application I might switch between those 'top-level' layouts rather often and I would want the view to stay cached in the background.
I think your problem is that you don't maintain a single instance of the controller. The recommended way to handle routing/controllers (based on Brian Mann's videos) is like this
App.module('Routes', function (Routes, App, Backbone, Marionette, $, _) {
// straight out of the book...
var Router = Marionette.AppRouter.extend({
appRoutes: {
"home": "home",
"login": "login",
"calendar": "calendar"
}
});
var API = {
home: function () {
App.Controller.go_home();
},
login: function () {
App.Controller.go_login();
},
calendar: function () {
App.Controller.go_calendar();
}
};
App.addInitializer(function (options) {
var router = new Router({controller: API});
});
});
... and the controller:
App.module("Controller", function (Controller, App, Backbone, Marionette, $, _) {
App.Controller = {
go_home: function () {
var layout = new App.Views.Main();
layout.on('show', function () {
// attach views to subregions here...
var news = new App.Views.News();
layout.newsRegion.show(news);
});
App.mainRegion.show(layout);
},
go_login: function () {
....
},
go_calendar: function () {
....
}
};
});
I suspect your problem is the lazy-loaded controller...