Multiple, extendable components on Backbone - javascript

Will keep it short: I have the need to build different components for my app. What I'm calling a "component" here is a collection of methods and constructors that repeats itself in many places of my app but is not necessarily exactly the same.
For the sake of an example, consider a pagination component. It is made of a few methods, a view and a template:
var Pagination = function() {
this.View = Backbone.View.extend({});
this.method = function() {};
this.method2 = function() {};
};
// Create instance
var myComponent = new Pagination();
Heavily abstracted code, but gives the idea. Now, I've created a "Component" constructor to aid me in my task. It is fairly simple:
Component = function() {
this.initialize.apply(this, arguments);
};
_.extend(Component.prototype, {
initialize: function() {}
});
Component.extend = Backbone.Model.extend;
This allows me to build multiple instances of the same component with slightly different methods or contents, without having to rebuild the same thing over and over again:
var Pagination = Component.extend({
View: Backbone.View.extend({}),
method: function() {},
method2: function() {}
}};
// Create instance
var myComponent = new Pagination();
So far so good. myComponent has the list of methods as well as the constructor for the View. Here is where the problem lies: for some of my Components's instances I need to extend not only the component itself, but the Backbone constructors inside it as well. Say, for example, that I want to extend (not replace) the View inside my pagination component. That's were everything falls down.
This is the last approach I've had, but the basic idea is: I have components that repeat themselves frequently in my app but with slight differences between them. These components may include methods, backbone constructors and primitive values inside of them. I need to be able to extend these but ALSO be able to extend it's individual parts (Backbone constructors) if the need arises.Anyone has an idea on how to accomplish this?

Related

Object initialization within another object

Today I was playing around with Backbone and Javascript, and came upon an interesting problem. Consider the following:
var App = {};
App.model = Backbone.Model.Extend({/* Omitted for brevity */});
App.view = Backbone.View.Extend({/* Omitted for brevity */});
App.obj = new App.view();
At this point I wanted to refactor for readability and mantainability's sake:
var App = {
model: Backbone.Model.Extend({}),
view: Backbone.View.Extend({}),
obj: new view() // Here comes trouble
}
The snippet above is what I wanted to obtain, but obviously the initialization doesn't work:
view is not in scope
App is not yet initialized, so App.view isn't usable.
this refers to window, so this.view isn't usable.
At this point, with the help of this answer I concocted a solution:
var App = {
model: Backbone.Model.Extend({}),
view: Backbone.View.Extend({}),
get obj() { delete this.obj; this.obj = new this.view(); return this.view; }
}
Using the getter I'm able to delay the creation of the object instance until used (thus after completing App initialization), and then I replace the getter from within with the object instance.
Everything works as expected, but since I'm not exactly fluent with modern JS I was wondering if there was a different or proper way to achieve what I wanted. I was also wondering if this sort of implementation could cause drawbacks or unexpected problems I'm not considering.
First and foremost I want to thank everyone for the insights. For the sake of completion, I wanted to post what seems to be the best and less obnoxious way to obtain what I wanted in the first place. While my hack works, it should never be used, and converting the literal into a constructor gives the same result with no hacks:
function App() {
this.model: Backbone.Model.Extend({});
this.view: Backbone.View.Extend({});
this.obj = new this.view();
}
var app = new App();
This version keeps itself dry, and has two added benefits: instantiation, and "private" members. It could easily be rewritten as such:
function App() {
var model: Backbone.Model.Extend({});
var view: Backbone.View.Extend({});
this.obj = new view();
}
var app = new App();
This would keep both model and view out of reach, while having easily accessible app.obj. Of course, if instantiation is an unwanted effect, nothing beats var App = {}.
Also to be noted that since my example was featuring Backbone, the constructor way is probably suggested when extensibility is welcome, and prototypes for the class can be added via _.extend(). This is the the way Backbone does it:
function App() {
// […]
}
_.extend(App.prototype, {
method1: function() {}
method2: function() {}
});
References: _.extend(), object constructors, literals vs. constructors.

Defining child views without an initialize method

