Home

Awesome

backbone-forms

A flexible, customisable form framework for Backbone.js applications.

Example: Quickly generate forms to edit models

var User = Backbone.Model.extend({
    schema: {
        title:      { type: 'Select', options: ['Mr', 'Mrs', 'Ms'] },
        name:       'Text',
        email:      { validators: ['required', 'email'] },
        birthday:   'Date',
        password:   'Password',
        address:    { type: 'NestedModel', model: Address },
        notes:      { type: 'List', itemType: 'Text' }
    }
});

var user = new User();

var form = new Backbone.Form({
    model: user
}).render();

$('body').append(form.el);

Example: Fully customise forms and templates

HTML:

<script id="formTemplate" type="text/html">
    <form>
        <h1>New User</h1>

        <h2>Main Info</h2>
        <div data-fields="title,name,birthday"></div>

        <h2>Account Info</h2>
        <h3>Email</h3>
        <div data-fields="email"></div>

        <h3>Password</h3>
        <p>Must be at least 8 characters long</p>
        <div data-editors="password"></div>
    </form>
</script>

JavaScript:

var UserForm = Backbone.Form.extend({
    template: _.template($('#formTemplate').html()),

    schema: {
        title:      { type: 'Select', options: ['Mr', 'Mrs', 'Ms'] },
        name:       'Text',
        email:      { validators: ['required', 'email'] },
        password:   'Password'
    }
});

var form = new UserForm({
    model: new User()
}).render();

$('body').append(form.el);

Live editable demos

<a name="top"/>

Guide

Table of Contents:

<a name="installation"/>

Installation

Dependencies:

Include backbone-forms.js:

<script src="backbone-forms/distribution/backbone-forms.min.js"></script>

Optionally, you can include the extra editors, for example the List editor:

<script src="backbone-forms/distribution/editors/list.min.js"></script>

To use a custom template pack, e.g. Bootstrap, include the relevant files after backbone-forms.js.

<script src="backbone-forms/distribution/templates/bootstrap.js"></script>
<link href="backbone-forms/distribution/templates/bootstrap.css" rel="stylesheet" />

If you use Backbone with browserify or node.js, you can just require('backbone-forms'); in your index file. If doing this you will need to set Backbone.$, e.g. Backbone.$ = require('jquery').

Note there is also a distribution file for RequireJS / AMD.

Back to top

<a name="usage"/>

Usage

Forms are generated from a schema, which can be defined on the form itself or on a model.

The schema keys should match the attributes that get set on the model. type defaults to Text. When you don't need to specify any options you can use the shorthand by passing the editor name as a string. See schema definition for more information.

var User = Backbone.Model.extend({
    schema: {
        title:      { type: 'Select', options: ['Mr', 'Mrs', 'Ms'] },
        name:       'Text',
        email:      { validators: ['required', 'email'] },
        birthday:   'Date',
        password:   'Password',
        address:    { type: 'NestedModel', model: Address },
        notes:      { type: 'List', itemType: 'Text' }
    }
});

var user = new User();

var form = new Backbone.Form({
    model: user
}).render();

$('body').append(form.el);

Once the user is done with the form, call form.commit() to apply the updated values to the model. If there are validation errors they will be returned. See validation for more information.

var errors = form.commit(); // runs schema validation

or

var errors = form.commit({ validate: true }); // runs schema and model validation

To update a field after the form has been rendered, use form.setValue:

model.on('change:name', function(model, name) {
    form.setValue({ name: name });
});

Usage without models

You can create a form without tying it to a model. For example, to create a form for a simple object of data:

var form = new Backbone.Form({
    //Schema
    schema: {
        id:         'Number',
        name:       'Text',
        password:   'Password'
    },

    //Data to populate the form with
    data: {
      id: 123,
      name: 'Rod Kimble',
      password: 'cool beans'
    }
}).render();

Then instead of form.commit(), do:

var data = form.getValue(); //Returns object with new form values

Initial data

If a form has a model attached to it, the initial values are taken from the model's defaults. Otherwise, you may pass default values using the schema.data.

Back to top

<a name="form"/>

Backbone.Form

Options

Events

Backbone.Form fires the following events:

Back to top

<a name="schema-definition"/>

Schema definition

The schema defined on your model can be the schema object itself, or a function that returns a schema object. This can be useful if you're referencing variables that haven't been initialized yet.

