0

How to call knockout models Update(fieldName, newValue) (that calls some api via ajax and update fieldName = newValue) when knockout model field value was changed via inline-edit?

I am using the following inline editing found at Knockout Inline Edit Binding

ko.bindingHandlers.hidden = {
    update: function(element, valueAccessor) {
        ko.bindingHandlers.visible.update(element, function() { return !ko.utils.unwrapObservable(valueAccessor()); });
    }        
};

ko.bindingHandlers.clickToEdit = {
    init: function(element, valueAccessor) {
        var observable = valueAccessor(),
            link = document.createElement("a"),
            input = document.createElement("input");

        element.appendChild(link);
        element.appendChild(input);

        observable.editing = ko.observable(false);

        ko.applyBindingsToNode(link, {
            text: observable,
            hidden: observable.editing,
            click: observable.editing.bind(null, true)
        });

        ko.applyBindingsToNode(input, {
            value: observable,
            visible: observable.editing,
            hasfocus: observable.editing
        });
    }
};

clickToEdit custom binding allows element to become input box when editing.

Now, I need to implement auto saving. This would mean to call Update(fieldName, newValue) for value that has been just edited (when focus lost or similar).

I am new to knockout, so do not understand the logic of how to implement this.

EDIT: I was managed to do somethign, but I know that my solution is bad.

I have modified bindingHandlers (found here: http://jsfiddle.net/rniemeyer/8D5aj/) and added focusout event that calls data save.

  1. Unfourtunately this is called for every field when page is loaded also and I do not need this.
  2. And also I do not know how to get observable fieldname and value within this event
  3. And last I am not able to save this only when field was really changed (this is not so important for now)

Script

ko.bindingHandlers.clickToEdit = {
    init: function (element, valueAccessor) {
        var observable = valueAccessor(),
            link = document.createElement("a"),
            input = document.createElement("input");

        element.appendChild(link);
        element.appendChild(input);

        observable.editing = ko.observable(false);

        ko.applyBindingsToNode(link, {
            text: observable,
            hidden: observable.editing,
            click: observable.editing.bind(null, true)
        });

        ko.applyBindingsToNode(input, {
            value: observable,
            visible: observable.editing,
            hasfocus: observable.editing,
            event: {
                keyup: function (data, event) {
                    //if user hits enter, set editing to false, which makes field lose focus
                    if (event.keyCode === 13) {
                        observable.editing(false);
                        return false;
                    }
                        //if user hits escape, push the current observable value back to the field, then set editing to false
                    else if (event.keyCode === 27) {
                        observable.valueHasMutated();
                        observable.editing(false);
                        return false;
                    }

                },
                focusout: function (data, event) {

                    // this is for test as I do not know how to get fieldname and value
                    var d = {
                        Field: "CompanyName",
                        Value: "Value"
                    };
                    var json = JSON.stringify(d)


                    $.ajax({
                        url: "/api/MyObject/PutValue/5",
                        contentType: "text/json",
                        dataType: "json",
                        type: "PUT",
                        data: json,
                        success: function(data) {
                            //
                        }
                    });
                }
            }
        });
    }

    //update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {

    //}

};
Community
  • 1
  • 1
renathy
  • 5,125
  • 20
  • 85
  • 149
  • do you have a sample fiddle to show with values? – naveen Nov 23 '13 at 12:58
  • this is http://jsfiddle.net/rniemeyer/8D5aj/ for inline editing without saving (sending to database that I need to implement), I believe my solution is bad and uses incorrect approach, so I have not creates fiddle for it – renathy Nov 23 '13 at 15:27

2 Answers2

2

If you are comfortable using more than one binding in your solution, why don't you try the simple one documented as Example 2: Click-to-edit in the knockoutjs documentation page?

Markup

<p>
    Name: <b data-bind="visible: !editing(), 
                        text: name, 
                        click: edit">&nbsp;</b>    
    <input data-bind="visible: editing, 
                      value: name, 
                      hasFocus: editing, 
                      event: { blur: myblurFunction }" />
</p>
<p><em>Click the name to edit it; click elsewhere to apply changes.</em></p>

Script

function PersonViewModel(name) {
    var self = this;
    self.name = ko.observable(name);
    self.editing = ko.observable(false);
    self.edit = function () {
        this.editing(true);
    }
    this.myblurFunction = function() {
        var newName = self.name();
        alert("Houston! We have lost focus on " + newName); 
        // ajax call here.   
    }
}

ko.applyBindings(new PersonViewModel("Bert Bertington"));

Fiddle: http://jsfiddle.net/codovations/Q66bQ/

naveen
  • 53,448
  • 46
  • 161
  • 251
  • I do not see where data are sent to DB? As I need data to send to DB? – renathy Nov 23 '13 at 15:25
  • And basically the custom binding I use for inline editing uses the same idea you have shown. I do not understand how to make call to db via ajax here. – renathy Nov 23 '13 at 15:29
  • You can make the call on the blur event. I think your problem is, you don't know how to get the field name and updated value to be passed in ajax. Is that so? Could you elaborate? – naveen Nov 23 '13 at 15:39
  • I like customBinding approach more then your posted approach. Can I my approach to save? I basically do not know if this is good approach and also how to get fieldname + updated value... – renathy Nov 23 '13 at 15:57
  • Another thing that your approach fits only one field eidting, what if I have 10 fields and each need inline edit? – renathy Nov 26 '13 at 08:06
  • @renathy: you should really stop concentrating on my answer. RP Nieyemer answer is the way to go. – naveen Nov 26 '13 at 13:06
2

I think that the best idea for saving to the database is to subscribe to your observable in your view model and trigger the AJAX call there.

So, in your view model it would look like:

myObservable.subscribe(function(newValue) {
    //call your update here with the field name and newValue
});

You could even add an extension that makes it easy like:

ko.subscribable.fn.withUpdater = function(prop, handler) {
      this.subscribe(handler.bind(this, prop));

      //support chaining 
      return this;

};

Now you could use it in your view model like:

this.firstName = ko.observable("Bob").withUpdater("firstName", yourUpdateFunction);

Here is a sample: http://jsfiddle.net/rniemeyer/dy2f5/

RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211
  • Is it possible to call "yourUpdateFunction" function that is defined within model? (as this.myUpdateFunction = function...)? – renathy Nov 26 '13 at 10:46
  • And is it possible to get model.id property within myUpdateFunction? – renathy Nov 26 '13 at 11:14
  • 1
    does model.id change at all? If not, then something like this http://jsfiddle.net/rniemeyer/X2RVS/ using bind: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind – RP Niemeyer Nov 26 '13 at 19:21
  • You are awesome! I am working with knockout only 1 week and I have many problems that seems so clear afterwards. I am using your inline edit and withUpdater also. Btw, in another project I need much more complicated inline editor and I am not sure that clickToEdit can fit there. http://stackoverflow.com/questions/20228451/knockout-inline-editing-for-different-controls – renathy Nov 26 '13 at 21:37