I have a large number of views (more than 50) which all extend from a single abstract base view, and therefore have a similar layout and many other features in common (event handlers, some custom methods and properties, etc).
I am presently using the initialize method of my base view to define the layout, which involves a subview, somewhat like the following:
App.BaseView = Backbone.View.extend({
//...
initialize: function() {
this.subView = new App.SubView();
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
this.subView.$el = this.$('#subview-container');
this.subView.render();
return this;
},
//...
});
I find, however, that for many views which extend my base view I need to override the initialize method, which calls the base class initialize (I also extend my events hash quite often as well). I dislike having to do this, especially with so many views which extend the base class.
In this post from a Backbone Github repository issue Derick Bailey says:
I'm also not a fan of requiring extending classes to call super
methods for something like initialize. This method is so basic and
so fundamental to any object that extends from a Backbone construct.
It should never be implemented by a base type - a type that is built
with the explicit intent of never being instantiated directly, but
always extended from.
So on this model I should be able to have an initialize available for each inheriting view class. This makes perfect sense to me; but how can I then implement the kind of general layout I need for my inheriting views? In the constructor method?
I don't know if what I want might be possible out-of-the-box with something like Marionette or LayoutManager, both of which I've briefly looked at but never used, but I would much prefer doing this in vanilla Backbone at the moment.
Where to implement the initializing of the base class?
The way I like to do it is to initialize base classes in the constructor leaving the initialize function empty. It makes sense as the initialize function is only a convenience offered by Backbone and is really just an extension of the constructor.
In fact, Backbone do this a lot. Most if not all functions and properties that we override often are there only to be easily overriden.
Here's a quick list of such example:
Model: initialize, defaults, idAttribute, validate, urlRoot, parse, etc.
Collection: initialize, url, model, modelId, comparator, parse, etc.
View: initialize, attributes, el, template, render, events, className, id, etc.
These functions are left to the user to implement his own behaviors and to keep that useful pattern in a base class, they should be kept untouched and the base class behavior should be hooked into other functions if possible.
Sometimes, it can get difficult, like if you want to do something before initialize is called in the constructor, but after the element and other properties are set. In this case, overriding _ensureElement (line 1223) could be a possible hook.
_ensureElement: function() {
// hook before the element is set correctly
App.BaseView.__super__._ensureElement.apply(this, arguments);
// hook just before the initialize is called.
}
This was just an example, and there are almost always a way to get what you want in the base class without overriding a function that the child will also override.
Simple base class
If the base view is used in a small component and overriden by few child views, and mostly used by the same programmer, the following base view could be enough. Use Underscore's _.defaults and _.extend to merge the child class properties with the base class.
App.BaseView = Backbone.View.extend({
events: {
// default events
},
constructor: function(opt) {
var proto = App.BaseView.prototype;
// extend child class events with the default if not already defined
this.events = _.defaults({}, this.events, proto.events);
// Base class specifics
this.subView = new App.SubView();
// then Backbone's default behavior, which includes calling initialize.
Backbone.View.apply(this, arguments);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
// don't set `$el` directly, use `setElement`
this.subView
.setElement(this.$('#subview-container'))
.render();
// make it easy for child view to add their custom rendering.
this.onRender();
return this;
},
onRender: _.noop,
});
Don't set $el directly, use setElement instead.
Then a simple child view:
var ChildView = App.BaseView.extend({
events: {
// additional events
},
initialize: function(options) {
// specific initialization
},
onRender: function() {
// additional rendering
}
});
Advanced base class
If you're facing one of the following situation:
overriding render is problematic, don't like onRender
the events property (or any other property) is a function in the child or the parent or both
the programmer using the base class don't know about its specifics
Then it's possible to wrap the child properties implementation into new functions and Underscore's _.wrap function does just that.
App.BaseView = Backbone.View.extend({
// works with object literal or function returning an object.
events: function() {
return { /* base events */ };
},
// wrapping function
_events: function(events, parent) {
var parentEvents = App.BaseView.prototype.events;
if (_.isFunction(parentEvents)) parentEvents = parentEvents.call(this);
if (parent) return parentEvents; // useful if you want the parent events only
if (_.isFunction(events)) events = events.call(this);
return _.extend({}, parentEvents, events);
},
constructor: function(opt) {
var proto = App.BaseView.prototype;
// wrap the child properties into the parent, so they are always available.
this.events = _.wrap(this.events, this._events);
this.render = _.wrap(this.render, proto.render);
// Base class specifics
this.subView = new App.SubView();
// then Backbone's default behavior, which includes calling initialize.
Backbone.View.apply(this, arguments);
},
/**
* render now serves as both a wrapping function and the base render
*/
render: function(childRender) {
// base class implementation
// ....
// then call the child render
if (childRender) childRender.call(this);
return this
},
});
So the child looks completely normal while keeping the base class behaviors.
var ChildView = App.BaseView.extend({
events: function() {
return {
// additional events
};
},
initialize: function(options) {
// specific initialization
},
render: function() {
// additional rendering
}
});
Potential problems
This could become a problem if you want to override the base class behavior completely, you would need to cancel some of the base class behavior manually in the child class, and it could prove to be confusing.
Say you have a special child used once that need to override the render completely:
var SpecialChildView = App.BaseView.extend({
initialize: function(options) {
// Cancel the base class wrapping by putting
// the this class's prototype render back.
this.render = SpecialChildView.prototype.render;
// specific initialization
},
render: function() {
// new rendering
}
});
So it's not black and white and one should evaluate what is needed and what is going to be in the way and choose the right overriding technique.