The following default editors are included:

Main attributes

For each field definition in the schema you can use the following optional attributes:

Main events

Every editor fires the following events:

Besides these three, editors can implement custom events, which are described below.

Back to top

<a name="editor-text"/>

Text

Creates a normal text input.

<a name="editor-select"/>

Select

Creates and populates a <select> element.

Methods

Examples

var schema = {
    country: { type: 'Select', options: new CountryCollection() }
};

var schema = {
    users: { type: 'Select', options: function(callback, editor) {
        users = db.getUsers();

        callback(users);
    }}
};

// Option groups (each group's option can be specified differently)
var schema = {
    options: [
        { group: 'Cities', options: ['Paris', 'Beijing']},
        { group: 'Countries', options: new Collection(objects)},
        { group: 'Food', options: '<option>Bread</option>'}
    ]
};
<a name="editor-radio"/>

Radio

Creates and populates a list of radio inputs. Behaves the same way and has the same options as a Select.

When the Radio's is given options as an array of objects, each item's label may be replaced with labelHTML. This content will not be escaped, so that HTML may be used to style the label. If it uses object syntax, this option is not possible.

Examples

var schema = {
    radios: {
        type: "Radio",
        options: [
            { label: "<b>Will be escaped</b>", val: "Text is not bold, but <b> and </b> text is visible"},
            { labelHTML: "<b>Will NOT be escaped</b>", val: "Text is bold, and HTML tags are invisible"}
        ]
    }
};

var schema = {
    radios: {
        type: "Radio",
        options: {
            value1: "<b>Text is not bold, but <b> and </b> text is visible</b>",
            value2: "There is no way to unescape this text"
        }
    }
};
<a name="editor-checkboxes"/>

Checkboxes

Creates and populates a list of checkbox inputs. Behaves the same way and has the same options as a Select. To set defaults for this editor, use an array of values.

Checkboxes options array has the same labelHTML option as Radio.

<a name="editor-object"/>

Object

The Object editor creates an embedded child form representing a JavaScript object.

Attributes

Events

Examples

var schema = {
    address: {
      type: 'Object',
      subSchema: {
        street: {},
        zip: { type: 'Number' },
        country: { type: 'Select', options: countries }
      }
    }
};

addressEditor.on('zip:change', function(addressEditor, zipEditor) {
    console.log('Zip changed to "' + zipEditor.getValue() + '".');
});
<a name="editor-nestedmodel"/>

NestedModel

Used to embed models within models. Similar to the Object editor, but adds validation of the child form (if it is defined on the model), and keeps your schema cleaner.

Attributes

Events

Examples

var schema = {
    address: { type: 'NestedModel', model: Address }
};

addressEditor.on('zip:change', function(addressEditor, zipEditor) {
    console.log('Zip changed to "' + zipEditor.getValue() + '".');
});
<a name="editor-date"/>

Date

Creates <select>s for date, month and year.

Extra options

You can customise the way this editor behaves, throughout your app:

var editors = Backbone.Form.editors;

editors.Date.showMonthNames = false; //Defaults to true
editors.Date.monthNames = ['Jan', 'Feb', ...]; //Defaults to full month names in English
<a name="editor-datetime"/>

DateTime

Creates a Date editor and adds <select>s for time (hours and minutes).

<a name="editor-list"/>

List

Creates a list of items that can be added, removed and edited. Used to manage arrays of data.

This is a special editor which is in a separate file and must be included:

<script src="backbone-forms/distribution/editors/list.min.js" />

Modal Adapter

By default, the List editor uses modal views to render editors for Object or Nested Model item types. To use the default modal adapter, you must include the Backbone.BootstrapModal library on the page:

<script src="backbone-forms/distribution/adapters/backbone.bootstrap-modal.min.js" />

You may also specify your own modal adapter, as long use you follow the interface of the Backbone.BootstrapModal class.

var MyModalAdapter = Backbone.BootstrapModal.extend({
    // ...
});

Form.editors.List.Modal.ModalAdapter = MyModalAdapter;

If you prefer non-modal editors, you may override the default list editors like so:

// Use standard 'Object' editor for list items.
Form.editors.List.Object = Form.editors.Object;

// Use standard 'NestedModel' editor for list items.
Form.editors.List.NestedModel = Form.editors.NestedModel;

