Heads up! This project was significally improved and renamed into X-editable!
Please use X-editable page to download newest release, create issues and view actual documentation.
All futher updates will be done there.

About

Invokes in-place editing feature to any element of your page

  • based on Bootstrap form and popover
  • supported types: text, textarea, select, date
  • validation on client and server-side
  • customizible popover's size and placement
  • toggle by itself or another element
  • keyboard support (escape/enter)

Download stable (v1.1.4)
release notes


News

25/09/12
Version 1.1.4 released!
Added shown / hidden events and api method submit to simplify creating of new records.

↓ Show all news

Why editable?

  • fast development, no special FORM markup
  • less number of screens in your application
  • easy adding / removing fields
  • unified backend script for data update
  • edit data directly is more clear for users who get used to MS Excel

Server-side

If you created server-side plugin for another framework or language please contact me and I will add your link here.

Example

Click on fields to edit:

Username superuser Simple text field
First name John Required text field, placement: right
Last name Originally empty text field
Sex Select, loaded from json
Action Insert Select, loaded from inline HTML
Group Admin Select, loaded from server
Status Active Select, loaded from server (error)
Date of birth 15.05.1984 Date field, format dd.mm.yyyy
Comments awesome
user!
Textarea
Weight, kg 65 Server-side validation error, width = span1
Note pretty note.. Toggle by another element
All ajax requests here are emulated:

Getting started

Include required js and css files:

jQuery & Bootstrap

<link href="path/to/bootstrap/css/bootstrap.css" rel="stylesheet">
<script src="path/to/jquery/jquery-1.8.2.min.js"></script>
<script src="path/to/bootstrap/js/bootstrap.min.js"></script>

Bootstrap-editable

<link href="path/to/bootstrap-editable/css/bootstrap-editable.css" rel="stylesheet">
<script src="path/to/bootstrap-editable/js/bootstrap-editable.min.js"></script>

Usage and markup

You can activate editable on your page easily with special markup and a few line of javascript. Options can be defined via either data-* attributes or json options.

Below you will find examples of each type and detailed list of all options.

Text

Just create <A> element

<a href="#" id="username">superuser</a>

and apply editable() method

$('#username').editable({
                           type:  'text',
                           pk:    1,
                           name:  'username',
                           url:   'post.php',  
                           title: 'Enter username'
                        });

Alternatively, you can set all the same options via data-* attributes

<a href="#" id="username" data-type="text" data-pk="1" data-name="username" data-url="post.php" data-original-title="Enter username">superuser</a>
$('#username').editable();

Textarea

Very similar to text, just set type to textarea

<a href="#" id="comments">awesome<br>user!</a>
$('#comments').editable({
                           type:  'textarea',
                           pk:    1,
                           name:  'comments',
                           url:   'post.php',  
                           title: 'Enter comments'
                        });

Select

For select element additionally set data-source and data-value

<a href="#" id="group" data-type="select" data-name="group" data-pk="1" data-value="5" data-source="groups.php" data-original-title="Select group">Admin</a>
$('#group').editable();

Date

Date is based on bootstrap-datepicker that is improved fork of eternicode's project. Use format and viewformat options to define how date will be stored and displayed

<a href="#" id="dob" data-format="dd.mm.yyyy">15.05.1984</a>
$('#dob').editable({
                           type:  'date',
                           pk:    1,
                           name:  'dob',
                           url:   'post.php',  
                           title: 'Select Date of birth'
                        });

Options

List of all possible options

