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.
Related
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()
I am having a trouble with my router and controller. On my app's before:start, I have a handler that fetches collections of Leads and Vehicles.
I have a single region, with my layout view as:
var App = new Marionette.Application({});
var AppLayoutView = Marionette.LayoutView.extend({
el: 'body',
regions: {
main: '#app-container'
}
});
My controller is:
var Controller = Marionette.Object.extend({
leads: function() {
App.regions.main.show(new App.leadsTableView({collection: App.leads}));
},
vehicles: function() {
App.regions.main.show(new App.vehiclesTableView({collection: App.vehicles}));
}
});
In my start handler:
App.on('start', function() {
App.regions = new AppLayoutView();
App.router = new Marionette.AppRouter({
controller: new Controller(),
appRoutes: {
'leads': 'leads',
'vehicles': 'vehicles'
}
});
Backbone.history.start({pushState: true});
});
App.start();
How can I start with a specific route? And, when a user goes to #vehicles, how can I make the region load the new view? I'm missing something about routers.
EDIT: When I go to, #leads in my URL, my vehicles view comes up. When I click on links that go to #leads and #vehicles, they don't work.
Default route
You can define a default by adding a "splat" route (one that starts with *) to the end of your routes. I like to use *default to make the intent obvious:
appRoutes: {
'leads': 'leads',
'vehicles': 'vehicles',
'*default': 'leads'
}
Broken links
Because you are using pushstate routing, the view URL is /vehicles rather than the hash fragment #vehicles. You should no longer use hash fragment urls.
Here's a simple approach to trigger pushState routes with link clicks:
$('a[href]').on('click', function(e) {
e.preventDefault();
var href = e.target.getAttribute('href');
App.router.navigate(href, { trigger: true })
});
You may find this post about moving from hash fragment to pushState routing useful.
You'll also need to configure your server to pass requests that match your route to the main app page - for example, it needs to understand that http://localhost/app/vehicle should be handled by http://localhost/app.
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...