6

jsFiddle example


I have an observableArray of selectable items (in a table). What I'm trying to do is open a modal on click of the table row, populate the modal with the item details, allow editing of the item and then save the changes - reflecting the updated item in the observableArray. I've got everything else working so far, but can't seem to get the array item to update.

So far I've tried:

  • Making every item in the observableArray an observable
  • using .replace on the array to update the item - this does work, but it just feels wrong
  • posting back the updated item, applying it to the database and rebinding the array - although this works, doesn't this defeat the point of KnockoutJS?

I've provided a jsFiddle link above that demonstrates what I'm trying to achieve.

View model and initialization

Feel free to make any suggestions on how I'm initializing self.selectItem I'm currently in the learning stage of KnockoutJS, and doing so by playing around with mock projects so I'm open to all constructive criticism.

var items = [{
    Id: 1,
    Text: 'First item'
}, {
    Id: 2,
    Text: 'Second item'
}];

var viewModel = function (items) {
    var self = this;
    self.items = ko.observableArray(items);
    self.selectedItemId = ko.observable();
    self.item = ko.observable();
    self.selectItem = function (item) {
        for (var i = 0; i < self.items().length; i++) {
            if (self.items()[i].Id === self.selectedItemId()) {
                self.item(self.items()[i]);
                break;
            }
        }
    };
};

ko.applyBindings(new viewModel(items));

Markup bindings

<select data-bind="options: items, optionsCaption: 'Select...', optionsText: 'Text', optionsValue: 'Id', value: selectedItemId, event: { change: selectItem }"></select>
<div data-bind="if: item">
    <input type="text" data-bind="value: item().Text" />
</div>
<table>
    <thead>
        <tr>
            <th>Text</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: items">
        <tr>
            <td data-bind="text: $data.Text"></td>
        </tr>
    </tbody>
</table>

3 Answers 3

3

I have create a new jsFiddle example with an update of items when you change the 'Text' property.

Your problem was the 'items' variable. If you want to update items properties, you have to make them observable :

var observableItems = [
    new ItemViewModel(1, "First item"),
    new ItemViewModel(2, "Second item")
];

function ItemViewModel(id, text){
    var self = this;
    self.Id = ko.observable(id);
    self.Text = ko.observable(text);
}

Hope it helps !

5
  • Of course! It should've been obvious, I especially like the use of observable classes for each entity in the array. Thanks for this, much appreciated! Commented Jul 16, 2013 at 11:00
  • I'm come from strong typed language... I think that's why I used observable classes for each entity :) Commented Jul 16, 2013 at 11:58
  • Awesome JofferyKern sir amazing and good solution. Need some change but it works at my end.
    – 3 rules
    Commented Jul 12, 2017 at 7:38
  • Thx @padhiyar. Feel free to update the post if it helps ! Commented Jul 12, 2017 at 9:52
  • @JoffreyKern no sir "need some change" means at my end as per my requirement not in the answer sir. :)
    – 3 rules
    Commented Jul 12, 2017 at 10:13
2

Your code work fine. All what you need is to update your items array to have it's property Text to be observable

var items = [{
    Id: 1,
    Text: ko.observable('First item')
}, {
    Id: 2,
    Text: ko.observable('Second item')
}];

And i add small code to check if the selected option is "Select..." then clear the item to hide the input text.

kindly check my Working DEMO

1
  • Close to the accepted answer but minus the use of classes, your help is still appreciated though. Thanks! Commented Jul 16, 2013 at 11:01
1

You need to convert the Text propperty into observable. I choose to use ko.mapping to do it. ko.mapping.fromJS will convert all js properties into ko.observables.

And in the post method I convert the observable viewmodel into raw data.

var viewModel = function (items) {
    var self = this;
    // to observable
    self.items = ko.mapping.fromJS(items);
    self.selectedItemId = ko.observable();
    self.item = ko.observable();
    self.selectItem = function (item) {
        for (var i = 0; i < self.items().length; i++) {
            if (self.items()[i].Id() === self.selectedItemId()) {
                self.item(self.items()[i]);
                break;
            }
        }
    };

    self.post =  function(){
    // to raw js object
        var data =  ko.mapping.toJS(self.items);
        console.log(JSON.stringify(data));
    };

};

See fiddle

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.