malroc / backbone-component

Components and helpers for Backbone
MIT License
41 stars 4 forks source link

backbone-component

Backbone.Component plugin adds component concept to Backbone. If you are familiar with web components draft or components in Ember, the idea of Backbone.Component is very similar.

You can think of component as of isolated view-like object controlling some typical UI elements.

Backbone.Component is an open-source part of http://sourcetalk.net

The problem

Say you have a date picker element controlled by some jQuery plugin. It is used in some of your views, so every time you render a view you need to initialize your date pickers, and probably also deinitialize on exit. Thus you need to write a lot of duplicate code.

In the general case you can't solve this issue with inheritance, since you may need to use the same plugin in the views having different parents, while you don't want to execute that code in every single view.

Creating a subview for that element is a bit better, but you still have to manually handle all these subviews.

That's what Backbone.Component is for.

Installation

Backbone.Component is available in Bower repository as backbone_component. If you use Bower, execute the following in your terminal window:

bower install backbone_component

Otherwise, just copy the file you need (minified or development version) to your javascript directory.

Usage

You need to include backbone-component.js in your project after backbone.js.

Dependencies

The only external dependencies of Backbone.Component are Backbone itself and Underscore (which is also required by Backbone). jQuery/Zepto is not required, but as in case of Backbone gives you additional posiibilities (see below).

Also, Backbone.Comonent depends on MutationObserver. If you want to support browsers that don't implement it (e.g. IE<11) you need to use some polyfill.

Initialization

First, you need to initialize Backbone.Component (the values below are the defaults):

Backbone.Component.initialize(
  {
    "namespace"     : Backbone.Components ,
    "baseViewClass" : Backbone.View       ,
    "transformHTML" : null
  }
);
  1. namespace - namespace containing your components. If for any reason you don't use namespaces at all and store all your classes in global namespace, you can set "namespace" : window, and that will work, but you better don't do this. This option accepts array of namespaces in case you have several namespaces for your components.

  2. baseViewClass - base class for your views. Usually Backbone.View, but you may use your own parent class. Please keep in mind that the prototype of the view passed here will be extended, so if you don't want to add method to your base class you can create a separate base class to use with components (but in this case they won't be available anywhere else).

  3. transformHTML - wrapper for the generated HTML, a function accepting and returning a string. This may be helpful if your template engine escapes HTML by default but allows an option to unescape it (safe method in Skim), or you want to add your custom HTML to that generated by Backbone.Component. If set to null (by default), doesn't perform any transformation.

Creating your component

You need to extend Backbone.Component and override 3 methods:

Backbone.Components.YourComponent = Backbone.Component.extend(
  {
    template:   function( your, own, params ) { } ,
    initialize: function( ) { }                   ,
    remove:     function( ) { }
  }
);
  1. template - generates base HTML of your component. Most probably it will contain some template processing code. It must return a string containing the generated HTML. In this method, you can define any arguments you need. It is supposed that the last argument is a hash containing additional options, but you can safely ignore this convention. Currently 2 options have the impact on the generated HTML: options.wrapper.htmlId and options.wrapper.htmlClass, they will appropriately set id and class of the wrapper HTML element. Also you are free to add your own options for your components.

  2. initialize - any code you need to initialize your component. This method is called when the component appears in DOM. It is executed for every single component instance in your view. In case of our hypothetical date picker plugin, it can look like this: this.$( "input" ).datepicker( ). Accepts no arguments, any returned value is ignored.

  3. remove - any code you need to safely remove your component. This method is called when the component disappears from DOM. It is executed for every single component instance in your view. In many cases, it isn't really necessary, and you can skip this method. Accepts no arguments, any returned value is ignored.

Rendering

Once you defined your component class, you can render your component in templates by calling insertYourComponent method. Assuming you use underscore-like templates, and inside the template this points to your Backbone.View instance, you render your components like this:

<%= this.insertYourComponent( your, own, params ) %>

All the arguments here are passed to the component's template method unchanged. insert<ComponentName> methods are added to your base view class during Backbone.Component initialization.

Observing

In some cases you may preffer writing HTML for your components manually instead of generating it in your component class. You can still take advantage of using Backbone.Component's initialize & remove methods and event handling.

Since version 0.2.3, you don't need to do anything for that: just create an HTML element with class either matching your component's className attribute if it is defined, or component-your-component-name (assuming your component class name is YourComponentName) otherwise.

Since version 0.3.1, you can also specify a custom selector (selector attribute) if you don't want to use the default component-your-component-name convention. E.g. to apply tooltip plugin to all elements with title you need to create a component with "selector" : "[title]".

Everytime an element with an appropriate class appears/disappears, component's initialize/remove method executes.

Note: you have to create the content of your element manually in this case. Backbone.Component won't do that for you.

Backbone.View's methods & properties

Though not a descendant of Backbone.View, Backbone.Component inherits some of its methods and properties. Here they are: el, $el, events, tagName, className, id, $( ), setElement( ), _setElement( ), delegate( )/undelegate( ), delegateEvents( )/undelegateEvents( ). All these methods and properties work in exactly the same way as they do in Backbone.View (except that tagName is a span by default).

Also, Backbone.Component uses Backbone.Event mixin, so you are free to use events in your components just like in any other Backbone object.

Note: some of these methods and variables are available only if you use jQuery or similar lib. See Backbone documentation for more details.

Helpers

If you are familiar with Ruby on Rails or similar MVC framework, you probably already know what helpers are. In Backbone.Component, helpers are very similar to components except that they don't have initialize/remove logic.

To create a helper, you need to extend Backbone.Helper and override template method. In the simplest case, your helper may look like this:

Backbone.Helpers.YourHelper = Backbone.Helper.extend(
  {
    template: _.template( "<div>...</div>" );
  }
);

Now you can call it in your templates:

<%= this.insertYourHelper( your, own, params ) %>

Unlike components, helpers do not inherit Backbone.View's methods and variables, and do not use Backbone.Events.

Note: for performance reasons, you should not use components where helpers will do the work. Backbone.Component doesn't observe helpers in the way it observes components, so if all you need is to render a template, please consider using helpers.

Change Log

v 0.3.1 (Oct 10, 2015)

v 0.3.0 (June 20, 2015)

v 0.2.3 (January 2, 2015)

v 0.2.2 (October 29, 2014)

v 0.2.1 (September 27, 2014)

v 0.2.0 (January 9, 2014)

v 0.1.0 (January 4, 2014)