title: KnockoutJS: Writable computed observable not updating
body: <p>I have the following code where I am trying to use a writable computed observable to validate user input, and automatically reject invalid input.
  This is just some test code where I am trying to prevent the user from deleting the comma character from the text. If the user deletes the comma character I am attempting to rebind the editor to the prior value to overwrite the most recent change.
  Is this possible using a writable computed field?
  The issue that I am having is that the writable computed binding fires only when I load the page (after manually setting the ValidText prop.) I can't however get it to fire when the user types into the editor.</p>
  
  <p>Code:</p>
  
  <pre><code>&lt;script type="text/javascript"&gt;
  
      var model = null;
  
      function EditModel() {
  
          this.Count = 2;
  
          var self = this;
  
          this.ExistingValue = ko.observable("");
  
          this.ValidText = ko.computed({
              read: function () {
                  return self.ExistingValue();
              },
              write: function (text) {
                  if (text.split(',').length === self.Count) {
                      self.ExistingValue(text);
                  }
              },
              owner: self
          });
      }
  
      $(document).ready(function () {
  
          model = new EditModel();
  
          model.ValidText('@(Model.Text)');
  
          ko.applyBindings(model);
      });
  
  &lt;/script&gt;
  
  &lt;div id="mainContainer"&gt;
      &lt;div id="editor" contenteditable="true" class="commentEditBox" data-bind="text: ValidText, valueUpdate: 'afterkeydown'" &gt;&lt;/div&gt;
  &lt;/div&gt; 
  </code></pre>
  
  <p>Ended up going with the custom binding idea and converted the code to the following:</p>
  
  <pre><code>&lt;script type="text/javascript"&gt;
  
      var model = null;
  
      ko.bindingHandlers.customEditorBinding = {
          init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
  
              $(element).on('keydown', function () {
                  var observable = valueAccessor();
                  observable($(this).text());
              });
          },
          update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
              var text = ko.utils.unwrapObservable(valueAccessor());
  
              if (text.split(',').length === model.Count) {
                  model.ValidText(text);
                  model.PreviousText = text;
                  $(element).text(text);
              }
              else {
                   model.ValidText(model.PreviousText);
                  $(element).text(model.PreviousText);
              }
          }
      };
  
      function EditModel() {
  
          var self = this;
  
          self.Count = 2;
  
          self.ValidText = ko.observable("");
  
          self.PreviousText = null;
      }
  
      $(document).ready(function () {
  
          model = new EditModel();
  
          model.ValidText('This is,a test');
  
          ko.applyBindings(model);
      });
  
  &lt;/script&gt;
  
  &lt;div id="mainContainer"&gt;
      &lt;div id="editor" contenteditable="true" class="commentEditBox" data-bind="customEditorBinding:ValidText" &gt;&lt;/div&gt;
  &lt;/div&gt;
  </code></pre>