GitHub - shine-on/backbone-bootstrap-forms: Form framework for BackboneJS with nested forms, editable lists and validation
A form framework for Backbone.JS applications.
Requires BackboneJS and jQuery.
<link href="backbone-forms/backbone-forms.css" rel="stylesheet" type="text/css"/>
<script src="backbone-forms/src/backbone-forms.js"></script>
Optionally, you can include the extra editors, for example those that require jQuery UI:
<script src="backbone-forms/src/jquery-ui-editors.js"></script>
If you use BackboneJS with node.js, you can just require('backbone-forms'); in your index file.
Define a 'schema' attribute on your Backbone models. The schema keys should match the attributes that get set on the model. Note that type defaults to Text.
var User = Backbone.Model.extend({
schema: {
email: { dataType: 'email', validators: ['required', validateEmail] },
start: { type: 'DateTime' },
contact: { type: 'Object', subSchema: {
name: {},
phone: {}
}}
address: { type: 'NestedModel', model: Address },
notes: { type: 'List' }
}
});
var formView = Backbone.View.extend({
render: function() {
var form = new Backbone.Form({
model: users.get(userId)
}).render();
$(this.el).append(form.el);
return this;
}
});
Once the user is done with the form, call commit() to apply the updated values to the model. If there are validation errors they will be returned:
var errors = form.commit();
model.bind('change:name', function(model, name) {
form.fields.name.setValue(name);
});
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({
data: { id: 123, name: 'Rod Kimble', password: 'cool beans' }, //Data to populate the form with
schema: {
id: { type: 'Number' },
name: {},
password: { type: 'Password' }
}
}).render();
var data = form.getValue(); //Returns object with new form values
For each field definition in the schema you can use the following optional attributes:
Creates and populates a <select> element.
options
- Options to populate the <select>
- Can be either:
- String of HTML <option>`s
- Array of strings/numbers
- Array of objects in the form
{ val: 123, label: 'Text' } - A Backbone collection
- A function that calls back with one of the above
Examples:
var schema = {
country: { 'Select', options: new CountryCollection() }
};
var schema = {
users: { 'Select', options: function(callback) {
users = db.getUsers();
callback(users);
}}
}
Backbone collection notes
If using a Backbone collection as the option attribute, models in the collection must implement a toString() method. This populates the label of the <option>. The ID of the model populates the value attribute.
If there are no models in the collection, it will be fetch()ed.
##Radio
Creates and populates a list of radio inputs. Behaves the same way and has the same options as a Select.
##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.
##Object
The Object editor creates an embedded child form representing a Javascript object.
subSchema
- A schema object which defines the field schema for each attribute in the object
Examples:
var schema = {
address: { type: 'Object', subSchema: {
street: {},
zip: { type: 'Number' },
country: { 'Select', options: countries }
}}
};
Creates a sortable and editable list of items, which can be any of the above schema types, e.g. Object, Number, Text etc. Currently requires jQuery UI for creating dialogs etc.
listType
- Defines the editor that will be used for each item in the list.
- Similar in use to the main 'type' schema attribute.
- Defaults to 'Text'
itemToString
- Optional, but recommended when using listType 'Object'
- A function that returns a string representing how the object should be displayed in a list item.
- When listType is 'NestedModel', the model's
toString()method will be used, unless a specificitemToString()function is defined on the schema.
sortable
- Optional. Set to false to disable drag and drop sorting
confirmDelete
- Optional. Whether to prompt the user before removing an item. Defaults to false.
confirmDeleteMsg
- Optional. Message to display to the user before deleting an item.
Examples:
var schema = {
users: { type: 'List', listType: 'Object', itemToString: function(user) {
return user.firstName + ' ' + user.lastName;
}
}
};
Events
The following events are fired when the user actions an item:
addItemeditItemremoveItem
Each event callback receives the relevant item value as an object, and a 'next' callback. To cancel the event and prevent the default action, do not run the callback.
This allows you to run asynchronous code, for example to check with the database that a username is available before adding a someone to the list:
var form = new Backbone.Form({ model: this.model }),
list = form.fields.list.editor;
//Only add the item if the username is available
list.bind('addItem', function(item, next) {
database.getUser(item.username, function(user) {
if (user) {
//Item will not be added to the list because we don't call next();
alert('The username is already taken');
}
else {
//Username available; add the item to the list:
next();
}
});
});
Creates a jQuery UI datepicker and time select field.
minsInterval
- Optional. Controls the numbers in the minutes dropdown. Defaults to 15, so it is populated with 0, 15, 30, and 45 minutes;
#Form options
model
The model to tie the form to. Calling form.commit() will update the model with new values.
data
If not using the model option, pass a native object through the data option. Then use form.getValue() to get the new values.
schema
The schema to use to create the form. Pass it in if you don't want to store the schema on the model, or to override the model schema.
fieldsets
An array of fieldsets descriptions. A fieldset is either a list of field names, or an object with legend and fields attributes. The legend will be inserted at the top of the fieldset inside a <legend> tag; the list of fields will be treated as fields is below.
fieldsets takes priority over fields.
fields
An array of field names (keys). Only the fields defined here will be added to the form. You can also use this to re-order the fields.
idPrefix
A string that will be prefixed to the form DOM element IDs. Useful if you will have multiple forms on the same page. E.g. idPrefix: 'user-' will result in IDs like 'user-name', 'user-email', etc.
#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();
#Custom Editors
Custom editors can be written. They must extend from Backbone.Form.editors.Base.
var CustomEditor = Backbone.Form.editors.Base.extend({
tagName: 'input',
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(this.value);
}
});
Notes:
- The editor must implement a getValue() and setValue().
- The original value is available through this.value.
- The field schema can be accessed via this.schema. This allows you to pass in custom parameters.
#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.
#Validation
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.
#Using nested attributes/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: {},
author: { type: 'Object', subSchema: {
id: { type: 'Number' },
name: { type: 'Object', subSchema: {
first: {},
last: {}
}}
}}
}
});
var form = new Backbone.Form({
model: new Model,
fields: ['title', 'author.id', 'author.name.last']
}).render();