I try to implement router which simply show view from defined map according to route's name.
This is my router:
/*global define*/
define([
'jquery',
'underscore',
'backbone',
'models/user',
'views/header',
'views/login',
'views/registration'
], function ($, _, Backbone, UserModel, HeaderView, LoginView, RegistrationView) {
'use strict';
var Router = Backbone.Router.extend({
models: {
userModel: {}
},
staticViews: {
header: {}
},
views: {
login: {},
registration: {}
},
routes: {
'login': 'login',
'registration': 'registration'
},
initialize: function () {
this.models.userModel = new UserModel();
this.staticViews.header = new HeaderView({el: 'nav#header', model: this.models.userModel})
this.views.login = new LoginView({el: 'section#lifeline-login', model: this.models.userModel});
this.views.registration = new RegistrationView({el: 'section#lifeline-registration', model: this.models.userModel});
this.hideAllViews();
this.navigate('login', {trigger: true, replace: true});
},
execute: function (callback, args, name) {
this.hideAllViews();
console.log(name); //undefined
console.log(callback);
var view = this.views[name];
if (view) {
view.$el.show();
}
if (callback) callback.apply(this, args);
},
hideAllViews: function () {
_.each(this.views, function (view) {
view.$el.hide();
});
},
login: function () {
},
registration: function () {
}
})
return Router ;
});
The main problem is undefined name in execute method.
I'm not sure is this the best way to do this. Another idea which comes to my mind is something like that, but it's little tricky:
routes: {
':name': 'change',
},
change: function (name) {
this.hideAllViews();
var view = this.views[name];
if (view) {
view.$el.show();
}
}
I see two possibilities:
You have a pre 1.2.0 Backbone, because
in latest version the name argument cannot be undefined but an empty string and
passing it into execute() was introduced with 62320783 which is contained in 1.2.0+
You manually create a route elsewhere using route() but missing to pass a handler name as 2nd argument
See the code of the 1.3.3 router:
route: function(route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
var router = this;
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
if (router.execute(callback, args, name) !== false) {
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
}
});
return this;
},
Update your Backbone or debug the cause of the undefined name by creating a breakpoint through the debugger in line 1 of route().
Ur backbone maybe too old!
At version 1.1.2,function execute has only 2 arguments,name is always null.
Related
I am new on backbone and using backbone with requirejs to make my first SPA.
When a user navigates to site.com/#report/1 an API call goes to site.com/app/report/1 and collects the data and renders the data in view page. Then, he goes to dashboard and navigates to site.com/#report/2 and another API call collects data of report 2 and displays as well. The problem is when I am using any event handler. In the view page, when anyone clicks on any event, in the callback it says that there is two JSON object saved as model. But it should be the second one only. The first report data also comes in console.log.
For your better understanding, I am giving the code of my router, model and view page.
Router
define([
'backbone',
'models/authlogin',
], function (Backbone, AuthloginModel) {
var AppRouter = Backbone.Router.extend({
routes: {
'': 'renderLoginPage',
'dashboard': 'renderDashBoard',
'report/:id': 'renderReportDetails',
'report/new': 'renderNewReportPage',
'members': 'renderMembersPage',
'member/:id': 'renderMemberPage',
'login': 'renderLoginPage',
'organization': 'renderOrganizationPage',
'programs': 'renderProgramsPage',
'program/:id': 'renderProgramPage',
'*path': 'errorpage'
},
errorpage: function () {
requirejs(['views/404', 'views/dashboard'], function (errorview, DashboardView) {
if ($('nav').length != 1) {
var authlogin = new AuthloginModel;
new DashboardView({model: authlogin});
}
new errorview;
});
},
renderLoginPage: function () {
requirejs(['models/auth', 'views/login'], function (AuthModel, LoginView) {
var auth = new AuthModel;
new LoginView({model: auth});
});
},
renderDashBoard: function () {
requirejs([
'collections/reports',
'views/reports',
'views/dashboard',
'../../javascript/main',
], function (ReportsCollection, ReportsView, DashboardView, MainJS) {
var authlogin = new AuthloginModel;
new DashboardView({model: authlogin});
var reports = new ReportsCollection;
reports.fetch({wait: true}).then(function () {
new ReportsView({collection: reports});
});
});
},
renderReportDetails: function (page) {
requirejs([
'models/reportdetails',
'views/reportdetails',
'views/dashboard'
], function (ReportDetailsModel, ReportdetailsView, DashboardView) {
var reportdetails = new ReportDetailsModel({id: page});
reportdetails.fetch().then(function () {
if ($('nav').length != 1) {
var authlogin = new AuthloginModel;
new DashboardView({model: authlogin});
}
;
new ReportdetailsView({model: reportdetails});
});
});
},
renderNewReportPage: function () {
requirejs([
'collections/newreports',
'views/reports',
'views/newreports',
'../../javascript/main',
], function (ReportsCollection, ReportsView, DashboardView, MainJS) {
})
},
renderMembersPage: function () {
requirejs([
"collections/members",
"collections/invitations",
"views/members",
'views/dashboard'
], function (MembersCollection, InvitationsCollection, MembersView, DashboardView) {
var members = new MembersCollection();
members.fetch().then(function () {
var invitations = new InvitationsCollection();
invitations.fetch().then(function () {
if ($('nav').length != 1) {
var authlogin = new AuthloginModel;
new DashboardView({model: authlogin});
}
new MembersView({collection: members, collection2: invitations});
});
});
});
},
renderOrganizationPage: function () {
requirejs([
'models/organization',
'models/orgMembers',
'views/organization',
'views/dashboard'
], function (OrgModel, OrgMembers, OrgView, DashboardView) {
var organization = new OrgModel();
var members = new OrgMembers();
organization.fetch().then(function () {
if ($('nav').length != 1) {
var authlogin = new AuthloginModel;
new DashboardView({model: authlogin});
}
members.fetch().then(function () {
new OrgView({model: organization, model2: members});
})
})
});
},
renderProgramsPage: function(){
requirejs([
'collections/programs',
'models/program',
'views/programs',
'views/program',
'views/dashboard'
],function(ProgramsCollection,ProgramsModel,ProgramsView,ProgramView,DashboardView){
var programscollection = new ProgramsCollection;
programscollection.fetch().then(function(){
if ($('nav').length != 1) {
var authlogin = new AuthloginModel;
new DashboardView({model: authlogin});
}
new ProgramsView({collection: programscollection});
});
});
}
});
var initialize = function () {
var app_router = new AppRouter;
Backbone.history.start();
};
return {
initialize: initialize
};
});
Model
define([
'underscore',
'backbone'
],function(_,Backbone){
var ReportDetailsModel = Backbone.Model.extend({
urlRoot: '/app/report/',
idAttribute: 'id'
});
return ReportDetailsModel;
});
View
define([
'backbone',
'../../javascript/magnify_pop_up',
'text!templates/reportdetails.html',
'text!templates/reportcomments.html'
], function (Backbone, Magnify, ReportDetailsTemplate, RepCommentsTemplate) {
var ReportdetailsView = Backbone.View.extend({
el: '#container-2',
template: _.template(ReportDetailsTemplate),
initialize: function () {
this.render();
},
events: {
"click #reject": "reject",
"click #resolve": "resolve",
"click button.triage": "triage"
},
triage: function (e) {
e.preventDefault();
console.log(this.model.toJSON());
/* this.model.save({'status':1},{patch:true,success:function(model,response){
console.log('saved');
},error:function(model,response){
console.log('error');
}});*/
},
resolve: function (e) {
e.preventDefault();
this.model.save({'status':2},{patch:true});
},
reject: function (e) {
e.preventDefault();
this.model.save({'status':3},{patch:true});
},
render: function () {
$('#container').html(this.template(this.model.toJSON()));
this.$el.html(_.template(RepCommentsTemplate)(this.model.toJSON()));
return this;
}
});
return ReportdetailsView;
});
I'm a beginner with angular and I try to understand if I should user a factory like that:
app.factory('FoobarServices', ['$http', function ($http) {
var Foobar = {
// Model
};
return {
getFoobar: function () {
},
setFoobar: function (Foobar) {
},
update: function (foobar) {
},
delete: function (id)
}
};
}]);
Or something like:
app.factory('Fooba', ['$http', function($http) {
function Foobar(foobar) {
// Initialize foobar
};
Foobar.prototype = {
getFoobars: function() {
},
setFoobar: function(foobar) {
},
update: function(foobar) {
},
delete: function(id) {
},
};
return Foobar;
}]);
I'm not sure to understand what's the pros and cons of each pattern, and which one is more suitable for an angular project.
Could you please tell me which one should I use?
It depends on how you want to use your service.
Factory is usually being used to store some constructor from which you can later instantiate some objects.
For example:
app.factory('Client', function () {
function Client (name) {
this.name = name;
}
Client.prototype.sayHello = function () {
console.log('Hello, my name is ' + this.name + '!');
}
return Client;
})
.controller('ClientController', function (Client) {
var bob = new Client('Bob');
})
If your service is singleton, you can register it as service instead of factory and angular will create an instance for you.
Or you can register it as factory but return some object with methods. It is useful when you don't want to deal with context (this) inside your service logic:
app.factory('ClientStorage', function () {
function set () {
// to be implemented
}
function get () {
// to be implemented
}
return {
get: get,
set: set
};
})
I've the following code:
require([templateName], function(template, view){
console.log(this); // outputs Window object
this.renderTemplate(template, {
articles: this.collection.toJSON() // rises error
});
});
now to make this work I need to change 'this' context as it is outside of it. How to achieve it?
/// EDIT - full code
define(['jquery', 'backbone', 'underscore', 'article/collections/Articles', 'generic-view-decorator','error-handler-decorator'],
function($, Backbone, _, ArticlesCollection, GenericViewDecorator, ErrorHelperDecorator) {
var ArticleView = Backbone.View.extend({
initialize: function(config) {
this.initializeCollection(ArticlesCollection, this.buildResourceUrl({
domain : 'oskszu.vgnett.no',
path : 'vgnett-dt/'+config.config.section + '/json',
query : [{
key : 'limit',
val : config.config.limit
}]
}));
this.initializeErrorCapturing(this.collection);
},
render: function() {
if(!this.collection.length) {
this.trigger('error-600');
} else {
var templateName = 'text!widgets/widgets/article/templates/article.tpl';
require([templateName]).call(this, function(template){
console.log(this);
/*this.renderTemplate(template, {
articles: this.collection.toJSON()
});*/
}, this);
}
}
});
_.extend(ArticleView.prototype, GenericViewDecorator);
_.extend(ArticleView.prototype, ErrorHelperDecorator);
return ArticleView;
}
);
Ok, I've solved it in the following way (for the future generations ;) ):
render: function() {
if(!this.collection.length) {
this.trigger('error-600');
} else {
var templateName = 'text!widgets/widgets/article/templates/article.tpl';
require([templateName], (function(template){
console.log(this);
/*this.renderTemplate(template, {
articles: this.collection.toJSON()
});*/
}).call(this));
}
}
I'm using the get backbone method on a collection but in the same file (router),in a function works while in other function doesn't work.Below the function where doesn't works
var Models = {};
var AppRouter = Backbone.Router.extend({
routes: {
"": "home",
"user/:id":"userDetails",
"settings":"settings",//mettere id dell utente loggato
"friends":"friends",
"mailbox":"mailbox",
"landscape":"landscape",
"gestione_richieste_amic":"gestione_richieste_amic"
},
friends: function(){
console.log("friend_router");
var self=this;
Models.utenti = new Usercollection();
Models.utenti.fetch({
success: function(object) {
console.log(object);
var view=new FriendsView({model:object});
self.changePage(view);
},
error: function(amici, error) {
}
});
console.log(Models.utenti);
var cur_user=Parse.User.current().id;
console.log(Models.utenti.get(cur_user));<--undefined, don't works here
console.log(cur_user);
} ,
The reason for this the Asynchronous nature of Ajax (fetch method).
The line where you log to the console will be executed before the collection is fetched. So you see an error.
1st Option - resolving the error is moving the log to inside of the success handler
friends: function () {
console.log("friend_router");
var self = this,
Models.utenti = new Usercollection();
Models.utenti.fetch({
success: function (object) {
console.log(object);
var view = new FriendsView({
model: object
});
self.changePage(view);
console.log(Models.utenti);
var cur_user = Parse.User.current().id;
console.log(Models.utenti.get(cur_user));
console.log(cur_user);
},
error: function (amici, error) {
}
});
},
2nd Option - you might take is to bind a sync event on the collection..
initialize: function () {
this.Models.utenti = new Usercollection();
this.listenTo(this.Models.utenti, 'sync', this.logCollection);
_.bindAll(this, 'logCollection');
},
logCollection: function () {
console.log(this.Models.utenti);
var cur_user = Parse.User.current().id;
console.log(this.Models.utenti.get(cur_user));
console.log(cur_user);
},
friends: function () {
console.log("friend_router");
var self = this;
this.Models.utenti.fetch({
success: function (object) {
console.log(object);
var view = new FriendsView({
model: object
});
self.changePage(view);
},
error: function (amici, error) {
}
});
},
In my app, not all users will have the same modules available. So, I'd like to load the routes, based on a json I load. This works, but I can't initialize the routes in Backbone. To load the files, I use Require.js.
To get everything working i use this code:
var initialize = function () {
//TODO Authentication check
$.ajax({
url: '/auth/test#test.com/test'
});
moduleNames = new Array();
appNames = new Array();
menu = new menuCollection;
menu.fetch( {
success: function(collection) {
collection.each(function(menuitem) {
moduleNames.push('apps/' + menuitem.attributes.href + '/router');
appNames.push(menuitem.attributes.href);
});
//Here something goes wrong
require(moduleNames, function(appNames) {
//////////////////
$.each(appNames, function(i, routerName) {
console.log(routerName);
objectName = 'router' + routerName.capitalize();
console.log(objectName);
varname = routerName + '_router';
console.log(varname);
var varname = this[objectName];
console.log(varname);
});
var home_router = new routerHome;
Backbone.history.start();
});
}
});
};
A typical router file looks like:
// Filename: router.js
define([
'jquery',
'underscore',
'backbone',
'apps/profile/views/info',
'apps/profile/views/contact',
], function ($, _, Backbone, viewInfo, viewContact) {
var routerSilentbob = Backbone.Router.extend({
routes:{
// Define some URL routes
'silentbob': 'showInfo',
'silentbob/info': 'showInfo',
'silentbob/contact': 'showContact'
},
showInfo:function () {
alert('testt');
},
showContact:function () {
viewContact.render();
}
});
return routerSilentbob;
});
This is the menu object:
[{"uuid":"041e42ee-9649-44d9-8282-5113e64798cf","href":"silentbob","title":"Silent Bob"},{"uuid":"111127aa-fdfc-45e5-978f-46f1d0ea0d89","href":"menu","title":"Menu"},{"uuid":"985574e5-f7ae-4c3f-a304-414b2dc769bb","href":"youtubeengine","title":"Youtube Engine"},{"uuid":"cc84424d-9888-44ef-9895-9c5cce5a999b","href":"cardgamesdk","title":"Cardgame SDK"},{"uuid":"73f4d188-4ec5-4866-84ec-ea0fa5901786","href":"flash2flashvideo","title":"Flash2Flash videotelefonie"},{"uuid":"0702f268-116d-4d62-98e2-8ca74d7ce5f3","href":"appstore","title":"Menu"},{"uuid":"2f8606e3-b81d-43bc-a764-a0811e402c6d","href":"me","title":"Mijn profiel"},{"uuid":"bb1acae2-a6c7-404c-861c-b8a838a19614","href":"contacts","title":"Contacten"},{"uuid":"9b6e6022-fe01-40ab-b8fb-df70d31c3b28","href":"messaging","title":"Berichten"},{"uuid":"29489359-3685-4b77-9faa-6c9f63e5fe09","href":"calendar","title":"Kalender"},{"uuid":"1c9541ff-2a25-40ca-b382-3c953d440f35","href":"cubigotv","title":"Cubigo TV"},{"uuid":"5b7af683-941b-45d7-bfae-9a9e12bb09c0","href":"links","title":"Websites"},{"uuid":"27efca4c-2b64-455d-8622-367f0f13d516","href":"ideabox","title":"Idee\u00ebn"},{"uuid":"84d2c2ea-7ce7-413e-963f-7b729590b5d9","href":"companyguide","title":"Bedrijven"},{"uuid":"2a61899f-d9de-478e-a03c-64a5fd6214d7","href":"associations","title":"Verenigingen"},{"uuid":"0cf05900-cee7-4f2e-87ae-7967315c2b93","href":"myneighbourhood","title":"Mijn buurt"},{"uuid":"01ae757b-d6a3-4ab0-98cb-a741572122bf","href":"htmlwebsite","title":"HtmlWebsite"}]
So, I can't find the right way to get the objects of my routers and load them into Backbone.
Is there a way where I won't need the variable as a parameter of my function?
Or can I load it different?
Have the "router" modules return the object literal passed to Backbone.Router.Extend()
define([], function () {
return {
// Router Definition here
}
})
Then create a router module like so:
define(arrayOfModulePaths, function () {
return {
listen: function(module) {
var Router = Backbone.Router.Extend(arguments[module]);
var router = new Router();
Backbone.history.start();
}
}
})
Then you can simply require the router module and pass the module index to router.listen()
require(moduleNames, function() {
var routerObjs = {};
$.each(arguments, function(i, CustomRoute) {
var routerName = appNames[i];
routerObjs['router' + routerName.capitalize()] = CustomRoute;
});
var home_router = new routerObjs.routerHome;
Backbone.history.start();
});