LAffair
LAffair

Reputation: 1998

Knockout click binding with parameters

I'm trying to add an click event to my button that will send the Id, Category and Name as parameters only when the button is clicked.

<tbody data-bind="foreach: tehTab()">
    <tr>
       <td data-bind="text: $data.Category"></td>
       <td data-bind="text: $data.Name"></td>
       <td>
          <button type="button" class="btn chart_btn" role="button" data-toggle="popover" data-trigger="focus" data-html="true" data-placement="right" container="body" tabindex="0" data-original-title="" title="" style="border:none; background-color:white" data-bind="attr: { id: $data.Id,'data-contentwrapper':'.chartdraw' + $data.Id},click: getLast7($data.Id, $data.Category, $data.Name) , text:$data.Value"></button>
         <div data-bind="css: 'chartdraw' + $data.Id" class="chartdrawetc" style="display:none">ASD</div>
       </td>
   </tr>
</tbody>

even if I try to change:

click: getLast7($data.Id, $data.Category, $data.Name)

with

 attr: { id: $data.Id ,onclick: getLast7($data.Id, $data.Category, $data.Name)

it still fires the getLast7 method as many times as the tehTab length.

What am I doing wrong?

Upvotes: 12

Views: 29699

Answers (6)

CodeThug
CodeThug

Reputation: 3192

If you want to retain your original binding syntax, have the function in your viewmodel return a function.

ko.applyBindings({
  Id: 15,
  Value: ko.observable('Click Me!'),
  Category: 'standard category',
  getLast7: function(id, category, name){
    return function() {
      console.log("You clicked and passed in " + id + ", " + category + ", " + name);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<button data-bind="click: getLast7($data.Id, $data.Category, 'Hello World!'), text:$data.Value">

Upvotes: 0

Philip Bijker
Philip Bijker

Reputation: 5115

You can also simply wrap the click handler (getLast7 in your case) in a function. You can then supply it any parameter you'd like. Properties from $data, custom values or even properties from different contexts:

ko.applyBindings({
  container: {
    containerId: 42,
    items: [{
      id: 1,
      category: 'item category',
      name: 'item name',
      log: function(id, category, name, custom, containerId) {
        console.log(`id: ${id}, category: ${category}, name: ${name}, custom text: ${custom} containerId: ${containerId}`);
      }
    }]
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="with: container">
  <div data-bind="foreach: items">
    <button type="button" data-bind="click: function() { log($data.id, $data.category, $data.name, 'custom text', $parent.containerId); }">Click me</button>
  </div>
</div>

Upvotes: 0

Fabio Silva Lima
Fabio Silva Lima

Reputation: 714

I always use that and works fine.

html:

<input type="button" value="click to test" data-bind="click: $root.getCompleteDetails" />

javascript:

getCompleteDetails: function (item, event) {
            // you can access all properties in item parameter 
        }

I hope it helps.

Upvotes: 0

Sachin Goyal
Sachin Goyal

Reputation: 98

For click binding inside a loop, use this to get values of entry in current row.

<tbody data-bind="foreach: DataList">
 <tr>
  <td><input type="label" data-bind="text: Id"></td>
  <td><input type="label" data-bind="text: Category"></td>
  <td><input type="label" data-bind="text: Name"></td>
  <td><input type="button" data-bind="click: $parent.getCompleteDetails" value="View Details"/></td>   
 </tr>
</tbody>

In Knockout script, get current row entries using this keyword

  self.DataList   =ko.observableArray([]); //this is used to populate the data entries.
  self.getCompleteDetails= function() {   
    alert(this.Id);
    alert(this.Category);
    alert(this.Name);
  }

Upvotes: 0

user3297291
user3297291

Reputation: 23372

Even though you can fix it with a bind in your click binding, I don't think this is the best solution.

The click binding passes the clicked model to the listener by default. Bind will create a new function for each item and moves logic to your views.

Here's an example:

var data = [
  { Id: 0, Category: "A", Name: "A0" },
  { Id: 1, Category: "B", Name: "B1" },
  { Id: 2, Category: "C", Name: "C2" },
  { Id: 3, Category: "D", Name: "D3" }
];

ko.applyBindings({
  data: data,
  log: function(item) {
    console.log(item);
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: data">
  <li data-bind="click: $parent.log, text: Name"></li>
</ul>

Create an extra onClick function if you need to extract properties to an argument list:

var data = [
  { Id: 0, Category: "A", Name: "A0" },
  { Id: 1, Category: "B", Name: "B1" },
  { Id: 2, Category: "C", Name: "C2" },
  { Id: 3, Category: "D", Name: "D3" }
];

var log = function(id, cat, name) {
   console.log("Item ", id, "was clicked. (name:", name, "category:", cat); 
}

ko.applyBindings({
  data: data,
  onClick: function(item) {
    log(item.Id, item.Category, item.Name);
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: data">
  <li data-bind="click: $parent.onClick, text: Name"></li>
</ul>

Upvotes: 2

Rajesh
Rajesh

Reputation: 24915

Adding () after function name will call it. You will have to use .bind

click: getLast7.bind(this, $data.Id, $data.Category, $data.Name)

Sample

function vm(){
  this.notify = function(str){
    console.log(str)
  }
}

ko.applyBindings(new vm())
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
<div data-bind="click: notify.bind(this, 'Hello')">Click me</div>

Upvotes: 31

Related Questions