Backbone and Requirejs. How to access to my backbone router from everywhere - object

I am working on my first project using requirejs. I have a router and a view and I would like to have access to my Router.navigate() method from the view when an item is clicked. I using CoffeeScript. how can I make router global?
router.coffee:
define [
'jquery'
'backbone'
'application/views/mainView'
'application/models/app'
],($,Backbone,MainView,App)->
class Router extends Backbone.Router
routes:
'organisation': 'organisationScreen'
'*actions': 'organisationScreen'
constructor:() ->
super #routes
initialize:()->
Backbone.history.start() #pushState: true
console.log " The Route Initialized"
organisationScreen:()->
$('.slides').fadeOut()
$('.confBlock').removeClass('onshow')
$('.organisationsBlock').addClass('onshow')
view.coffee
define [
'jquery'
'backbone'
'application/views/conferenceView'
],($,Backbone,ConferenceView)->
class OrganisationView extends Backbone.View
#el: '#appcontainer'
tagName : 'li'
className : 'organisation'
events:
'click .org-item' : 'choice'
template : _.template($('#Organisation-template').html())
initialize : ()->
...
render: ()->
...
choice:(ev)->
# Call Router.navigate()

All Router::navigate does is call Backbone.history.navigate so I would just use that. Here is the source
choice: (ev)->
Backbone.history.navigate(...);

Simply store your router along with other global variables you need in the Backbone object.
in your router init do the following:
Backbone.application = {};
Backbone.application.router = this;
then use it like this:
define(["backbone"], function (Backbone) {
Backbone.application.router.myaction();
});

I see three options for your problem:
Pass router in view constructor and use as local variable #options.router
Make circular dependency between two modules (check this answer https://stackoverflow.com/a/16314250/329226)
Trigger custom navigation event from view and listen to it from within the router

Related

require.js shim, export myOwnGlobal name

I'm not sure about the use of "exports" on shim config, following the example on the requireJS API, I can use Backbone (B in capital letter) to export it to a global scope.
This means that it will be a window object property.
But I realized that I'm forced to use that name, and I can't export it by other reference name, ie: "MyGlobalBackbone"
require.config({
paths: {
backboneAlias:'backbone'
},
shim : {
backboneAlias : {
deps : [ 'underscore', 'jquery-1.9.1' ],
exports : 'MyGlobalBackbone'
}
}
});
require(['backboneAlias'],function(backboneAsAliasDependency){
console.log(backboneAsAliasDependency);//Loaded Ok
console.log(MyGlobalBackbone); //Uncaught ReferenceError: MyGlobalBackbone is not defined
});
This code only works if I use "Backbone" instead of "MyGlobalBackbone"...
Actually you got it the other way around: shimming doesn't export a variable to global scope, it imports it FROM the global scope. The name ("Backbone") was set by Backbone's author, and this is the part you're explaining to RequireJS in shim config element.
See it in the API:
http://requirejs.org/docs/api.html#config-shim
Look at this sentence:
//Once loaded, use the global 'Backbone' as the
//module value.
Let's see it in that way, you will understand it:
//Once loaded, use a global variable 'Backbone' that defined by the backbone vendor as the
//module value.
You should use map to make an alias.
require.config({
paths: {
...
},
shim : {
...
},
map: {
'*': {
'MyGlobalBackbone': 'Backbone'
}
}
});
This will allow you to use MyGlobalBackbone instead of Backbone for all (*) modules.

requirejs - what export exactly do here?

I use the require.js - when i use the requiredjs, i am not get the underscore and backbone while i console without using shim's export modoule.
But jquery not asking this export shim dependent.. so, why we need to use the shim and it's export for the underscore and backbone?
here is my code:
requirejs.config({
baseUrl: 'js',
paths: {
"jquery":'lib/jquery-1.9.1.min',
"underscore":"lib/underscore-min",
"backbone" : "lib/backbone-min"
},
shim:{
"underscore":{
exports: '_'
//what is does here? without this i am getting undefined
},
"backbone":{
exports: 'Backbone'
//what is does here? without this i am getting undefined
}
}
});
require(["jquery","underscore","backbone"],function ($,_,Backbone) {
console.log($,_,Backbone);
//without shim export i am getting conosle like this:
// "function(), undefined, udefined" - why?
});
Backbone and underscore aren't AMD-compliant, they store themselves in global scope (i.e. in the window element in a browser environment). shim element allows exposing their global variables like if they were AMD modules, by "linking" the global variable (_ in case of underscore and Backbone in case of Backbone) with the "exports" part of the "virtual" module (I called it "virtual" because this happens on the fly, you don't have to change any code).
This:
"underscore":{
exports: '_'
}
means that adding dependency on "underscore" will grab a reference to window._ and expose it as an AMD module.
jQuery doesn't need it because it detects whether it's loaded as an AMD module and exposes itself in an AMD-compliant way in that case (scroll down to the very bottom of the original source code for more details)

