/knockout-editable

Knockout plugin to handle accept / rollback of changes to observables and view models

Primary LanguageHTML

knockout-editable

This is plugin for KnockoutJS which allows to accept or rollback changes on observables or view models. It is extremely easy to use solution suitable in most use cases.

You could find examples of usage on http://romanych.github.com/ko.editables/

Installation

knockout-editable is now live on npm. It depends on knockout.

npm install knockout knockout-editable

How to use

editable extender

Adds following methods to any writeable observable:

  • beginEdit()
  • commit()
  • rollback()
  • oldValue()

Also it adds hasChanges observable property. hasChanges returns boolean value indicating wether the actual value of observable is different to initial value or not. Under initial value we understand value of ubservable when beginEdit() was called.

Example:

var name = ko.observable().extend({editable: true});
var nameLength = ko.dependentObservable(function() {
    return name() ? name().length : 0;
});

name('user');       // name set to 'user'
name.beginEdit();   // begin transaciton
name('me');         // name set to 'me', nameLength was recalculated
name.oldValue(); 	// gives us 'user'
nameLength();       // gives us 2
name.hasChanges();  // gives us `true`
name.commit();      // transaction commited; values are unchanged since last edit; we could start another one
name.hasChanges();  // gives us `false`
name.rollback();    // nothing happens since transation is commited
name.hasChanges();  // gives us `false`
name.beginEdit();   // begin another transaction
name('someone');    // name set to 'someone', nameLength was recalculated
name.rollback();    // rollback transaction; name set to initial value 'me', nameLength recalculated
name();             // gives us 'me'

Note: it is safe to extend same observable multiple times

Check out examples/index.html for this example.

ko.editable plugin

Extends any object with same methods and properties as editable extender. Basically it iterates over all poperties of passed object and extends all writeable observables with editable extender. Also it goes into deep when scaning for observables. So you could use pretty compex viewModels and activate editable with single call

Example:

var user = {
    FirstName: ko.observable('Some'),
    LastName: ko.observable('Person'),
    Address: {
        Country: ko.observable('USA'),
        City: ko.observable('Washington')
    }
};
ko.editable(user);

user.beginEdit();
user.FirstName('MyName');
user.hasChanges();          // returns `true`
user.commit();
user.hasChanges();          // returns `false`
user.Address.Country('Ukraine');
user.hasChanges();          // returns `true`
user.rollback();
user.Address.Country();     // returns 'USA'

Tip: if you are using view models based on classes the best practice is to call ko.editable(this) when all data properties are defined

Check out examples/advanced.html for this example.

Using scopes with extender

From time to time model is pretty complex and part of it declared in different places. Just small example, you have view model with 2 sets of fields:

  • Personal Information

  • Settings

    var ViewModel = function() { var self = this; self.FirstName = ko.observable(); self.LaststName = ko.observable();

      self.ReceiveEmails = ko.observable(false);
      self.ShowMyEmail = ko.observable(false);
    

    };

You want to add check have user changed something in Personal information or in Setting sections. It is very easy to do with scoping:

var ViewModel = function() {
	var self = this;
	self.FirstName = ko.observable().extend({editable: { scope: 'Personal' }});
	self.LastName = ko.observable().extend({editable: { scope: 'Personal' }});
	self.ChangedPersonalInformation = ko.computed(function() {
		return ko.editable.hasChanges('Personal');
	});

	self.ReceiveEmails = ko.observable(false).extend({editable: { scope: 'Settings' }});
	self.ShowMyEmail = ko.observable(false).extend({editable: { scope: 'Settings' }});
	self.ChangedSettings = ko.computed(function() {
		return ko.editable.hasChanges('Settings');
	});
};

The usage is pretty simple:

var viewModel = new ViewModel();
viewModel.FirstName('Josh');
viewModel.LastName('Pill');

ko.editable.beginEdit('Personal'); // Important! You begin to edit scopes, not the view model or observable.
ko.editable.beginEdit('Team');

viewModel.FirstName('Me');
viewModel.ChangedPersonalInformation(); // `true`
viewModel.ChangedSettings();            // `false`

viewModel.ShowMyEmail(true);
viewModel.ChangedSettings();            // `true`

ko.editable.commit('Personal');
ko.editable.rollback('Settings');

viewModel.FirstName();                  // `Me`
viewModel.ShowMyEmail();                // `false`
viewModel.ChangedPersonalInformation(); // `false`
viewModel.ChangedSettings();            // `false`

Check out examples/scoping.html for this example.