Reuse a Backbone Marionette Module on a single page - javascript

I'm really enjoying Marionette and the structure it adds to Backbone. However, I'm a little stumped with how to reuse a Module on a single page.
I have a Marionette Module that renders and handles events for a category tree. I would like to reuse this Module on the same page, that would display different collections in different regions in the page. I came to realize that the Marionette Modules are essentially singletons on the Application object, which is also a singleton. I apparently can't create a new instance of a Module to display and handle events for a new collection in a different region. Likewise, I can't register and trigger events on the Module, because there is only one and the events triggered in each region would need to be independent of each other.
Am I thinking about Modules incorrectly? How can they reused on the same page with different regions / collections?

Think of modules as namespaces. You would never try to duplicate a namespace, right? But it's very common to have a namespace expose public types that other parts of the code can create multiple instances of.
App.module('CategoryTree', function(self, App, Backbone, Marionette, $, _) {
self.Collection = Backbone.Collection.extend( ... );
self.CollectionView = Marionette.CollectionView.extend( ... );
...
});
//elsewhere...
var workingCollection = new App.CategoryTree.Collection( ... );
var workingView = new App.CategoryTree.CollectionView( ... );
var previewCollection = new App.CategoryTree.Collection( ... );
var previewView = new App.CategoryTree.CollectionView( ... );

EDIT I figured out what I don't like about this... If you need a show an unknown number of category trees (in response to an AJAX call for instance) you are dead in the water.
I found one potential solution that seems to be working. Basically, a Module definition is just a function. Instead of only using that function when adding a Module to the Application, I define the function as-is somewhere else in the global scope so I can get to it.
Project.Modules.CategoryTree = function(self, App, Backbone, Marionette, $, _) {
// ... module definition goes here ...
}
Any time I want to use this module, I essentially add it as a submodule to an existing Module or Application. Like so...
App.module("WorkingTree", Project.Modules.CategoryTree)
App.module("PreviewTree", Project.Modules.CategoryTree)
While this does reuse the module code by creating two distinct instances, it doesn't smell quite right to me. Is there a better solution out there?

Related

Communication between ViewModels in KnockoutJS

