I am doing my first Backbone project. I am using Backbone 1.3.3 and Underscore 1.8.3. I am having a problem with my router. When I invoke the router, it places the hashtag at the very end of the URL:
http://server:9999/backbone_demo/addMember#
instead of:
http://server:9999/backbone_demo/#addMember
which is what I want.
var router = null;
var context = "backbone_demo";
DemoRouter = Backbone.Router.extend({
initialize: function () {
initializeEventsForNavigation();
},
routes: {"" : "start", "/bands": "bands", "/addMember": "addMember", "*defaultRoute" : "defaultRoute"},
defaultRoute: function() {
this.start();
},
start: function () {
this.bands();
},
bands: function () {
BackboneDemo.View.unrenderAllViews();
BackboneDemo.View.renderBandList();
},
addMember: function() {
BackboneDemo.View.unrenderAllViews();
BackboneDemo.View.renderAddMember();
}
});
var init = function() {
initializeEventsForBandListRender();
initializeEventsForAddMemberRender();
router = new DemoRouter();
Backbone.history.start({pushState: true, root: context});
};
var navigate = function(e) {
var route = e.detail.route;
router.navigate("/" + route, true);
};
If I pass "addMember" to my navigate function (in e.detail.route), I will be passing "/addMember" to router.navigate. The router works fine except for this detail.
I think you should set pushState to false if you want to use hash routing.
Backbone.history.start({pushState: false, root: context});
Let's assume that we have testing code like this:
var App = (function () {
var api = {
Router: null,
init: function () {
this.content = $("#content");
Backbone.history.start();
return this;
}
};
var ViewsFactory = {
view1: function () {
var model1 = new model1();
return new api.Views.View1({
model: model1
});
},
view2: function () {
var model2 = new model2();
return new api.Views.View2({
model: model2
});
},
view3: function () {
var model3 = new model3();
return new api.Views.View3({
model: model3
});
},
};
var Router = Backbone.Router.extend({
routes: {
"": "view1",
"2": "view2",
"3": "view3",
},
view1: function () {
var view1 = ViewsFactory.view1();
$(".content").html(view1.render().el);
},
view2: function () {
var view2 = ViewsFactory.view2();
$(".content").html(view2.render().el);
},
view3: function () {
var view3 = ViewsFactory.view3();
$(".content").html(view3.render().el);
},
});
api.Router = new Router();
return api;
})();
And I want to use Require.js. Please don't focus on names, but on the idea.
If I understand it correctly, I have to include in require method every view (View1, View2, View3) and every model (Model1, Model2, Model3). But what is the purpose of using Require.js in such case instead of traditional <script> tags?
Using ViewsFactory is a good practice in backbone projects?
WHy not a view factory. In your case I'm not sure it's really useful though.
requirejs will help to build reusable modules. http://requirejs.org/docs/why.html
The best option in router is using variables like this:
var $ = require('jquery'),
Backbone = require('backbone');
I'm trying to improve the navigation of my little backbone application. Right now I just have some simple navigation using html links that use to #path/to/page in the href element.
What I'm running into is when I click on one of these and then click the back button, the page doesn't refresh properly, and the HTML content doesn't change. So I'm trying to incorporate the navigate functionality into my code.
The issue I'm running into is that I can't find an example that matches the code layout I'm currently using, and I don't understand how backbone works enough to adapt the things I find into something useful.
Here's what I've got:
app.js - called from the index.html file
require.config({
baseUrl: 'js/lib',
paths: {
app: '../app',
tpl: '../tpl',
bootstrap: 'bootstrap/js/',
},
shim: {
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'underscore': {
exports: '_'
}
}
});
require([
'jquery',
'backbone',
'app/router',
], function ($, Backbone, Router) {
var router = new Router();
Backbone.history.start();
});
app/router.js - instantiated in app.js
define(function (require) {
"use strict";
var $ = require('jquery'),
Backbone = require('backbone'),
WindowView = require('app/views/Window'),
breadcrumbs = {"Home": ""},
$body = "",
$content = "",
windowView = "";
return Backbone.Router.extend({
initialize: function () {
require([], function () {
$body = $('body');
windowView = new WindowView({el: $body}).render();
$content = $("#content", windowView.el);
});
},
routes: {
'' : 'home',
'profile/login(/)' : 'candidateProfileLogin',
'profile/manage(/)' : 'candidateProfileLogin',
'profile/manage/:id(/)' : 'candidateProfileHome',
'profile/manage/:id/questionnaire/:page(/)' : 'candidateProfileQuestionnaire',
'profile/manage/:id/:section(/)' : 'candidateProfileSection',
},
home: function (){
},
candidateProfileLogin: function () {
require(['app/views/CandidateLogin'], function (CandidateLoginView) {
console.log(Backbone.history.fragment);
var view = new CandidateLoginView({el: $content});
view.render();
});
},
candidateProfileHome: function (id) {
require(["app/views/Candidate", "app/models/candidate"], function (CandidateView, models) {
var candidate = new models.Candidate({id: id});
candidate.fetch({
success: function (data) {
var view = new CandidateView({model: data, el: $content});
view.render();
},
error: function (data) {
var view = new CandidateView({model: data, el: $content});
view.render();
}
});
});
},
candidateProfileSection: function (id, section) {
require(["app/views/Candidate", "app/models/candidate"], function (CandidateView, models) {
var candidate = new models.Candidate({id: id});
candidate.fetch({
success: function (data) {
var view = new CandidateView({model: data, el: $content});
view.render(section);
},
error: function (data) {
//Output the data to the console. Let the template take care of the error pages
console.log(data);
var view = new CandidateView({model: data, el: $content});
view.render();
}
});
});
},
candidateProfileQuestionnaire: function (id, page) {
require(["app/views/Candidate", "app/models/candidate"], function (CandidateView, models) {
var candidate = new models.Candidate({id: id});
candidate.fetch({
success: function (data) {
var view = new CandidateView({model: data, el: $content});
view.render(page);
},
error: function (data) {
//Output the data to the console. Let the template take care of the error pages
console.log(data);
var view = new CandidateView({model: data, el: $content});
view.render();
}
});
});
},
});
});
app/views/Candidate.js - My view I'm trying to process the clicks
define(function (require) {
"use strict";
var $ = require('jquery'),
_ = require('underscore'),
Backbone = require('backbone'),
tpl = require('text!tpl/Candidate.html'),
template = _.template(tpl),
CandidateErrorView = require('app/views/CandidateError'),
errtpl = require('text!tpl/CandidateError.html'),
errTemplate = _.template(errtpl);
return Backbone.View.extend({
events: {
'submit #voters-guide-personalInfo': 'savePersonalInfo',
'submit #voters-guide-essay' : 'saveEssay',
'submit #voters-guide-survey' : 'saveSurvey',
'submit #voters-guide-endorsements': 'saveEndorsements',
'submit #voters-guide-photo' : 'savePhoto',
'click #table-of-contents a' : 'navTOC',
},
savePersonalInfo: function (event) {
console.log(event);
},
saveEssay: function (event) {
console.log(event);
},
saveSurvey: function (event) {
console.log(event);
},
saveEndorsements: function (event) {
console.log(event);
},
savePhoto: function(event) {
console.log(event);
},
navTOC: function (event) {
console.log(event.target);
var id = $(event.target).data('candidate-id');
var path = $(event.target).data('path');
//router.navigate("profile/manage/" + id + "/" + path, {trigger: true});
},
render: function (page) {
//Check to see if we have any errors
if (!this.model.get('error')) {
var dataToSend = {candidate: this.model.attributes};
switch(page) {
case 'personalInfo':
template = _.template(require('text!tpl/Candidate-personalInfo.html'));
break;
case 'essay':
template = _.template(require('text!tpl/Candidate-essay.html'));
break;
case 'survey':
template = _.template(require('text!tpl/Candidate-survey.html'));
break;
case 'endorsements':
template = _.template(require('text!tpl/Candidate-endorsements.html'));
break;
case 'photo':
template = _.template(require('text!tpl/Candidate-photo.html'));
break;
default:
break;
}
this.$el.html(template(dataToSend));
return this;
} else {
this.$el.html(errTemplate({candidate: this.model.attributes}));
return this;
}
}
});
});
Now, in an attempt to stop the 'the page content doesn't reload when I hit the back button' issue, I've been looking into the navigate function that backbone has available (this: router.navigate(fragment, [options]);). There are lots of examples of how this is used, but none of them seem to have anything similar to the file setup that I'm using, so I'm not exactly sure how best to access this functionality from my view. If I include the router file in the view and instantiate a new version of it, the page breaks b/c it tries to run the initialize function again.
I'm just really at a loss on how this is supposed to work.
Can someone point me in the right direction?
Thanks!
--Lisa
P.S. If someone has any better ideas, I am all ears!
You should have access to the Backbone object, which within it, has access to navigate around using the history.navigate function. If you call that passing in trigger: true you'll invoke the route. For instance:
Backbone.history.navigate("profile/manage", { trigger: true });
I have the following code but using pushState: true does not have the function called anymore, pre-pushstate with a hash it works, but when doing ?q=ok does not get read, when I had pushState not set #?q=ok works.
Here is the code:
(function(search,Backbone,$) {
$(function() {
app = new search.Views.AppView({
el: $("[search]")
});
var Router = Backbone.Router.extend({
routes: {
'(?:params*)': function(params) {
if (params) {
params = QueryString.decode(params);
}
else {
params = {};
}
if (params.q) {
app.setQuery(params.q);
}
}
}
});
var router = new Router();
Backbone.history.start({pushState: true});
});
})(search,Backbone,jQuery);
I have created a very basic backbone app, to understand how it works.
In the router, I just wanna display just 1 model, i.e. a user, not the whole collection, by passing an id in the url, how to do that?
For example, I'd like to do someapp.com/app/#user/2, and this would display just user no2 details.
Please see my work in jsfiddle
// router
var ViewsRouter = Backbone.Router.extend({
routes: {
'': 'viewOne',
'one': 'viewOne',
'two': 'viewTwo',
'user/:id': 'user'
},
viewOne: function() {
var view = new TheViewOne({ model: new TheModel });
},
viewTwo: function() {
var view = new UserView({ model: new TheModel });
},
user: function(id) {
// how to get just 1 user with the corresponding id passed as argument
// and display it???
}
});
Many thanks.
https://github.com/coding-idiot/BackboneCRUD
I've written a complete Backbone CRUD with no backend stuff for beginners. Below is the part where we get the user from the collection and show/render it.
View
var UserEditView = Backbone.View.extend({
render: function(options) {
if (options && options.id) {
var template = _.template($("#user-edit-template").html(), {
user: userCollection.get(options.id)
});
this.$el.html(template);
} else {
var template = _.template($("#user-edit-template").html(), {
user: null
});
// console.log(template);
this.$el.html(template);
}
return this;
},
Router
router.on('route:editUser', function(id) {
console.log("Show edit user view : " + id);
var userEditView = new UserEditView({
el: '.content'
});
userEditView.render({
id: id
});
});
Update
Particularly, sticking to your code, the router will look something like this :
user: function(id) {
var view = new UserView({ model: userCollection.get(id) });
}