Name type default description
type string 'text' input type - text | select | textarea | date
name string null name of field, required. If not set, will be taken from id attribute.
pk string | function null primary key of editable object (e.g. record id in database). Can be css selector or function to get id dynamically, e.g.:
function() {
    return $('#user_id').text();
}
value string element's text initial value. Should be defined for select type to store id of shown text
params object null additional params to submit
url string null url to submit data via ajax. Sending pk, name, value and params
emptytext string 'Empty' text shown on empty elements
inputclass string 'span2' Css class for input - span1 | span2 | span3 | span4
toggle string | jQuery null element that toggles popover by click. By default element itself. Can be CSS selector or HTML, e.g. data-toggle="#pencil"
send string 'auto' strategy for sending data on server - auto | always | never.
'auto' = data will be sent only if pk defined, otherwise will be stored in element
placement string | function 'top' how to position the popover - top | bottom | left | right
enablefocus bool false wether to return focus on element after popover is closed. This allows fully keyboard input, but focused links may look not pretty
autotext string 'auto' can be auto | always | never. Allows to automatically set element's text based on it's value. Usefull for select and date. E.g.:
<a href="#" id="group" data-type="select" data-value="1" data-source="{1: 'User', 2: 'Admin'}" ></a>
Content of element above is empty, but when apply editable it will be set to 'User', because provided value is 1
$('#group').editable();
auto means text will be set only if element is empty and source defined as object (no extra request).
always|never means always(never) try to set element's text
template string depends on type html template used for input inside popover. Allows to do whatever with input:
$('#comments').editable({
template: '<textarea rows="12"></textarea>'
});
validate function | object null function for client-side validation. If returns string - means validation not passed and string showed as error. E.g.:
validate: function(value) {
  if($.trim(value) == '') 
    return 'This field is required';
}
Definition as object is usefull when applying editable to several elements and difference is only in validation. Object structure is name: function, e.g.
validate: {
    username: function(v) {...},
    fullname: function(v) {...}
}
success function null called after success submit. If returns string - means error occured and string is shown as error. Parameters equals to $.ajax() success callback. Can be used to deal with json returned from server. E.g.:
function(data) {
    if(typeof data == 'object' && !data.success) return data.msg;  
}
By default any submit request returning http status = 200 considered successful, and status != 200 considered error.
error function null called on ajax error when submitting data. If returns string - it will be shown as error message. Parameters equals to $.ajax() error callback. Can be used for special error handling. E.g.:
function(xhr) {
    if(xhr.status == 500) return 'Internal server error';  
}
By default response text will be shown as error message.
Tip! If you need one option for all editable fields you can define it in defaults:
$.fn.editable.defaults.success = function(data) { ... }

Type specific options

There are additional options for different input types

Text & Textarea

Name type default description
placeholder string null text shown if input is empty

Select

Name type default description
source string | object null source for data in select element. If string - considered url for ajax to load items. Otherwise should be json. E.g. data-source="groups.php" or
data-source="{'M': 'male', 'F': 'female'}"
prepend string | object false allows to prepend values in select list.
E.g. if set prepend: 'Not defined' then new value {'': 'Not defined'} will be automatically inserted at the begining of select list
loadingError string 'Error when loading options' Message shown when options can't be loaded

Date

Name type default description
format string yyyy-mm-dd format used for submitting date to server. Combination of d, dd, m, mm, yy, yyyy.
viewformat string equals to format format used for displaying date. Combination of d, dd, m, mm, yy, yyyy.
weekStart integer 0 day of the week start. 0 for Sunday - 6 for Saturday
startView string 0, 'month' The view that the datepicker should show when it is opened. Accepts values of 0 or 'month' for month view (the default), 1 or 'year' for the 12-month overview, and 2 or 'decade' for the 10-year overview. Useful for date-of-birth datepickers
datepicker object null Full configuration of datepicker. Some most used options from here are dublicated above for convenience

Events

Event Description
init fired when editable was initialized. To access editable itself use second parameter:
$('#username').on('init', function(e, editable) {
    alert('Initialized with value: ' + editable.value);
});
Should be attached to element before applying $().editable().
update fired when editable has been successfully updated with new value:
$('#username').on('update', function(e, editable) {
    alert('new value: ' + editable.value);
});
render fired each time when text of element is rendered (initially and after each ajax update). Can be used for custom formatting of element's text:
$('#action').on('render', function(e, editable) {
    var colors = {0: "gray", 1: "green", 2: "blue", 3: "red"};
    $(this).css("color", colors[editable.value]); 
});
Property e.isInit defines wether it is initial render or render after update.
shown fired when popover is shown (for select will wait for loading dropdown options).
hidden fired when popover is closed.

Methods

$().editable(options)

Initializes an editable.


.editable('show')

Shows editable popover manually.


.editable('hide')

Hides editable popover manually.


.editable('option', nameOrObject, [value]) New

Set one or several options.

$('#username, #fullname').editable('option', 'pk', 123);
$('#username, #fullname').editable('option', {pk: 123});

Below methods are mainly for creating new records (details in next section)

.editable('validate')

Will call validate method for each editable in array. Returns object with names and validation messages, e.g.:

$('#username, #fullname').editable('validate');
/* possible result:
{
    username: "username is requied",
    fullname: "fullname should be minimum 3 letters length"
}
*/

