ampersand-app
Part of the Ampersand.js toolkit for building clientside applications.
Simple instance store and event channel that allows different modules within your app to communicate without requiring each other directly. The entire module is only ~30 lines of code, you can read the source here to see exactly what it does.
The Singleton pattern
Whenever you require('ampersand-app')
it returns the same instance of a plain 'ol JavaScript Object
.
This is called the Singleton pattern.
This object it returns is nothing special. It's just a plain old JavaScript Object
that has been decorated with ampersand-events methods as well as an extend
and reset
method.
That's it!
Why is this useful?
It's quite common to create an app
global to store collections and models on and then to reference that global whenever you need to look up related model instance from another module within your app. However, this creates many indirect interdependencies within your application which makes it more difficult to test isolated parts of your application.
It's also quite common to need "application-level" events that any number of pieces of your app may need to handle. For example, navigation events, or error events that could be triggered by any number of things within your app but that you want to handle by a single module that shows them as nice error dialogs.
This module provides a pattern to address both those cases without having to rely on globals, or have circular dependency issues within your apps. It also means you don't have to adjust code linting rules to ignore that app
global.
Before ampersand-app
Module "A" (app.js):
var MyModel = require('./models/some-model');
window.app = {
init: function () {
this.myModel = new MyModel();
}
};
window.app.init();
Module "B" (that needs access to app
):
module.exports = View.extend({
someMethod: function () {
app.myModel.doSomething():
}
});
With ampersand-app
you'd do this instead:
Module "A" (app.js):
var app = require('ampersand-app');
var MyModel = require('./models/some-model');
app.extend({
init: function () {
this.myModel = new MyModel();
}
};
app.init();
Module "B" (that needs access to app
):
var app = require('ampersand-app');
module.exports = View.extend({
someMethod: function () {
app.myModel.doSomething():
app.trigger('some custom event');
}
});
Now when we go to write tests for module "B" we can easily mock things that it expects from app
.
So our tests for module B might look like this:
var test = require('tape');
var ModuleB = require('../module-b');
var app = require('ampersand-app');
test('test module B', function (t) {
app.reset();
app.myModel = {
doSomething: function () {}
};
app.on('some custom event', function () {
t.pass('custom event fired');
app.reset();
t.end();
});
var view = new ModuleB();
t.doesNotThrow(function () {
view.someMethod();
}, 'make sure calling some method does not explode');
});
test('next test', function () {
app.reset();
});
Warning: Not for use in re-usable modules
If you're writing a re-usable module for distribution on npm it should not have ampersand-app
as a dependency.
Doing so makes assumptions about how you want it to be used.
Say you want to make an error
event handling module, that requires ampersand-app
listens for error
events from that app
and shows a nice error dialog.
Rather than make all those assumptions about how its going to be used, just make the nice error dialog view and suggest in the readme how someone might use ampersand-app
as an event channel to trigger them.
This allows people who don't use this particular application pattern to still use your npm module and leaves the event names, and application architecture up to the person building the app.
install
npm install ampersand-app
API Reference
event methods
The app
object is an event object so it contains all the methods as described in the ampersand-events docs.
The app
object becomes a handy way to communicate within your app so various modules can notify each other about "app-level" events such as user navigation, etc.
extend app.extend(obj, [*objs])
Convenience method for attaching multiple things to the app at once. This is simply an alias for amp-extend
that pre-fills the app
as the object being extended.
obj
{Object} copy properties from this object onto app
. You can pass as many objects to this as you want as additional arguments.
var app = require('ampersand-app');
var UserCollection = require('./models/user-collection');
var MeModel = require('./models/me');
app.extend({
me: new MeModel(),
users: new UserCollection(),
router: new Router(),
init: function () {
this.router.history.start({pushState: true});
}
});
reset app.reset()
Resets the app singleton to its original state, clearing all listeners, and deleting everything you've added to it, but keeping the same object instance.
This is primarily for simplifying unit testing of modules within your app. Whenever you require('ampersand-app')
you get the same object instance (this is the Singleton pattern). So, having app.reset()
lets you mock app state required for testing a given module.
credits
Created by @HenrikJoreteg.
license
MIT