Simsons
Simsons

Reputation: 12745

Knockout : Update to Text box value is not updating the observable item

I have an ObservavleArray , which is initialized with two items when the UI loads.

Now there is an add button which pushes new observable item to observable array. This works fine.

But when I edit the values on text box and I expect them to change on underlying observable item, but this does not happen.

Here is the code snippet for that.

 var InventoryViewModel = function() {
     var self = this;
     self.STockItems = ko.observableArray();

     InitializeStock(self);

     self.addItems = function(vm) {
         self.STockItems.push(new ko.observable(''));
     }
     self.ProcessInventory = function(vm) {
         console.log(vm.STockItems());
     };
 }

 var vm = new InventoryViewModel();
 ko.applyBindings(vm);


 function InitializeStock(context) {
     context.STockItems.push(new ko.observable('Item1'));
     context.STockItems.push(new ko.observable('Item2'));
 }
li {
    list-style: none;
    margin-top: 10px;
}

.info-text {
    margin-top: 30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: STockItems">
    <li>
        <span class="input-group">
            <input  data-bind="textInput: $data" />                                       
        </span>

    </li>
</ul>
<div class="text-right"> <a data-bind="click:addItems">+ Add more</a></div>
<button type="submit" class="btn btn-accent" data-bind="click: $root.ProcessInventory">Process Inventory</button>
<pre class='info-text' data-bind="text: ko.toJSON($data, null, '\t')">
</pre>

http://jsfiddle.net/xvza6nu2/1/

Edit: As I am pushing a observable item directly , I guess I do not have to create a wrapper to push the items.

function EditableField(initialValue) {
  this.value = ko.observable(initialValue);
}

If I must use this wrapper, then why? What's the difference?

Upvotes: 0

Views: 1659

Answers (1)

Ray
Ray

Reputation: 3959

Observables need to be bound to a property in the viewmodel, that's the only way to reference them from the HTML. $data is a reference to the viewmodel's context rather than the observable itself. It just so happened that in your foreach context there was only 1 observable, so it looked like your initial setup was working.

Read all about the binding context here.

To get it to work properly, you need to push objects into your observableArray, with named properties that will later be referenced from the HTML. Each object in the observableArray becomes a mini-viewmodel in the foreach loop.

Edit:

  1. There's no need to use new to create on observable.
  2. $root refers to the primary viewModel. It's useful to use it when we want to reference it from a child's/descendant's viewModel. In your case, for ProcessInventory click, it's not needed since we are already in the primary viewModel context.

 var InventoryViewModel = function() {
     var self = this;
     self.STockItems = ko.observableArray();

     InitializeStock(self);

     self.addItems = function(vm) {
         self.STockItems.push({item : ko.observable('')});
     }
     self.ProcessInventory = function(vm) {
         console.log(vm.STockItems());
     };
 }

 var vm = new InventoryViewModel();
 ko.applyBindings(vm);


 function InitializeStock(context) {
     context.STockItems.push({item : ko.observable('Item1')});
     context.STockItems.push({item : ko.observable('Item2')});
 }
li {
    list-style: none;
    margin-top: 10px;
}

.info-text {
    margin-top: 30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: STockItems">
    <li>
        <span class="input-group">
            <input  data-bind="textInput: item" />                                       
        </span>

    </li>
</ul>
<div class="text-right"> <a data-bind="click:addItems">+ Add more</a></div>
<button type="submit" class="btn btn-accent" data-bind="click: ProcessInventory">Process Inventory</button>
<pre class='info-text' data-bind="text: ko.toJSON($data, null, '\t')">
</pre>

Upvotes: 1

Related Questions