See an editable demo of using the Nested Model, List and Modal Adapter together.

Attributes

Events

Examples

function userToName(user) {
    return user.firstName + ' ' + user.lastName;
}

var schema = {
    users: { type: 'List', itemType: 'Object', itemToString: userToName }
};

listEditor.on('add', function(listEditor, itemEditor) {
    console.log('User with first name "' + itemEditor.getValue().firstName + '" added.');
});

listEditor.on('item:focus', function(listEditor, itemEditor) {
    console.log('User "' + userToName(itemEditor.getValue()) + '" has been given focus.');
});

listEditor.on('item:lastName:change', function(listEditor, itemEditor, lastNameEditor) {
    console.log('Last name for user "' + itemEditor.getValue().firstName + '" changed to "' + lastNameEditor.getValue() +'".');
});

Back to top

<a name="validation"/>

Validation

There are 2 levels of validation: schema validators and the regular built-in Backbone model validation. Backbone Forms will run both when form.validate() is called. Calling form.commit() will run schema level validation by default, and can also run model validation if { validate: true } is passed.

Schema validation

Validators can be defined in several ways:

Built-in validators

Built-in Validators Demo

Examples

var schema = {
    //Built-in validator
    name: { validators: ['required'] },

    //Multiple built-in validators
    email: { validators: ['required', 'email'] },

    //Built-in editors with options:
    password: { validators: [
        { type: 'match', field: 'passwordConfirm', message: 'Passwords must match!' }
    ] },

    //Regular expression
    foo: { validators: [/foo/] },

    //Regular expression with flags - if using flags, regexp must be string
    baz: {
        validators: [{
            type: 'regexp',
            regexp: 'baz',
            flags: 'i',
            message: 'Must type \'baz\' - case insensitive'
        }]
    },

    //Custom function
    username: { validators: [
        function checkUsername(value, formValues) {
            var err = {
                type: 'username',
                message: 'Usernames must be at least 3 characters long'
            };

            if (value.length < 3) return err;
        }
    ] }
};

Handling errors

Error messages will be added to the field's help text area, and a customisable bbf-error class will be added to the field element so it can be styled with CSS.

Validation runs when form.commit() or form.validate() are called. If validation fails, an error object is returned with the type (validator that failed) and customisable message:

//Example returned errors from form validation:
{
    name:   { type: 'required', message: 'Required' },              //Error on the name field
    email:  { type: 'email', message: 'Invalid email address' },    //Error on the email field
    _others: ['Custom model.validate() error']                      //Error from model.validate()
}

Customising error messages

After including the Backbone Forms file, you can override the default error messages.

{{mustache}} tags are supported; they will be replaced with the options passed into the validator configuration object. {{value}} is a special tag which is passed the current field value.

Backbone.Form.validators.errMessages.required = 'Please enter a value for this field.';

Backbone.Form.validators.errMessages.match = 'This value must match the value of {{field}}';

Backbone.Form.validators.errMessages.email = '{{value}} is an invalid email address.';

You can also override the error message on a field by field basis by passing the message option in the validator config.

Model validation

If your models have a validate() method the errors will be added to the error object. To make the most of the validation system, the method should return an error object, keyed by the field object. If an unrecognised field is added, or just a string is returned, it will be added to the _others array of errors:

var User = Backbone.Model.extend({
    validate: function(attrs) {
        var errs = {};

        if (usernameTaken(attrs.username)) errs.username = 'The username is taken';

        if (!_.isEmpty(errs)) return errs;
    }
});

Schema validators

Forms provide a validate method, which returns a dictionary of errors, or null. Validation is determined using the validators attribute on the schema (see above).

If you model provides a validate method, then this will be called when you call Form.validate. Forms are also validated when you call commit. See the Backbone documentation for more details on model validation.

Example

//Schema definition:
var schema = {
    name: { validators: ['required'] }
};

var errors = form.commit();

Back to top

<a name="customising-templates"/>

Customising templates

Backbone Forms comes with a few options for rendering HTML. To use another template pack, such as for Bootstrap, just include the .js file from the templates folder, after including backbone-forms.js.

You can change all the default templates by copying the included distribution/templates/bootstrap.js file and adapting that. Placeholders are the data-xxx attributes, e.g. data-fieldsets, data-fields and data-editors.

Alternate field templates

