1

I'm trying to allow editing items in an array of strings like so:

http://jsfiddle.net/adamfraser/sr4Fg/44/

html

<h4> edit </h4>
<ul class="list" data-bind="foreach:titles">
  <li class="title" data-bind="editableHTML:$data" contenteditable="true"></li>
</ul>

<h4> view </h4>
<ul class="" data-bind="foreach: titles">
  <li class="title" data-bind="text:$data"></li>
</ul>

js

ko.bindingHandlers.editableHTML = {
  init: function(element, valueAccessor) {
    var $element = $(element);
    var initialValue = ko.utils.unwrapObservable(valueAccessor());
    $element.html(initialValue);
    $element.on('keyup', function() {
      observable = valueAccessor();
      observable($element.html());
    });
  }
};

viewModel= {
  titles : ko.observableArray([
    "one", "two"
  ])
};

ko.applyBindings(viewModel);

While the custom editableHTML handler works for regular (non-array) observables, it's not cutting it for observableArrays. Anyone know why? I'm still new to KO.

Adam Fraser
  • 6,255
  • 10
  • 42
  • 54
  • Look at your console `Uncaught TypeError: string is not a function` – Matt Burland Sep 29 '14 at 18:30
  • Yeah, I've seen it, but I'm not sure what's going on here yet. Still figuring out Knockout. – Adam Fraser Sep 29 '14 at 18:33
  • Update: this fiddle shows that my handler works for non-array data http://jsfiddle.net/adamfraser/sr4Fg/45/ – Adam Fraser Sep 29 '14 at 18:49
  • 1
    Your items in the array also need to be observable. However, when using the foreach binding Knockout will automatically unwrap each item in the array which will result in the same behavior. [link1](http://stackoverflow.com/questions/9510539/knockout-js-how-to-correctly-bind-an-observablearray) [link2](http://stackoverflow.com/questions/14222982/knockout-js-binding-to-array-of-observable-ints) – Aaron Carlson Sep 29 '14 at 20:25
  • @AaronCarlson, you can overcome the automatic unwrapping and still have access to the observable function by passing `$rawData` instead of `$data`, but that of course will require some changes to the binding-handler code. – haim770 Sep 29 '14 at 21:19

1 Answers1

3

Option 1:

You can explicitly point the editableHTML handler to the parent array (titles in your case) using custom direction (context in my code).

Once you have the reference to the parent array you can use the replace method that is available for each observable array to update the value.

Something like:

ko.bindingHandlers.editableHTML = {
  init: function(element, valueAccessor, allBindings) {
    var $element = $(element);
    var initialValue = ko.utils.unwrapObservable(valueAccessor());
    var parentContext = allBindings.get('context');

    $element.html(initialValue);

    $element.on('keyup', function() {
      var curVal = valueAccessor();
      var newVal = $element.html();

      if (parentContext)
          parentContext.replace(curVal, newVal);
      else if (ko.isObservable(valueAccessor()))
          valueAccessor()(newVal);
    });
  }
};

Then:

<li class="title"
    contenteditable="true"
    data-bind="editableHTML: $data, context: $parent.titles"></li>

See Fiddle

Option 2:

Change the titles array to contain observables instead of plain values, then call the observable upon value change.

For example:

ko.bindingHandlers.editableHTML = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var $element = $(element);
    var initialValue = ko.utils.unwrapObservable(valueAccessor());
    var origObservable = bindingContext.$rawData;

    $element.html(initialValue);

    $element.on('keyup', function() {
      var curVal = valueAccessor();
      var newVal = $element.html();

      if (ko.isObservable($origObservable))
          origObservable(newVal);
    });
  }
};

Your updated view-model:

var viewModel = {
   titles: ko.observableArray([ko.observable("one"), ko.observable("two")])
};

And the HTML remains the same:

<li class="title"
    contenteditable="true"
    data-bind="editableHTML: $data"></li>

See Fiddle

haim770
  • 48,394
  • 7
  • 105
  • 133
  • 2
    FYI - With option 1 if there are duplicates in the list and the last one is edited the first item in the list will be updated instead. – Aaron Carlson Sep 29 '14 at 21:38