How to make Backbone-Relational (0.8.5) work with RequireJS?

After another long research, sth comes out :-) It seems the problem is about the function "getObjectByName". It can not work well with requireJS(ADM). Currently, I have to setup a globel var to fix the problem. I am sure there must be have better solution.
Here is my temp soluton:
(1) setup a global var and setup the search model scope to the global ("APP")
var APP = {};
define(['backbone-relational'], function(){
Backbone.Relational.store.addModelScope(APP);
})
(2) export your relation model to the global
APP.YourRelationalModel = YourRelationModel;
It works, not good though... I'm really looking forward to a better answer. Thanks.
//------------
test versions:
1.Backbone-Relational 0.8.5
2.Backbone 1.0.0 and Underscore 1.4.4
3.JQuery 1.8.3
4.RequireJS 2.1.5
Code is very simple: (or see https://github.com/bighammer/test_relational_amd.git)
require.config({
paths : {
js : 'js',
jquery : 'js/jquery-1.8.3',
underscore : 'js/underscore',
backbone : 'js/backbone',
'backbone-relational' : 'js/backbone-relational'
},
shim : {
underscore : {
exports : '_'
},
backbone : {
deps : ['underscore', 'jquery'],
exports : 'Backbone'
},
'backbone-relational' : {
deps: ['backbone']
}
}
});
define(['backbone', 'backbone-relational'], function (Backbone) {
var Child = Backbone.RelationalModel.extend();
var Parent = Backbone.RelationalModel.extend({
relations : [
{
key : 'child',
type : Backbone.HasOne,
relatedModel : 'Child'
}
]
});
var test = new Parent();
});
save above code in main.js and included in index.html as follows:
It doesn't work. There is warning message:
Relation=child: missing model, key or relatedModel (function (){ return parent.apply(this, arguments); }, "child", undefined).
I read the source code of backbone-relational and know there is something wrong with the namespace. Relational-Backbone cannot find the relatedModel defined in "Parent" (i.e. cannot find releatedMode:"Child"). I failed to find the solution to fix this due to my limited knowledge of javascript :-)
Can anyone help me with this?
Before I asked my question, I studied the following solutions:
Backbone.RelationalModel using requireJs
Can't get Backbone-relational to work with AMD (RequireJS)
Loading Backbone.Relational using Use! plugin
None of them worked in this case.
You don't have to reference relatedModel by string, you can reference it directly, so instead of relatedModel: 'Child', just use: relatedModel: Child.
And since you are using requireJS, you can reference model from other file easily.
define(['backbone', 'models/child', 'backbone-relational'], function (Backbone, Child) {
var Parent = Backbone.RelationalModel.extend({
relations : [{
key : 'child',
type : Backbone.HasOne,
relatedModel : Child
}]
});
var test = new Parent();
});
The above solution didn't apply to me. I am gradually moving code out of Rails Asset Pipeline (not RequireJS/AMD/CommonJS/anything) into Webpack, starting with dependencies. When I moved requiring backbone-relational into Webpack bundle preparation by my models and relation definitions were still in Rails Asset Pipeline, I started getting a lot of unexplained Relation=child: missing model, key or relatedModel (function (){ return parent.apply(this, arguments); }, "child", undefined).
In my case, the solution ended up being quite simple, despite taking a long time to discover on my part:
// In a Webpack module, later included into Rails Asset Pipeline
// temporarily to facilitate migration
require('expose?Backbone!backbone') // Exposing just for migration
require('backbone-relational')
Backbone.Relational.store.addModelScope(window)
backbone-relational by default uses its global scope to resolve string-based relatedModels but since it was required without a real global scope, the solution is simply to pass that in using addModelScope so it can search that scope for the specified models.

