View
Giraffe.View is optimized for simplicity and flexibility. Views can move around the DOM safely and freely with the attachTo
method, which accepts any selector, DOM element, or view, as well as an optional jQuery insertion method like 'prepend'
, 'after'
, or 'html'
. The default is 'append'
.
var parentView = new Giraffe.View();
parentView.attachTo('body', {method: 'prepend'});
$('body').find(parentView.$el).length; // => 1
The attachTo
method automatically sets up parent-child relationships between views via the references children
and parent
to allow nesting with no extra work.
var childView = new Giraffe.View();
childView.attachTo(parentView); // or `parentView.attach(childView);`
childView.parent === parentView; // => true
parentView.children[0] === childView; // => true
Views automatically manage the lifecycle of all children
, and any object with a dispose
method can be added to children
via addChild
. When a view is disposed, it disposes of all of its children
, allowing the disposal of an entire application with a single method call.
parentView.dispose(); // disposes both `parentView` and `childView`
When a view is attached, render
is called if it has not yet been rendered. When a view renders, it first calls detach
on all of its children
, and when a view is detached, the default behavior is to call dispose
on it. To overried this behavior and cache a view even when its parent
renders, you can set the cached view's disposeOnDetach
property to false
.
var parentView = new Giraffe.View();
parentView.attach(new Giraffe.View());
parentView.attach(new Giraffe.View({disposeOnDetach: false}));
parentView.attachTo('body'); // render() is called, disposes of the first view
parentView.children.length; // => 1
Views are not automatically reattached after render
, so you retain control, but their parent-child relationships stay intact unless they're disposed. See Giraffe.View#afterRender
for more.
Giraffe.View gets much of its smarts by way of the data-view-cid
attribute attached to view.$el
. This attribute allows us to find a view's parent when attached to a DOM element and safely detach views when they would otherwise be clobbered.
Currently, Giraffe has only one class that extends Giraffe.View, Giraffe.App, which encapsulates app-wide messaging and routing.
Like all Giraffe objects, Giraffe.View extends each instance with every property in options
.
children
When one view is attached to another, the child view is added to the parent's children
array. When dispose
is called on a view, it disposes of all children
, enabling the teardown of a single view or an entire app with one method call. Any object with a dispose
method can be added to a view's children
via addChild
to take advantage of lifecycle management.
parent
Child views attached via attachTo
have a reference to their parent view.
attachTo
Attaches this view to el
, which can be a selector, DOM element, or view. If el
is inside another view, a parent-child relationship is set up. options.method
is the jQuery method used to attach the view. It defaults to 'append'
and also accepts 'prepend'
, 'after'
, 'before'
, and 'html'
. If the view has not yet been rendered when attached, render
is called. This render
behavior can be overridden via options.forceRender
and options.suppressRender
. See the View Basics example for more. Triggers attaching
and attached
events.
attach
attach
is an inverted way to call attachTo
. Unlike attachTo
, calling this function requires a parent view. It's here only for aesthetics. Takes the same options
as attachTo
in addition to the optional options.el
, which is the first argument passed to attachTo
, defaulting to the parent view.
render
Giraffe implements render
so it can do some helpful things, but you can still call it like you normally would. By default, render
uses a view's template
, which is the DOM selector of an Underscore template, but this is easily configured. See Giraffe.View#template
, Giraffe.View.setTemplateStrategy
, and Giraffe.View#templateStrategy
for more.
beforeRender
This is an empty function hook for you to implement. Less commonly used than afterRender
, but helpful in circumstances where the DOM has state that needs to be preserved across renders. For example, if a view with a dropdown menu is rendering, you may want to save its open state in beforeRender
and reapply it in afterRender
.
afterRender
This is an empty function hook for you to implement. After a view renders, afterRender
is called. Child views are normally created and attached to the DOM here. Views that are cached by setting disposeOnDetach
to false
will be in view.children
in afterRender
, but will not be attached to the parent's $el
.
templateStrategy
Giraffe implements its own render
function which calls templateStrategy
to get the HTML string to put inside view.$el
. Your views can either define a template
, which uses Underscore templates by default and is customizable via Giraffe.View#setTemplateStrategy
, or override templateStrategy
with a function returning a string of HTML from your favorite templating engine. See the Template Strategies example for more.
template
Consumed by the templateStrategy
function created by Giraffe.View#setTemplateStrategy
. By default, template
is the DOM selector of an Underscore template. See the Template Strategies example for more.
// the default `templateStrategy` is 'underscore-template-selector'
view.template = '#my-template-selector';
// or
Giraffe.View.setTemplateStrategy('underscore-template');
view.template = '<div>hello <%= name %></div>';
// or
Giraffe.View.setTemplateStrategy('jst');
view.template = function(data) { return '<div>hello' + data.name + '</div>'};
serialize
Gets the data passed to the template
. Returns the view by default.
detach
Detaches the view from the DOM. If view.disposeOnDetach
is true
, which is the default, dispose
will be called on the view and its children
unless the argument preserve
is true
. When a view renders, it first calls detach(false)
on the views inside its $el
.
detachChildren
Calls detach
on each object in children
, passing preserve
through.
addChild
Adds child
to this view's children
and assigns this view as child.parent
. If child
implements dispose
, it will be called when the view is disposed. If child
implements detach
, it will be called before the view renders.
addChildren
Calls addChild
on the given array of objects.
removeChild
Removes an object from this view's children
. If preserve
is false
, the default, Giraffe will attempt to call dispose
on the child. If preserve
is true, Giraffe will attempt to call detach(true)
on the child.
removeChildren
Calls removeChild
on all children
, passing preserve
through.
setParent
Sets a new parent for a view, first removing any current parent-child relationship. parent
can be falsy to remove the current parent.
isAttached
If el
is null
or undefined
, tests if the view is somewhere on the DOM by calling $document.find(view.$el)
. If el
is a view, tests if el
contains this view. Otherwise, tests if el
is the immediate parent of view.$el
.
ui
ui
is an optional view property that helps DRY up DOM references in views. It provides a convenient way to cache jQuery objects after every render
, and the names given to these objects can be used in Backbone.View#events
. Declaring this.ui = {$button: '#button'}
in a view makes this.$button
always available once render
has been called. Typically the ui
value is a string which is then searched for inside this.$el
, but if it's a function, its return value will be assigned. If it's neither a string nor a function, the value itself is assigned.
Giraffe.View.extend({
ui: {
$someButton: '#some-button-selector'
},
afterRender: {
this.$someButton; // just got cached
},
events: {
'#click $someButton': 'onClickSomeButton' // ui names work here
}
});
dataEvents
Inspired by Backbone.View#events
, dataEvents
binds a space-separated list of events ending with the target object to methods on a view. It is a shorthand way of calling view.listenTo(targetObj, event, cb)
. In this example collection
is used, but any object on the view that implements Backbone.Events is a valid target object. To have a view listen to itself, the keywords 'this'
and '@'
can be used.
Giraffe.View.extend({
dataEvents: {
'add remove change collection': 'render',
'event anotherEvent targetObj': function() {},
'eventOnThisView @': 'methodName'
}
});
As a result of using listenTo
, dataEvents
accepts multiple events per definition, handlers are called in the context of the view, and bindings are cleaned up in dispose
via stopListening
.
There are some unfortunate restrictions to dataEvents
. Objects created after initialize
will not be bound to, and events fired during the constructor
and initialize
will not be heard. We advocate using Backbone.Events#listenTo
directly in these circumstances.
See the Data Events example for more.
invoke
Calls methodName
on the view, or if not found, up the view hierarchy until it either finds the method or fails on a view without a parent
. Used by Giraffe to call the methods defined for the events bound in Giraffe.View.setDocumentEvents
.
appEvents
_dispose
Destroys a view, unbinding its events and freeing its resources. Calls Backbone.View#remove
and calls dispose
on all children
.
beforeDispose
This is an empty function hook for you to implement.
afterDispose
This is an empty function hook for you to implement.
beforeInitialize
This is an empty function hook for you to implement.
afterInitialize
This is an empty function hook for you to implement.
detachByEl
Detaches the top-level views inside el
, which can be a selector, element, or Giraffe.View. Used internally by Giraffe to remove views that would otherwise be clobbered when the option method: 'html'
is used in attachTo
. Uses the data-view-cid
attribute to match DOM nodes to view instances.
getClosestView
Gets the closest parent view of el
, which can be a selector, element, or Giraffe.View. Uses the data-view-cid
attribute to match DOM nodes to view instances.
getByCid
Looks up a view from the cache by cid
, returning undefined if not found.
to$El
Gets a jQuery object from el
, which can be a selector, element, jQuery object, or Giraffe.View, scoped by an optional parent
, which has the same available types as el
. If the third parameter is truthy, el
can be the same element as parent
.
setDocumentEvents
Giraffe provides a convenient high-performance way to declare view method calls in your HTML markup. Using the form data-gf-eventName='methodName'
, when a bound DOM event is triggered, Giraffe looks for the defined method on the element's view. For example, putting data-gf-click='onSubmitForm'
on a button calls the method onSubmitForm
on its view on 'click'
. If the view does not define the method, Giraffe searches up the view hierarchy until it finds it or runs out of views. By default, only the click
and change
events are bound by Giraffe, but setDocumentEvents
allows you to set a custom list of events, first unbinding the existing ones and then setting the ones you give it, if any.
Giraffe.View.setDocumentEvents(['click', 'change']); // default
// or
Giraffe.View.setDocumentEvents(['click', 'change', 'keydown']);
// or
Giraffe.View.setDocumentEvents('click change keydown keyup');
removeDocumentEvents
Equivalent to Giraffe.View.setDocumentEvents(null)
.
setDocumentEventPrefix
Sets the prefix for document events. Defaults to data-gf-
, so to bind to 'click'
events, one would put the data-gf-click
attribute on DOM elements with the name of a view method as the value.
setTemplateStrategy
Giraffe provides common strategies for templating.
The strategy
argument can be a function returning an HTML string or one of the following:
'underscore-template-selector'
view.template
is a string or function returning DOM selector
'underscore-template'
view.template
is a string or function returning underscore template
'jst'
view.template
is an html string or a JST function
See the Template Strategies example for more.
App
Giraffe.App is a special Giraffe.View that provides encapsulation for an entire application. Like all Giraffe views, the app has lifecycle management for all children
, so calling dispose
on an app will call dispose
on all children
that have the method. The first Giraffe.App created on a page is available globally at Giraffe.app
, and by default all Giraffe objects reference this app as this.app
unless they're passed a different app in options.app
. This app reference is used to bind appEvents
, a hash that all Giraffe objects can implement which uses the app as an event aggregator for communication and routing.
var myApp = new Giraffe.App();
window.Giraffe.app; // => `myApp`
myApp.attach(new Giraffe.View({
appEvents: {
'say:hello': function() { console.log('hello world'); }
},
// app: someOtherApp // if you don't want to use `window.Giraffe.app`
}));
myApp.trigger('say:hello'); // => 'hello world'
appEvents
are also used by the Giraffe.Router. See Giraffe.App#routes
for more.
The app also provides synchronous and asynchronous initializers with addInitializer
and start
.
Like all Giraffe objects, Giraffe.App extends each instance with every property in options
.
appEvents
Similar to the events
hash of Backbone.View, appEvents
maps events on this.app
to methods on a Giraffe object. App events can be triggered from routes or by any object in your application. If a Giraffe.App has been created, every Giraffe object has a reference to the global Giraffe.app instance at this.app
, and a specific app instance can be set by passing options.app
to the object's constructor. This instance of this.app
is used to bind appEvents
, and these bindings are automatically cleaned up when an object is disposed.
// in a Giraffe object
this.appEvents = {'some:appEvent': 'someMethod'};
this.app.trigger('some:appEvent', params) // => this.someMethod(params)
routes
If routes
is defined on a Giraffe.App or passed to its constructor as an option, the app will create an instance of Giraffe.Router as this.router
and set the router's triggers
to the app's routes
. Any number of routers can be instantiated manually, but they do require that an instance of Giraffe.App is first created, because they use appEvents
for route handling. See Giraffe.Router#triggers
for more.
var app = new Giraffe.App({routes: {'route': 'appEvent'}});
app.router; // => instance of Giraffe.Router
// or
var MyApp = Giraffe.App.extend({routes: {'route': 'appEvent'}});
var myApp = new MyApp();
myApp.router; // => instance of Giraffe.Router
addInitializer
Queues up the provided function to be run on start
. The functions you provide are called with the same options
object passed to start
. If the provided function has two arguments, the options and a callback, the app's initialization will wait until you call the callback. If the callback is called with a truthy first argument, an error will be logged and initialization will halt. If the app has already started when you call addInitializer
, the function is called immediately.
app.addInitializer(function(options) {
doSyncStuff();
});
app.addInitializer(function(options, cb) {
doAsyncStuff(cb);
});
app.start();
start
Starts the app by executing each initializer in the order it was added, passing options
through the initializer queue. Triggers the appEvents
'app:initializing'
and 'app:initialized'
.
Router
The Giraffe.Router integrates with a Giraffe.App to decouple your router and route handlers and to provide programmatic encapsulation for your routes. Routes trigger appEvents
on the router's instance of Giraffe.App. All Giraffe objects implement the appEvents
hash as a shortcut. Giraffe.Router#cause
triggers an app event and navigates to its route if one exists in Giraffe.Router#triggers
, and you can ask the router if a given app event is currently caused via Giraffe.Router#isCaused
. Additionally, rather than building anchor links and window locations manually, you can build routes from app events and optional parameters with Giraffe.Router#getRoute
.
var myApp = new Giraffe.App;
var myRouter = Giraffe.Router.extend({
triggers: {
'post/:id': 'route:post'
}
});
myRouter.app === myApp; // => true
myRouter.cause('route:post', 42); // goes to `#post/42` and triggers 'route:post' on `myApp`
myRouter.isCaused('route:post', 42); // => true
myRouter.getRoute('route:post', 42); // => '#post/42'
The Giraffe.Router requires that a Giraffe.App has been created on the page so it can trigger events for your objects to listen to. For convenience, if a Giraffe.App is created with a routes
hash, it will automatically instantiate a router and set its triggers
equal to the app's routes
.
var myApp = Giraffe.App.extend({
routes: {'my/route': 'app:event'}
});
myApp.router.triggers; // => {'my/route': 'app:event'}
Like all Giraffe objects, Giraffe.Router extends each instance with every property in options
.
triggers
The Giraffe.Router triggers
hash is similar Backbone.Router#routes
, but instead of route: method
the Giraffe.Router expects route: appEvent
. Backbone.Router#routes
is used internally, which is why Giraffe.Router#triggers
is renamed. The router also has a redirect feature as demonstrated below.
triggers: {
'some/route/:andItsParams': 'some:appEvent', // triggers 'some:appEvent' on this.app
'some/other/route': '-> some/redirect/route' // redirect
}
cause
If this.triggers
has a route that maps to appEvent
, the router navigates to the route, triggering the appEvent
. If no such matching route exists, cause
acts as an alias for this.app.trigger
.
isCaused
Returns true if the current window.location
matches the route that the given app event and optional arguments map to in this router's triggers
.
getRoute
Converts an app event and optional arguments into a url mapped in this.triggers
. Useful to build links to the routes in your app without manually manipulating route strings.
reload
Performs a page refresh. If url
is defined, the router first silently navigates to it before refeshing.
appEvents
_dispose
Removes registered callbacks.
Model
Giraffe.Model and Giraffe.Collection are thin wrappers that add lifecycle management and appEvents
support. To add lifecycle management to an arbitrary object, simply give it a dispose
method and add it to a view via addChild
. To use this functionality in your own objects, see Giraffe.dispose
and Giraffe.bindEventMap
.
Like all Giraffe objects, Giraffe.Model and Giraffe.Collection extend each instance with every property in options
except parse
which is problematic per issue 7.
appEvents
_dispose
Removes event listeners and removes this model from its collection.
Collection
See Giraffe.Model
.
appEvents
_dispose
Removes event listeners and disposes of all models, which removes them from the collection.
configure
Initializes an object with several generic features. All Giraffe objects call this function in their constructors to gain much of their functionality. Uses duck typing to initialize features when dependencies are met.
Features:
- -pulls option defaults from the global
Giraffe.defaultOptions
, the staticobj.constructor.defaultOptions
, and the instance/prototypeobj.defaultOptions
- -extends the object with all options minus
omittedOptions
(omits all iftrue
) - -defaults
obj.dispose
toGiraffe.dispose
- -defaults
obj.app
toGiraffe.app
- -binds
appEvents
ifappEvents
andapp
are defined andobj
extendsBackbone.Events
- -binds
dataEvents
ifdataEvents
is defined andobj
extendsBackbone.Events
- -wraps
initialize
withbeforeInitialize
andafterInitialize
if it exists
defaultOptions
The global defaults extended to every object passed to Giraffe.configure
. Empty by default. Setting omittedOptions
here globally prevents those properties from being copied over, and if its value is true
extension is completely disabled.
function Foo() {
Giraffe.configure(this);
};
Giraffe.defaultOptions = {bar: 'global'};
var foo = new Foo();
foo.bar; // => 'global'
You can also define defaultOptions
on a function constructor. These override the global defaults.
Foo.defaultOptions = {bar: 'constructor'};
foo = new Foo();
foo.bar; // => 'constructor'
The instance/prototype defaults take even higher precedence:
Foo.prototype.defaultOptions = {bar: 'instance/prototype'};
foo = new Foo();
foo.bar; // => 'instance/prototype'
Options passed as arguments always override defaultOptions
.
foo = new Foo({bar: 'option'});
foo.bar; // => 'option'
Be aware that the values of all defaultOptions
are not cloned when copied over.
dispose
Disposes of an object, removing event listeners and freeing resources. An instance method of dispose
is added for all objects passed through Giraffe.configure
, and so you will normally call dispose
directly on your objects.
Calls Backbone.Events#stopListening
and sets obj.app
to null. Also triggers the 'disposing'
and 'disposed'
events and calls the beforeDispose
and afterDispose
hooks on obj
before and after the disposal. Takes optional args
that are passed through to the events and the function calls.
bindAppEvents
Attempts to bind appEvents
for an object. Called by Giraffe.configure
.
bindDataEvents
Binds the dataEvents
hash that allows any instance property of obj
to be bound to easily. Expects the form {'event1 event2 targetObj': 'handler'}. Called by Giraffe.configure
.
bindEvent
Uses Backbone.Events.listenTo
to make contextObj
listen for eventName
on targetObj
with the callback cb
, which can be a function or the string name of a method on contextObj
.
unbindEvent
The stopListening
equivalent of bindEvent
.
bindEventMap
Makes contextObj
listen to targetObj
for the events of eventMap
in the form eventName: method
, where method
is a function or the name of a function on contextObj
.
Giraffe.bindEventMap(this, this.app, this.appEvents);
unbindEventMap
The stopListening
equivalent of bindEventMap
.
wrapFn
Wraps obj[fnName]
with beforeFnName
and afterFnName
invocations. Also calls the optional arguments beforeFn
and afterFn
.