0

I have list of check boxes, on click of any checkbox i need to get all checked check boxes. To do that I am calling the java script function "ChangeColumnSelection".

The issue is, the clicked check box is not updating view model immediately. When i click next text box, I am seeing that the previously check box value got updated in the view model.

 <ul class="list-group" data-bind="foreach: SelectionColumnList">
      <li class="list-group-item">
          <input type="checkbox" data-bind="attr: {onclick: 'javascript:ChangeColumnSelection(\'' + ColumnID + '\')'}, checked: IsSelected"
                        class="pull-xs-left push-down rightmargin" />
          <span data-bind="text: ColumnName"></span>
     </li>
 </ul>

Update:

My view model is

  var dynamicGridViewModel = {
        SelectionColumnList: ko.observableArray([])
   }; 

    selectionInfo.ColumnID = columnInfo.ColumnID;
    selectionInfo.ColumnName = columnInfo.ColumnName;
    selectionInfo.DisplayOrder = columnInfo.DisplayOrder;
    selectionInfo.SortType = 'None';
    selectionInfo.IsSelected = true;
    dynamicGridViewModel.SelectionColumnList.push(selectionInfo);
adiga
  • 34,372
  • 9
  • 61
  • 83
sivaprakash
  • 167
  • 1
  • 4
  • 13

1 Answers1

3

You don't need onclick events. You can achieve this just with checked binding:

var array = [{
  ColumnID: 1,
  ColumnName: "ColumnName 1"
}, {
  ColumnID: 2,
  ColumnName: "ColumnName 2"
}]

var viewModel = function() {
  var self = this;
  self.SelectionColumnList = ko.observableArray(array);
  
  // no need to populate the array manually. Knockout will take care of it
  self.chosenItems = ko.observableArray();

  // every time chosenItems array changes, subscribe callback function gets triggered
  self.chosenItems.subscribe(function() {
    console.log(self.chosenItems());
  })
}

ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<ul class="list-group" data-bind="foreach: SelectionColumnList">
  <li class="list-group-item">
    <input type="checkbox" data-bind="checkedValue: ColumnID, checked: $parent.chosenItems" />
    <span data-bind="text: ColumnName"></span>
  </li>
</ul>

Here, ColumnId is set as the value of checkbox input. Hence, the chosenItems array will be an array of selected ColumnIds.

The great thing about Knockout is that, it allows not only primitive types like string, number or bool for checkedValue, but objects too. If you want the entire Column object to be populated in chosenItems, then you can set the checkedValue like this:

<input type="checkbox" data-bind="checkedValue: $data, checked: $parent.chosenItems" />

If you want to perform some operation upon changing of any checkbox's state, you can perform that inside the subscribe callback. This function gets triggered every time the array changes.

(Also, the proper way to add a click binding is data-bind="click: clickFunction")

UPDATE:

You're using an object literal as your viewModel. I suggest you create viewModel function and use the new operator. If you want to bind checked to a boolean property of Column, then you can create a computed property and subscribe to that computed property:

var columns = [{
  ColumnID: 1,
  ColumnName: "ColumnName 1",
  IsSelected: false
}, {
  ColumnID: 2,
  ColumnName: "ColumnName 2",
  IsSelected: true
}];

var viewModel = function() {
  var self = this;
  self.SelectionColumnList = ko.observableArray([]);

  // this property has the selected ColumnIds
  self.selectedItems = ko.computed(() => {
    // If you're using ES6 systax
    // return self.SelectionColumnList()
    //  .filter(column => column.IsSelected())
    //   .map(column => column.ColumnID);

    // out of the columns, get the ColumnIds with IsSelected as true
    return self.SelectionColumnList()
      .filter(function(column) {
        return column.IsSelected();
      })
      .map(function(column) {
        return column.ColumnID
      });
  });

  // gets triggered everytime checkbox is checked/unchecked
  self.selectedItems.subscribe(function() {
    console.log(self.selectedItems());
  });
}

// create a new instance of the viewmodel
var dynamicGridViewModel = new viewModel();

