Simple inline editing with knockoutjs

A little while back, I stumbled upon a neat little trick to write pages with simple inline editing. I’m pretty sure I picked up this technique from Ryan Niemeyer’s excellent blog (although I can’t find a direct link), and I’ve seen it crop up in a few other places too. The approach has come in so handy that I’ve pieced together a little knockout binding handler to make wire up even simpler. Let’s take a look.

Inline Editing

Here’s I mean by when I talk about “Inline Editing”:

inline-edit

Example from http://addyosmani.github.com/todomvc/

The idea is that rather than having to navigate to a separate form for editing on-screen data, the user triggers an inline editor to appear instead (usually by single/double clicking), saving them time and overall improving their experience.

The Approach

The technique is that there are actually two versions of the editable element – one for viewing, and one for editing:

<div class="editor">
    <
div class="view"><a href="#"></a>Click to add</div>
    <
input class="edit" type="text" />
</
div>

Either the “view” element, or the “edit” element will be displayed, and the other will be hidden using CSS { display:none }. We can switch between the two by adding an additional CSS class to the “editor” element depending on the editing state of the view model property:

.edit {
display: none
;
}

.
editing .edit
{
display: block
;
}

.
editing .view
{
display: none
;
}

Knockout Model

We need triggers to toggle the editing state – one on double clicking, and one when we’ve finished editing. Let’s add these triggers to the view model:

var viewModel = {
   
//the item we’re editing
    item: ko.observable
(),
   
    
//track whether we are editing
    editing: ko.observable
(),
   
   
// edit an item
    editItem: function
() {
       
this.editing( true 
);
    },
   
   
// stop editing an item.
    stopEditing: function
() {
       
this.editing( false 
);
    }
};

Knockout Bindings

Since the editing state on the view model is an observable, we can use the CSS binding from knockout to apply the CSS class:

<div class="editor" data-bind="css: { editing: editing }">

We can use the text binding for the “view” element and the value binding for the “edit” input. We then need to trigger “editItem” on double click:

<div class="view" data-bind="
        event: { dblclick: editItem }, 
        text: item() || ‘Double click to edit’"
> 

…and we need to trigger “stopEditing” when we’re done editing. For simplicity, let’s use the blur binding here*:

<input class="edit" type="text" 
       data-bind="value: item, event: { blur: stopEditing }" /> 

And That’s it – Inline editing. If you’ve followed so far, you should now have something that looks like this fiddle.

*We should use other bindings here to detect the enter key being pressed, but lets keep things simple (see here for the appropriate bindings if you’re curious).

A little extra

Ok, so far so good, but what happens if we have a few of these editors in our page for different forms? If we apply this approach as is, we’d have two functions and one extra property for each inline editor. That could get out of hand quickly. Fortunately, we can apply a similar approach to the one I mentioned in my previous post – extending each model property that we want an inline-editor for:

ko.extenders.liveEditor = function (target) {
   
target.editing = ko.observable(false
);

   
target.edit = function 
() {
       
target.editing(true
);
    };

   
target.stopEditing = function 
() {
       
target.editing(false
);
    };
   
return target
;
};

Applying this extender to our model property will add the required observable to track the editing state, plus provide the trigger methods for toggling the state.

Going one step further still, we can actually have this extender applied as part of a binding handler – that way our model doesn’t have to care about the editor extender at all:

ko.bindingHandlers.liveEditor = {
   
init: function (element, valueAccessor
) {
       
var observable = valueAccessor
();
       
observable.extend({ liveEditor: this
});
    },
   
update: function (element, valueAccessor
) {
       
var observable = valueAccessor
();
       
ko.bindingHandlers.css.update(element, function () { return { editing: observable.editing 
}; });
    }
};

Link to full sample on jsFiddle.net

About craigcav

Craig Cavalier works as a Software Developer for Liquid Frameworks in Houston Tx, developing field ticketing and job management solutions for industrial field service companies.

Posted on May 18, 2012, in Knockout and tagged , . Bookmark the permalink. 10 Comments.

  1. how about ajax posting after user edit a field? how can i implement it

    • There are lots of ways you can do this, assuming you wish to post the changes on a per-field basis, one approach I’ve taken in the past is to explicitly subscribe to the change notification for the relevant field:

      http://knockoutjs.com/documentation/observables.html#explicitly_subscribing_to_observables

      See here for a live example alongside the liveEditor:

      http://jsfiddle.net/craigcav/AtY3m/

      • if i want to fire the save method on every field (value) do i have to subscribe it one by one or there is a way to subscribe them without doing it manually i heard that ko.toJS do that but i don’t know how to implement it, im kinda new

      • This is one of those situations where “it depends”.

        I’ve found the number of situations where I need to track/persist changes on a per-field basis to be few. In that situation, manually subscribing is good enough. In situations where there are a larger number of fields, I usually make the persist action explicit; adding a save button to the form, and using ko.toJS to turn the view model into JSON to send to the server.

        That said, those certainly aren’t the only options. If you really require many fields to be tracked, but you wish to avoid the excess code to manually subscribe each property, I could imagine wiring up a single subscription against the view model itself, rather than for each of its properties (assuming you’d be posting to the same url). Whenever a property of the view model changes, you can trigger the save action. To detect changes of a view models properties, wrap ko.toJs(viewmodel) with a computed observable, and subscribe to its changes. For more details on this approach, check out this post: http://www.knockmeout.net/2011/05/creating-smart-dirty-flag-in-knockoutjs.html

      • oh! thanks i was thinking of going with the foreach approach, but the one with dirty flag is kinda better looking thanks for your time. good post!

  2. Hi, i was testing your code if i put the input type=”text” css wont work do you have a fix?

  3. Thanks for interesting article. I’ve improved/simplified it with approach presented here: http://knockoutjs.com/documentation/hasfocus-binding.html (Click-to-edit example). Life demo: http://jsfiddle.net/EaTA5/1/

  4. Hope this post will help. I already know Knockout js but you just did great. Awesome Job!

  1. Pingback: Knockout Inline Edit Binding - feed99

  2. Pingback: Knockout Inline Edit Binding

Leave a reply to Lukasz Cancel reply