Having trouble defining global var in require.js

I'm trying to define a global object that i can reference across all of my modules. however, in the modules, i am unable to reference my path, and it's saying that "g" does not exist.
In main1.js, i have this:
requirejs.config({
paths: {
Underscore: 'lib/underscore/1.3.3/underscore.min',
Backbone: 'lib/backbone/0.9.2/backbone.min',
Globals: 'lib/backbone/ globalVars'
}
});
require([ 'views/pages', 'views/filters'], function(allPages, filters) {
filters.render();
allPages.render();
});
inside globalVars.js, i have this:
(function() {
var Globals = {
isDemo: false
}
console.log('in globalvars') // this shows in my console
}).call(this);
and finally, inside of view/pages.js, i have this:
define([
'Globals',
'Underscore',
'Backbone'
], function(g, _, Backbone){
console.log(g.isDemo) //<-- returns "TypeError: g is undefined"
If i use a define inside my main1.js like this:
define( 'Globals', function() {
return {
isDemo: true
}
})
it works just fine. I haven't had much luck with trying to figure out why this is not working. I'd like to be able to just include a path to the globalVars rather than boilerplate pasting a define block in each and every module that needs it, since changing isDemo to false would require updating many other module pages (main2.js, main3.js, etc) as well. thanks!
Well, to start with, your globalVars.js is not in the module pattern, so requirejs doesn't know what you're trying to register as the module. If you change that file to use the pattern, like the define you added to main1.js, you should be all set. Is there a reason you aren't defining it as a module?

Routes not processed

I've got a node.js (server) and backbone.js (client) app - I can load and init my backbone app on a page... and init the router, but my default route (".*") is not getting called. I can manually call the index function after I initialize the router, but I don't have to take that step when I've built backbone apps over rails.
Does anyone have a clue as to why this is happening?
adding code (in coffeescript):
class NodeNetBackbone.Routers.RegistryPatients extends Backbone.Router
routes:
'' : 'index'
'.*' : 'index'
'/index' : 'index'
'/:id' : 'show'
'/new' : 'new'
'/:id/edit' : 'edit'
initialize: ->
console.log 'init the router'
#registry_patients = new NodeNetBackbone.Collections.RegistryPatients()
# TODO: Figure out why this isn't sticking...
#registry_patients.model = NodeNetBackbone.Models.RegistryPatient
# TODO: Try to only round trip once on initial load
# #registry_patients.reset($('#container_data').attr('data'))
#registry_patients.fetch()
# TODO: SSI - why are the routes not getting processed?
this.index()
index: ->
console.log 'made it to the route index'
view = new NodeNetBackbone.Views.RegistryPatients.Index(collection: #registry_patients)
# $('#container').html('<h1>Patients V3: (Backbone):</h1>')
$('#container').html(view.render().el)
Backbone routes are not regexes (unless you manually add a regex route using route). From the fine manual:
Routes can contain parameter parts, :param, which match a single URL component between slashes; and splat parts *splat, which can match any number of URL components.
[...] A route of "file/*path" will match #file/nested/folder/file.txt, passing "nested/folder/file.txt" to the action.
And if we check the source, we'll see this:
// Backbone.Router
// -------------------
//...
// Cached regular expressions for matching named param parts and splatted
// parts of route strings.
var namedParam = /:\w+/g;
var splatParam = /\*\w+/g;
So your '.*' route should only match a literal '.*' rather than matching "anything" as you're expecting.
I think you want something more like this:
routes:
'' : 'index'
#...
'*path' : 'index'
Make sure your *path route is at the bottom of your route list:
// Bind all defined routes to `Backbone.history`. We have to reverse the
// order of the routes here to support behavior where the most general
// routes can be defined at the bottom of the route map.
This assumption about the "order" of elements in an object seems rather dangerous and ill-considered to me as there is no guaranteed order:
The mechanics and order of enumerating the properties (step 6.a in the first algorithm, step 7.a in the second) is not specified.
I think you'd be better off adding your default *path route manually in your initialize method:
class NodeNetBackbone.Routers.RegistryPatients extends Backbone.Router
routes:
'' : 'index'
'/index' : 'index'
'/:id' : 'show'
'/new' : 'new'
'/:id/edit' : 'edit'
initialize: ->
console.log 'init the router'
#route '*path', 'index'
#...

Resources