If only certain fields need a different template this can be done by providing the template in the schema:

var altFieldTemplate = _.template('<div class="altField" data-editor></div>');

var form = new Backbone.Form({
  schema: {
    age: { type: 'Number' },        //Uses the default field template
    name: { template: altFieldTemplate }  //Uses the custom template
  }
});

100% custom forms

To customise forms even further you can pass in a template to the form instance or extend the form and specify the template, e.g.:

<script id="formTemplate" type="text/html">
    <form>
        <h1><%= heading1 %></h1>

        <h2>Name</h2>
        <div data-editors="firstName"><!-- firstName editor will be added here --></div>
        <div data-editors="lastName"><!-- lastName editor will be added here --></div>

        <h2>Password</h2>
        <div data-editors="password">
            <div class="notes">Must be at least 7 characters:</div>
            <!-- password editor will be added here -->
        </div>
    </form>
</script>
var form = new Backbone.Form({
    template: _.template($('#formTemplate').html()),
    model: new UserModel(), //defined elsewhere
    templateData: {heading1: 'Edit profile'}
});

Back to top

<a name="more"/>

More

<a name="editors-without-forms"/>

Editors without forms

You can add editors by themselves, without being part of a form. For example:

var select = new Backbone.Form.editors.Select({
    model: user,
    key: 'country',
    options: getCountries()
}).render();

//When done, apply selection to model:
select.commit();
<a name="nested-fields"/>

Using nested fields

If you are using a schema with nested attributes (using the Object type), you may want to include only some of the nested fields in a form. This can be accomplished by using 'path' syntax as in the example below.

However, due to Backbone's lack of support for nested model attributes, getting and setting values will not work out of the box. For this to work as expected you must adapt your model's get() and set() methods to handle the path names, or simply use DeepModel which will handle paths for you automatically.

var Model = Backbone.DeepModel.extend({
    schema: {
        title: 'Text',
        author: { type: 'Object', subSchema: {
            id: 'Number',
            name: { type: 'Object', subSchema: {
                first: 'Text',
                last: 'Text'
            }}
        }}
    }
});

var form = new Backbone.Form({
    model: new Model,
    fields: ['title', 'author.id', 'author.name.last']
}).render();

The following shorthand is also valid:

var Model = Backbone.DeepModel.extend({
    schema: {
        title: 'Text',
        'author.id': 'Number',
        'author.name.first': 'Text'
    }
});

var form = new Backbone.Form({
    model: new Model
});
<a name="custom-editors"/>

Custom editors

Writing a custom editor is simple. They must extend from Backbone.Form.editors.Base.

var CustomEditor = Backbone.Form.editors.Base.extend({

    tagName: 'input',

    events: {
        'change': function() {
            // The 'change' event should be triggered whenever something happens
            // that affects the result of `this.getValue()`.
            this.trigger('change', this);
        },
        'focus': function() {
            // The 'focus' event should be triggered whenever an input within
            // this editor becomes the `document.activeElement`.
            this.trigger('focus', this);
            // This call automatically sets `this.hasFocus` to `true`.
        },
        'blur': function() {
            // The 'blur' event should be triggered whenever an input within
            // this editor stops being the `document.activeElement`.
            this.trigger('blur', this);
            // This call automatically sets `this.hasFocus` to `false`.
        }
    },

    initialize: function(options) {
        // Call parent constructor
        Backbone.Form.editors.Base.prototype.initialize.call(this, options);

        // Custom setup code.
        if (this.schema.customParam) this.doSomething();
    },

    render: function() {
        this.setValue(this.value);

        return this;
    },

    getValue: function() {
        return this.$el.val();
    },

    setValue: function(value) {
        this.$el.val(value);
    },

    focus: function() {
        if (this.hasFocus) return;

        // This method call should result in an input within this editor
        // becoming the `document.activeElement`.
        // This, in turn, should result in this editor's `focus` event
        // being triggered, setting `this.hasFocus` to `true`.
        // See above for more detail.
        this.$el.focus();
    },

    blur: function() {
        if (!this.hasFocus) return;

        this.$el.blur();
    }
});

Notes:

<a name="help"/>

Help & discussion

<a name="changelog"/>

Changelog

master

0.14.1

0.14.0

0.13.0

0.12.0

0.11.0

0.10.0

Required changes when upgrading:

0.9.0