How to load javascript files dynamically with Require.js and Backbone? - javascript

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();
});

Related

Backbone's Router.execute(callback, args, name) method gets undefined name

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.

I am having trouble passing in my backbone collection in to a react component

my backbone collection collection doesn't populate when i just pass it in as props to a react component. I have tried first fetching the collection using componentDidmount and componentWillMount, but that still didn't populate the collection. If I test the code by setting a window variable pointing to DecksIndex and in the console tools call getInstance() and then fetch
,the data loads fine. my code is as follows:
//router.js
var DeckComponent = require("./views/deck.jsx")
var DecksIndex = React.createFactory(require("./views/decks.jsx"))
var decksCollection = require("./component/collections/decks.js");
module.exports = Backbone.Router.extend({
initialize: function(){
this.rootEl = document.getElementById('container');
},
routes: {
"":"index",
"decks/:id":"deckShow"
},
index: function(){
var decks = new DecksIndex({decks: decksCollection.getInstance()});
this._swapView(decks)
console.log("hooray!")
},
deckShow: function(id){
//var deck = Flashcards.Collections.decks.getOrFetch(id);
var showDeck = new DeckComponent();
this._swapView(showDeck);
},
_swapView: function(view){
if (this.currentView) {
React.unmountComponentAtNode(this.rootEl);
}
this.currentView = view
React.render(view, document.getElementById('container'));
}
});
//decks.js
var deck = require('../models/deck.js')
var decks = Backbone.Collection.extend({
url: "/api/decks",
model: deck,
getOrFetch: function(id){
var model = this.get(id);
var that = this;
if (model) {
model.fetch();
}else{
model = new deck({id: id})
model.fetch({
success: function(){
that.add(model)
}
})
}
return model;
},
parse: function (data) {
debugger;
return data.objects
},
});
decks.getInstance = _.memoize(function () {
return new decks();
});
module.exports = decks;
//decks.jsx
var DecksList = React.createClass({
render: function() {
return (
<div className="deck-list">
{
this.props.decks.map(function (deck) {
var title = deck.name
debugger;
return (
<div key={deck.id} className="note-summary">
{title}
</div>
);
})
}
</div>
);
}
});
module.exports = DecksList;
this is an example of a situation where a container component that manages state makes sense. If DecksList had a container that retrieved the collection when it mounted and only rendered DecksList once the data was available it would probably solve the problem. Here's a good article on the pattern: https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0

Protractor Page objects - TypeError: Object #<Object> has no method 'methodName'

I'm trying to write a simple test using page objects pattern - based on the 'docs/page-objects'.
I created a file describing the page object and other using this page object to test a page.
//page object
var LoginPage = function() {
this.userInput = browser.driver.findElement(by.id('username'));
this.pwdInput = browser.driver.findElement(by.id('password'));
this.btnEnter = browser.driver.findElement(by.id('btnLogin'));
this.get = function(){
browser.get('http://example.com');
};
this.setUser = function (user){
this.userInput.sendKeys(user);
};
this.setPasswd = function (password) {
this.pwdInput.sendKeys(password);
};
this.clickBtnEnter = function (){
btnEnter.click();
};};
The spec file:
var loginPage = require('./LoginPage.js');
describe('myApp', function() {
it('should save contract config', function (){
loginPage.get();
loginPage.setUser('userid');
loginPage.setPasswd('passwd');
loginPage.clickBtnEnter();
});
});
The following error is shown when I run this test: TypeError: Object # has no method 'get' - at this line: loginPage.get();.
When I was searching for this problem I found various approaches about using page objects in Protractor, such as Astrolable.
Now I am not sure about the correct usage of page objects.
Do you have any ideas about how I can fix this test?
Thank you guys.
Try this:
Ensure you have the following in your LoginPage.js file
module.exports = LoginPage;
Add the missing new keyword
var LoginPage = require('./LoginPage.js');
var loginPage = new LoginPage();
After trying the above syntax (no success) I rewrote the page object using the Astrolable. Now it works! My test looks like this:
//pageobject
'use strict';
var env = require('./environment.js')
var LoginPage = function () {
browser.driver.get('http://example.com');
};
LoginPage.prototype = Object.create({}, {
userInput: { get: function() { return browser.driver.findElement(by.id('username'));}},
pwdInput: { get: function() { return browser.driver.findElement(by.id('password'));}},
btnEnter: { get: function() { return browser.driver.findElement(by.id('btnLogin'));}},
setUser: { value: function (loginName) {
this.userInput.sendKeys(loginName);
}},
setPasswd: { value: function (loginPass) {
this.pwdInput.sendKeys(loginPass);
}},
clickBtnEnter: { get: function() { return this.btnEnter.click();}}
});
module.exports = LoginPage;
Spec file:
'use strict';
var loginPage = require('./LoginPage.js');
describe('myApp', function() {
var poLogin = new loginPage();
it('should save contract config', function (){
poLogin.setUser('userid');
poLogin.setPasswd('passwd');
poLogin.clickBtnEnter;
});
});
Now it is working fine.
Thanks for answering.

Backbone.js create object from string

I want to create objects like this
var classes = {
A: <object here>,
B: <object here>,
...
};
new classes[name]()
However I cannot get it to work in my backbone app.
define([
// Application.
"app"
],
function(app) {
var Partials = app.module();
var classes = {
'AccountNavStart' : Partials.Views.AccountNavStart = Backbone.View.extend({
template: 'member/account-nav',
serialize: function(){
return { model: this.model };
}
})
};
// Required, return the module for AMD compliance.
return Partials;
});
and I try to use it like this
new Partials.classes['AccountNavStart']()
But I get an error
Uncaught TypeError: Cannot read property 'AccountNavStart' of undefined
Any ideas how to solve this problem?
Save classes to Partials before returning it:
function(app) {
//...
Partials.classes = classes;
return Partials;
});

backbone event listener on singleton object

I am trying to create a singleton object as a model for a view on backbone, and I want to re-render the view whenever this singleton object is being changed and updated. I am not sure the following code would be the right way of doing it or not
Model
define(function(require) {
var Singleton = require("modules/Singleton");
var Singleton = null;
var SingletonHolder = {
init: function() {
Singleton = new Singleton();
return Singleton.fetch();
},
current: function() {
return Singleton;
},
refresh: function() {
return Singleton.fetch();
}
};
return SingletonHolder;
});
Controller
var currentObj = SingletonHolder.current();
var tempView = new TempView({
model: currentObj
});
View
var TempView = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, "change", this.render);
}
render: function() {
...
}
});
For some reasons it doesn't work. Did i miss anything ? I also tried to call Singleton.refresh which it goes to the server side and fetches the latest data from the database, but it doesn't detect the changes and re-render the view.
You don't have to define a Singleton if you already use requirejs.
Singleton:
define(['models/foo'], function(FooModel){
return new FooModel;
})
Controller:
define(['foosingleton', 'tempview'], function(fooSingleton, TempView){
var tempView = new TempView({
model: fooSingleton
});
});
Here is a similar question: Is it a bad practice to use the requireJS module as a singleton?

Categories