.editable('getValue')

Returns current values for one or several editable elements. If value is null or undefined it will not be returned.

$('#username, #fullname').editable('validate');
/* possible result:
{
    username: "superuser",
    fullname: "John"
}
*/

.editable('markAsSaved')

Mark editable as saved to server (removes class "editable-changed" and updates lastSavedValue of editable).

$('#username, #fullname').editable('markAsSaved');

.editable('submit', config) New

This method is combination of validate, getValue and markAsSaved working together in more or less standart way. When applied it runs validation for matched editable elements and if no errors - submits data to provided url. It's configuration contains following options:

Option name Description
url url for ajax submit
data additional data to submit
success callback applied on successful creation of new record
error callback applied on any error (including client-side and server-side validation errors)

There are four possible scenarios:

  • successful record creation
    Server response pattern must be: {id: <new-record-id>}. id will be automatically stored into pk value of all editables and they will start working in regular way. Will be called success() callback with response object in param.
  • client-side validation error
    Will be called error() callback with errors object in param.
  • server-side validation error
    Server response pattern must not be like: {id: <something>}. Good solution is response like {errors: <object with error messages>} to have silimar handling of server-side and client-side validation errors. Will be called error() callback with errors object in param.
  • ajax error, http status != 200
    Will be called error() callback with xhr object in param.

Example code:

$('#username, #fullname').editable('submit', {
    url: 'new.php',              
    data: {custom: 'text'},     
    success: function(data) {
        /* actions on success */
    },
    error: function(data) {
        /* actions on validation error (or ajax error) */
        var msg = '';
        if(data.errors) {              //validation error
            $.each(data.errors, function(k, v) { msg += k+": "+v+"<br>"; });  
        } else if(data.responseText) {   //ajax error
            msg = data.responseText; 
        }
    }
});

Creating new record

Creating new record is a bit problem for editable interface because primary key (record id) is unknown and in-place-editing has no related object on backend. In that case editable will store new value inside and will not submit anything to server until you do it manually .This is default behavior when pk option is empty and send='auto'. To submit manually several editable fields there is submit api method. Try example below to see how it works.

Live example - creating new user:

Username
First name
Group
Date of birth
Comments

Editables are initialized without pk option:

$('.myeditable').editable({
    url: 'post.php',
    /* pk not defined for new record */
    validate: {
        username: function(v) {if(v == '') return 'Required field!'}
    } 
});

When you update fields - no data are submited to server, but values are stored and editables marked with css class editable-changed.

When you click on save button, api method submit is called. It runs validation for all fields and if no errors - submits to server. If server response is like {id: <new-record-id>} it automatically stores id into pk value of all editables and they start working in regular way. Additionally success and error callbacks can be passed for customization (e.g. to show message).

$('#save-btn').click(function() {
    $('.myeditable').editable('submit', {   //call submit
        url: 'new.php',                     //url for creating new user
        success: function(data) {
            var msg = 'New user created! Now editables work in regular way.';
            $('#msg').addClass('alert-success').removeClass('alert-error')
                     .html(msg).show();
            $('#save-btn').hide(); 
        },
        error: function(data) {
            var msg = '';
            if(data.errors) {                //validation error
                $.each(data.errors, function(k, v) { msg += k+": "+v+"<br>"; });  
            } else if(data.responseText) {   //ajax error
                msg = data.responseText; 
            }
            $('#msg').removeClass('alert-success').addClass('alert-error')
                     .html(msg).show();
        }
    }); 
});

To have more advanced behavior when creating new record you can easily write your own handler using these api methods: validate, getValue and markAsSaved.
E.g.:

$('#save-btn').click(function() {
    var data,
        $elems = $('.myeditable'),
        errors = $elems.editable('validate'); //run validation for all values
    if($.isEmptyObject(errors)) {
        data = $elems.editable('getValue'); //get all values
        $.ajax({
            type: 'POST',
            url: 'new.php', 
            data: data, 
            dataType: 'json'
        }).success(function(response) {
            if(response && response.id) {
               $elems.editable('option', 'pk', response.id); //store pk
               $elems.editable('markAsSaved');  //on success mark fields as saved
               /* other success actions */
               ...
            } else {
               /* server-side validation error */
               ...
            }
        }).error(function() {
           /* ajax error */
           ...
        });
    } else {
        /* client-side validation error */
        ...
    }
});

If you find a bug or have an idea please feel free to create issue on github.