I'm trying to implement communication between my view models in a knockoutjs driven application. I scaffolded it using yeoman tool, and as you can see it uses AMD:
define(['knockout', 'signals', 'text!./nav-bar.html'], function(ko, Signal, template) {
function NavBarViewModel(params) {
this.route = params.route;
}
return { viewModel: NavBarViewModel, template: template };
});
I have to define an object that I would later use to dispatch events, right? Something like that:
var EventDispatcher = {
itemClicked: new Signal()
};
And then, whenever something happens in the NavBarViewModel I'd like to do:
EventDispatcher.itemClicked.dispatch();
The problem is - where should I put this EventDispatcher thing? It's obvious that it should be some kind of a global object, so that every VM could get a hold on it, but it would be ugly. Dependency injection came to mind, since everything else in this architecture I choose is done this way, but how to achieve that? I come from WPF, MVVM world, and so far I've used MVVMLight framework which had this great Messenger component. Is there anything like that in the JS world (and if it's js-signals lib I'm already using, then how should I use it to achieve my goal?)
I could also use the subscribable object built into the knockout fw, but the question still stands - where to put it (or how to share the instance between VMs)?
You'd quite simply inject it by including it in your define.
First, create a new file, EventDispatcher.js with your EventDispatcher code inside (and other relevant Knockout bits, like returning the view model and whatnot).
Then in your current file add it in:
define([ ... , ... , "EventDispatcher"], function( ... , ... , EventDispatcher )
Now you can simply call its methods within this file by using:
EventDispatcher.itemClicked.dispatch()
(Where EventDispatcher is what we've named it in our define parameters).
Do bear in mind though that your EventDispatcher.js file will also need the signals file passed to it through its own define wrapper.

Backbone.js and global instances

we're pretty new to backbone and building a new app with it.. we're using the standard namespacing ie:
(function($) {
window.Lara = {
Models: {},
Collections: {},
Views: {},
Events: {},
Templates: {}
};
var vent = _.extend({}, Backbone.Events);
}(jQuery));
We need to keep state of certain views so that they're accessible across the different models and views, my question is where should instances be held of all the models and views etc... I'm finding it hard to keep things within scope between all the events and different views so I could just put all the needed global instances somewhere within the App namespace... is this the right approach?
I feel like the defined Lara.Models, Lara.Views etc should be kept clean and as a template for instances... should I just create an Lara.Instances and dump them all in there?
Any suggestions would be great here!
Not quite sure about your questions, you need refs to your existing views/models?
I would keep an reference for important models like UserSession in the Application scope, for your case, it would be something like
Lara.currentUser = new Lara.Models.UserSession() # Set it when user logs in
Is this something you are looking for? Leve me the comment

backbone.js - how to communicate between views?

I have a single page web app with multiple backbone.js views. The views must sometimes communicate with each other. Two examples:
When there are two ways views presenting a collection in different ways simultaneously and a click on an item in one view must be relayed to the other view.
When a user transitions to the next stage of the process and the first view passes data to the second.
To decouple the views as much as possible I currently use custom events to pass the data ($(document).trigger('customEvent', data)). Is there a better way to do this?
One widely used technique is extending the Backbone.Events -object to create your personal global events aggregator.
var vent = {}; // or App.vent depending how you want to do this
_.extend(vent, Backbone.Events);
Depending if you're using requirejs or something else, you might want to separate this into its own module or make it an attribute of your Application object. Now you can trigger and listen to events anywhere in your app.
// View1
vent.trigger('some_event', data1, data2, data3, ...);
// View2
vent.on('some_event', this.reaction_to_some_event);
This also allows you to use the event aggregator to communicate between models, collections, the router etc. Here is Martin Fowler's concept for the event aggregator (not in javascript). And here is a more backboney implementation and reflection on the subject more in the vein of Backbone.Marionette, but most of it is applicable to vanilla Backbone.
Hope this helped!
I agree with #jakee at first part
var vent = {};
_.extend(vent, Backbone.Events);
however, listening a global event with "on" may cause a memory leak and zombie view problem and that also causes multiple action handler calls etc.
Instead of "on", you should use "listenTo" in your view
this.listenTo(vent, "someEvent", yourHandlerFunction);
thus, when you remove your view by view.remove(), this handler will be also removed, because handler is bound to your view.
When triggering your global event, just use
vent.trigger("someEvent",parameters);
jakee's answer suggests a fine approach that I myself have used, but there is another interesting way, and that is to inject a reference to an object into each view instance, with the injected object in turn containing references to as many views as you want to aggregate.
In essence the view-aggregator is a sort of "App" object, and things beside views could be attached, e.g. collections. It does involve extending the view(s) and so might not be to everybody's taste, but on the other hand the extending serves as a simple example for doing so.
I used the code at http://arturadib.com/hello-backbonejs/docs/1.html as the basis for my ListView and then I got the following to work:
define(
['./listView'],
function (ListView) {
var APP = {
VIEWS : {}
}
ListView.instantiator = ListView.extend({
initialize : function() {
this.app = APP;
ListView.prototype.initialize.apply(this, arguments);
}
});
APP.VIEWS.ListView = new ListView.instantiator();
console.log(APP.VIEWS.ListView.app);
}
);

Node.JS - Using prototype in a module

So I'm writing a whole bunch of vendor-specific files in node which all have a similar controller pattern, so it makes sense for me to cut them out and put into a common file.
You can see my common controller file here: https://gist.github.com/081a04073656bf28f46b
Now when I use them in my multiple modules, each consecutively loaded module is overwriting the first. This is because the file is only required once and passed dynamically through to each module on load (this allows me to add extra modules and these modules are able to add their own routes, for example). You can see an example module here: https://gist.github.com/2382bf93298e0fc58599
You can see here on line 53 I've realised that we need to create a seperate instance every time, so I've tried to create a new instance by copying the standardControllers object into a new object, then initialising the new object. This has zero impact on the code, and the code behaves in exactly the same way.
Any ideas guys? I'm in a bit of a jam with this one!
First thing I'd do is try to make things simpler and reduce coupling by invoking the single responsibility principle, et al.
http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html
Put those Schemas into their own files, eg
models/client.js
models/assistant.js
models/contact.js
I've also found that embedded docs + mongoose is generally a PITA. I'd probably promote all those to top level docs.
You don't need to enclose your object's keys in quotes.
routes = {
list: function() {} // no quotes is aok
}
Also 'list' in typical REST apps is called 'index'. Anyway.
Ok, I'd break this up differently. Since you're requiring stuff from the index.js file in the middleware, they become tightly coupled, which is bad. in fact, I think I'd rewrite this whole thing so it was tidier. Sorry.
I'd probably replace your 'middleware' file with an express-resource controller
https://github.com/visionmedia/express-resource (built by author of express). This is a good framework for restful controllers, such as what you're building. The auto-loader is really sweet.
You may also want to look at: http://mcavage.github.com/node-restify/ It's new, I haven't tried it out, but I've heard good things.
Since what you're building is basically an automated mongoose-crud system, with optional overriding, I'd create an express-resource controller as your base
/controllers/base_controller.js
and it might look like
var BaseController = function() {} // BaseController constructor
BaseController.prototype.index = function() {
// copy from your middleware
}
BaseController.prototype.show = function() {
// copy from your middleware
}
BaseController.prototype.create = function() {
// copy from your middleware
}
// etc
module.exports = BaseController
Then I'd do something like:
/controllers/some_resource_controller.js
which might look something like:
var BaseController = require('./base_controller')
var NewResourceController = function() {
// Apply BaseController constructor (i.e. call super())
BaseController.apply(this, arguments)
}
NewResourceController.prototype = new Base()
NewResourceController.prototype.create = function() {
// custom create method goes here
}
module.exports = NewResourceController
Then to use it, you can do:
var user = app.resource(myResourceName, new ResourceController());
…inside some loop which sets myResourceName to be whatever crud you're trying to set up.
Here's some links for you to read:
http://tobyho.com/2011/11/11/js-object-inheritance/
http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/
Also, it sounds like you're not writing tests. Write tests.
http://www.codinghorror.com/blog/2006/07/i-pity-the-fool-who-doesnt-write-unit-tests.html

Backbone/RequireJS and multiple models

I am having trouble wrapping my mind around primarily RequireJS. I see that it is a good/necessary technology, but implementing it, for me, has been a real stretch. I greatly appreciate your help!
I am trying to develop a fairly flexible application with Backbone and RequireJS. The problem is that I am totally used to syntax like new Person() without having to specify dependencies. Is there an efficient way to use RequireJS with quite a number of models? I think my problem is always working with returns. I considered using a factory method to create the model with the require function, but doing so necessitates that the require function is synchronous, which completely defeats the purpose of RequireJS.
It just doesn't seem right to have to require all of my models first and then include those in the instantiation function - or do I?
Do you have any suggestions or tutorials about how to structure and model an application like this?
Thank you for helping me out!
JMax
You can use what I call require js module pattern. If you know a group of classes are often used together you can do something like this.
First you define each class in a separate file and then you define a module to hold them together
Module.js
define([
'./models/FirstModel',
'./models/SecondModel',
'./views/FirstView',
'./views/SecondView',
'txt!./templates/template.tpl'
], function(FirstModel, SecondModel, FirstView, SecondView, template) {
return {
FirstModel: FirstModel,
SecondModel: SecondModel,
FirstView: FirstView,
SecondView: SecondView,
template: template
}
});
And then when you want to use class from this module you just do
define(['./Module'], function(Module) {
var AView = Module.FirstView.extend({
model: Module.FirstModel,
render: function() {
this.html(_.template(Module.template)(this.model.attributes));
if (something) {
this.$el.append(new Module.SecondView().render().el);
}
}
})
return AView;
});
I don't believe using modules defined with requirejs we should return an instance - we should always return a constructor or an object.
You should totally embrace the defining and requiring - with time you'll start to love it - without having to think much about adding/tracking dependencies etc. everywhere by hand or (so 2005!) having most of the stuff in one file :)

Categories