1

I have looked all over and can't really find an elegant solution to my problem...

<div class="viewPerson">
     <strong>Name:</strong> <span data-bind="text: Person.Name"></span>
</div>

<div class="editPerson">
    <strong>Name:</strong> <input type="text" data-bind="value: Person.Name">  </span>
    <input type="submit" value="Save" data-bind="click: Save" />
</div>

I have two data bindings on a page (Knockout 2.2.1) for the same field (Name), one in my "View Div" and one in my "Edit Div". Is there an easy way for me to say do not update the "View Div" until it has been saved back to the database.

Here is an example in jsfiddle http://jsfiddle.net/NoseBagUK/bvw4j/3/

So far the best method I have is to have two copies of the Person object and once the ajax magic has happened I have a function for syncing the two Person objects. like this..http://jsfiddle.net/NoseBagUK/jjK4j/1/

Any help greatly appreciated.

Phil

NoseBagUK
  • 345
  • 4
  • 23
  • If you're going to data-bind two elements to a single value, then preventing a data-bind call would seem to me to be counter-productive. Your second fiddle is likely your best bet. As a side note, it is STRONGLY ADVISED in JavaScript to never use `eval()`. See: http://stackoverflow.com/questions/86513/why-is-using-the-javascript-eval-function-a-bad-idea – Patrick D May 17 '13 at 11:29
  • Yeah I would never use Eval on a public facing website – NoseBagUK May 17 '13 at 12:36

1 Answers1

7

I would actually recommend you update the viewmodel immediately and undo that change if the ajax call fails. It will make your app seem faster - since 90+% of your ajax calls will hopefully succeed, it makes no sense coding for the benefit of 10% of cases.

EDIT: I highly recommend this video. It supports this point brilliantly.

But this is how to do it:

The essence of it all is a intercepting computed for your originalObservable:

ko.computed({
    read: function() {
        return originalObservable();
    },
    write: function( newVal ) {
        // perform ajax call
        $.post( '<url>' , { data: originalObservable() } , function() {
            // in your success callback
            // update your observable
            originalObservable( newVal );
        });
        // note: we get to this point without updating the originalObservable
        //       because this is part of the synchronous code
        //       where as the ajax call will, of course, be asynchronous
    }
});

You can use this is many different ways:

-1- On your viewmodel as a separate property and bind your edit html to this

function Viewmodel() {
    this.original = ko.observable();
    this.editable: ko.computed( /* as above */ );
}

-2- A slight variation of (1), but cleaner, is to put the computed on the observable as a property:

function Viewmodel() {
    this.original = ko.observable();
    this.original.editable: ko.computed( /* as above */ );
}

-3- Create a custom binding which creates this computed for you, leaving your viewmodel even cleaner:

ko.bindingHandlers.myAjaxSave = {
    init: function( element, valueAccessor ) {
        var originalObservable = valueAccessor();
        var editable = ko.computed({
            read: function() {
                return originalObservable();
            },
            write: function( newVal ) {
                // perform ajax call
                $.post( '<url>' , { data: originalObservable() } , function() {
                    // in your success callback
                    // update your observable
                    originalObservable( newVal );
                });
                // note: we get to this point without updating the originalObservable
                //       because this is part of the synchronous code
                //       where as the ajax call will, of course, be asynchronous
            }
        });

        // this applies the binding to the HTML element
        ko.applyBindingToNode( element , {
            value: editable
        });
    }
}

You can now use this in your input HTML element like:

<input data-bind="myAjaxSave: Person.Name" />
Sethi
  • 1,378
  • 8
  • 14
  • 1
    Trying it now... I was always taught never to update the interface until changes are committed to the db, nothing worse than thinking you have done a lot of changes then finding out none of them worked. – NoseBagUK May 17 '13 at 12:37
  • i was once of that mindset but then i watched this: http://oredev.org/2012/sessions/asynchronous-user-interfaces--the-future-of-web-uis I was going to post this link in the answer but I couldn't find it at the time – Sethi May 17 '13 at 12:43