// loop through the columns and populate the observableArray
columns.forEach(function(columnInfo) {
  var selectionInfo = {};

  selectionInfo.ColumnID = columnInfo.ColumnID;
  selectionInfo.ColumnName = columnInfo.ColumnName;

  // this property must be an observable
  selectionInfo.IsSelected = ko.observable(columnInfo.IsSelected);

  dynamicGridViewModel.SelectionColumnList.push(selectionInfo);
})

ko.applyBindings(dynamicGridViewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<ul class="list-group" data-bind="foreach: SelectionColumnList">
  <li class="list-group-item">
    <input type="checkbox" data-bind="checked: IsSelected" />
    <span data-bind="text: ColumnName"></span>
  </li>
</ul>

Here's a fiddle for testing

adiga
  • 34,372
  • 9
  • 61
  • 83
  • Thanks for your answer. I added click binding as you suggested, but click event not only fires on click, it also when populating data to view model. – sivaprakash Nov 17 '17 at 05:03
  • @sivaprakash I added the `click` binding documentation just as an additional resource. You don't need it in your case. Did you completely ignore what I wrote above the click binding part :D You need to change your `checked` binding. Read the answer on how to implement checked binding properly. Go through the documentation links I added. Click on the `Run code snippet` to see the working example – adiga Nov 17 '17 at 05:10
  • Thanks, click problem is solved. I wanted checkedValue to be bind with boolean property and Checked should push entire object to selectedItems view model – sivaprakash Nov 17 '17 at 05:57
  • @sivaprakash I'm assuming each `Column` has a `IsSelected` property? Please add all relevant parts of your viewmodel to the question. – adiga Nov 17 '17 at 06:10
  • I have included my view model – sivaprakash Nov 17 '17 at 06:25
  • @sivaprakash I have updated the answer. Please check it and let me know – adiga Nov 17 '17 at 07:40
  • I changed my view model to function view model, but I'm not able to ass items to observable array outsider view model. – sivaprakash Nov 17 '17 at 10:54
  • dynamicGridViewModel.FilterColumnList.push(filterInfo); – sivaprakash Nov 17 '17 at 10:54
  • @sivaprakash As you can see in the answer, you have to create an object by using the `new` operator: `var dynamicGridViewModel = new viewModel();` – adiga Nov 17 '17 at 10:56
  • @sivaprakash If your function itself is called `dynamicGridViewModel`, then call the instance something else like: `var gridInstance = new dynamicGridViewModel()`. Then bind `gridInstance.FilterColumnList.push(filterInfo)` – adiga Nov 17 '17 at 10:57
  • I need to push an item to array property of existing view model. I don't want to create new view model – sivaprakash Nov 17 '17 at 11:02
  • Are you not allowed to change from an `object literal` to a `function()` ? – adiga Nov 17 '17 at 11:07
  • You can take a look at [this answer](https://stackoverflow.com/a/21554907/3082296) on how to add a `computed` property to an object literal. – adiga Nov 17 '17 at 11:08
  • I have changed my view model to functional view model var dynamicGridViewModel = function() { var self = this; self.FilterColumnList = ko.observableArray([]) }; – sivaprakash Nov 17 '17 at 11:14
  • var filterInfo = {}; filterInfo.ColumnID = columnInfo.ColumnID; dynamicGridViewModel.FilterColumnList.push(filterInfo); Im not able to add like above – sivaprakash Nov 17 '17 at 11:15
  • 1
    `dynamicGridViewModel ` is a function.You can't access `FilterColumnList` like `dynamicGridViewModel.FilterColumnList`. You need to create an object using the `new` operator. This is similar to how a `class` works in C# or java. You can't access the property by `classname.property`. You need to create an instance of the class first. [So in javascript, it is done by using the `new` keyword against a function](https://stackoverflow.com/a/4859846/3082296). Like this: `var gridInstance = new dynamicGridViewModel()`. Then use `gridInstance.FilterColumnList ` etc – adiga Nov 17 '17 at 11:17