View Model inheritance when using Durandal

I am building an application using Durandal and I have the need to share some functionality across view models.
I have 5 screens to build and they are all virtually the same screen except that in the activate function they will call to a different api end points but otherwise the view and view models will be identical.
Is there a pattern that I should be following to structure this correctly to promote code reuse?
If the views and the view models are identical except for calling different api actions, what about just taking in a parameter as part of the route? Then in the activate function, you can switch on the parameter. The route values can be designated so that your url is relevant, like [http://site/page/subtype], where subtype is the parameter (instead of using numeric values)
Regarding inheritance, depending on the features you need, there's so many ways to do JavaScript inheritance it can be a little confusing. There are some full-featured inheritance models provided by libraries such as base2 and Prototype. John Resig also has an inheritance model that I've used successfully.
In general, I prefer to stick to simpler solutions when it comes to JS inheritance. If you need a pretty much the full set of inheritance features, those libraries are good to consider. If you only really care about accessing a set of properties and functions from a base class, you might be able to get by with just defining the view model as a function, and replacing the function's prototype with the desired base class. Refer to Mozilla's Developer Docs for good info on inheritance.
Here's a sample:
//viewModelBase
define(function (require) {
"use strict";
function _ctor() {
var baseProperty = "Hello from base";
function baseFunction() {
console.log("Hello from base function");
}
//exports
this.baseProperty = baseProperty;
this.baseFunction = baseFunction;
};
//return an instance of the view model (singleton)
return new _ctor();
});
//view model that inherits from viewModelBase
define(function (require) {
"use strict";
function _ctor() {
var property1 = "my property value";
function activate() {
//add start up logic here, and return true, false, or a promise()
return true;
}
//exports
this.activate = activate;
this.property1 = property1;
};
//set the "base"
var _base = require("viewModelBase");
_ctor.prototype = _base;
_ctor.prototype.constructor = _ctor;
//return an instance of the view model (singleton)
return new _ctor();
});
Keep in mind this example all results in what effectively is a singleton (i.e. you'll only get the same instance, no matter how many times you require() it)
If you want a transient (non-singleton) just return _ctor. Then you'll need to instantiate a new instance after you require() it.
One more note, in general, functions should be defined on the prototype, not within the constructor function itself. See this link for more information on why. Because this example results in only a single instance, it's a moot point, so the functions are inside the constructor for improved readability and also the ability to access the private vars and functions.

Using inheritance in meteor.js

I've been hoping to use inheritance in Meteor, but I couldn't find anything about it in the documentation or on Stack Overflow.
Is it possible to have templates inheriting properties and methods from another abstract template, or class?
I think the short answer is no, but here's a longer answer:
One thing I've done to share functionality among templates is to define an object of helpers, and then assign it to multiple templates, like so:
var helpers = {
displayName: function() {
return Meteor.user().profile.name;
},
};
Template.header.helpers(helpers);
Template.content.helpers(helpers);
var events = {
'click #me': function(event, template) {
// handle event
},
'click #you': function(event, template) {
// handle event
},
};
Template.header.events(events);
Template.content.events(events);
It's not inheritance, exactly, but it does enable you to share functionality between templates.
If you want all templates to have access to a helper, you can define a global helper like so (see https://github.com/meteor/meteor/wiki/Handlebars):
Handlebars.registerHelper('displayName',function(){return Meteor.user().profile.name;});
I've answered this question here. While the solution doesn't use inheritance, it allow you to share events and helpers across templates with ease.
In a nutshell, I define an extendTemplate function which takes in a template and an object with helpers and events as arguments:
extendTemplate = (template, mixin) ->
helpers = ({name, method} for name, method of mixin when name isnt "events")
template[obj.name] = obj.method for obj in helpers
if mixin.events?
template.events?.call(template, mixin.events)
template
For more details and an example see my other answer.
Recently, I needed the same functionality in my app so I've decided to create my own package that will do that job out of the box. Although it's still work in progress, you can give it a go.
Basically, the entire method is as follows:
// Defines new method /extend
Template.prototype.copyAs = function (newTemplateName) {
var self = this;
// Creating new mirror template
// Copying old template render method to keep its template
var newTemplate = Template.__define__(newTemplateName, self.__render);
newTemplate.__initView = self.__initView;
// Copying helpers
for (var h in self) {
if (self.hasOwnProperty(h) && (h.slice(0, 2) !== "__")) {
newTemplate[h] = self[h];
}
}
// Copying events
newTemplate.__eventMaps = self.__eventMaps;
// Assignment
Template[newTemplateName] = newTemplate;
};
In your new template (new_template.js) in which you want to extend your abstract one, write following:
// this copies your abstract template to your new one
Template.<your_abstract_template_name>.copyAs('<your_new_template_name>');
Now, you can simply either overwrite your helpers or events (in my case it's photos helper), by doing following:
Template.<your_new_template_name>.photos = function () {
return [];
};
Your will refer to overwritten helper methods and to abstract ones that are not overwritten.
Note that HTML file for new template is not necessary as we refer to abstract one all the time.
Source code is available on Github here!

Dependency Injection with RequireJS

How much can I stretch RequireJS to provide dependency injection for my app? As an example, let's say I have a model that I want to be a singleton. Not a singleton in a self-enforcing getInstance()-type singleton, but a context-enforced singleton (one instance per "context"). I'd like to do something like...
require(['mymodel'], function(mymodel) {
...
}
And have mymodel be an instance of the MyModel class. If I were to do this in multiple modules, I would want mymodel to be the same, shared instance.
I have successfully made this work by making the mymodel module like this:
define(function() {
var MyModel = function() {
this.value = 10;
}
return new MyModel();
});
Is this type of usage expected and common or am I abusing RequireJS? Is there a more appropriate way I can perform dependency injection with RequireJS? Thanks for your help. Still trying to grasp this.
This is not actually dependency injection, but instead service location: your other modules request a "class" by a string "key," and get back an instance of it that the "service locator" (in this case RequireJS) has been wired to provide for them.
Dependency injection would involve returning the MyModel constructor, i.e. return MyModel, then in a central composition root injecting an instance of MyModel into other instances. I've put together a sample of how this works here: https://gist.github.com/1274607 (also quoted below)
This way the composition root determines whether to hand out a single instance of MyModel (i.e. make it singleton scoped) or new ones for each class that requires it (instance scoped), or something in between. That logic belongs neither in the definition of MyModel, nor in the classes that ask for an instance of it.
(Side note: although I haven't used it, wire.js is a full-fledged dependency injection container for JavaScript that looks pretty cool.)
You are not necessarily abusing RequireJS by using it as you do, although what you are doing seems a bit roundabout, i.e. declaring a class than returning a new instance of it. Why not just do the following?
define(function () {
var value = 10;
return {
doStuff: function () {
alert(value);
}
};
});
The analogy you might be missing is that modules are equivalent to "namespaces" in most other languages, albeit namespaces you can attach functions and values to. (So more like Python than Java or C#.) They are not equivalent to classes, although as you have shown you can make a module's exports equal to those of a given class instance.
So you can create singletons by attaching functions and values directly to the module, but this is kind of like creating a singleton by using a static class: it is highly inflexible and generally not best practice. However, most people do treat their modules as "static classes," because properly architecting a system for dependency injection requires a lot of thought from the outset that is not really the norm in JavaScript.
Here's https://gist.github.com/1274607 inline:
// EntryPoint.js
define(function () {
return function EntryPoint(model1, model2) {
// stuff
};
});
// Model1.js
define(function () {
return function Model1() {
// stuff
};
});
// Model2.js
define(function () {
return function Model2(helper) {
// stuff
};
});
// Helper.js
define(function () {
return function Helper() {
// stuff
};
});
// composition root, probably your main module
define(function (require) {
var EntryPoint = require("./EntryPoint");
var Model1 = require("./Model1");
var Model2 = require("./Model2");
var Helper = require("./Helper");
var entryPoint = new EntryPoint(new Model1(), new Model2(new Helper()));
entryPoint.start();
});
If you're serious about DI / IOC, you might be interested in wire.js: https://github.com/cujojs/wire
We use a combination of service relocation (like Domenic describes, but using curl.js instead of RequireJS) and DI (using wire.js). Service relocation comes in very handy when using mock objects in test harnesses. DI seems the best choice for most other use cases.
Not a singleton in a self-enforcing getInstance()-type singleton, but
a context-enforced singleton (one instance per "context").
I would recommend it only for static objects. It's perfectly fine to have a static object as a module that you load using in the require/define blocks. You then create a class with only static properties and functions. You then have the equivalent of the Math Object that has constants like PI, E, SQRT and functions like round(), random(), max(), min(). Great for creating Utility classes that can be injected at any time.
Instead of this:
define(function() {
var MyModel = function() {
this.value = 10;
}
return new MyModel();
});
Which creates an instance, use the pattern for a static object (one where values are always the same as the Object never gets to be instantiated):
define(function() {
return {
value: 10
};
});
or
define(function() {
var CONSTANT = 10;
return {
value: CONSTANT
};
});
If you want to pass an instance (the result of using a Module that have return new MyModel();), then, within an initialize function, pass a variable that capture the current state / context or pass on the Object that contains information on state / context that your modules needs to know about.

Categories