1

In this code example I'm looking for a way to pass parameters from data in Knockout's template into Knockout's data-bind that should ultimately appear in modal window.

The result should be a modal window that displays a participant's name based on which participant is to be deleted.

Right now I have to enter the names manually, what should be done to have them automatically binded?

var viewModel = function() {
  this.showModal = function() {
    console.log("Showing modal");
    $('#modal').modal('show');
  }
  this.buyer = {
    name: 'Franklin',
    credits: 250
  };
  this.seller = {
    name: 'Mario',
    credits: 5800
  };
};
ko.applyBindings(new viewModel);
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div data-bind="template: { name: 'person-template', data: buyer }"></div>
<div data-bind="template: { name: 'person-template', data: seller }"></div>

<script type="text/html" id="person-template">
  <h3 data-bind="text: name"></h3>
  <p>Credits: <span data-bind="text: credits"></span>
  </p>
  <button data-bind="click: $root.showModal" class="btn">Delete</button>
</script>

<div id="modal" class="modal fade" tabindex="-1" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
        </button>
        <h4 class="modal-title">Delete item</h4>
      </div>
      <div class="modal-body">
        <p>Really delete Franklin?</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary">Delete</button>
      </div>
    </div>
    <!-- /.modal-content -->
  </div>
  <!-- /.modal-dialog -->
</div>
<!-- /.modal -->
Peter G.
  • 7,816
  • 20
  • 80
  • 154

2 Answers2

4

First, you need to make the name something KO manages:

<p>Really delete <span data-bind="text: deleting"></span>?</p>

Give yourself an observable for that in your view model:

this.deleting = ko.observable(null);

Then update showModal to update an observable:

this.showModal = function(deleting) {
  this.deleting(deleting.name);
  console.log("Showing modal");
  $('#modal').modal('show');
};

Then, since you're using $root.showModal, this will be incorrect, but we can addres that with bind:

<button data-bind="click: $root.showModal.bind($root)" class="btn">Delete</button>

Live Example:

var viewModel = function() {
  this.showModal = function(deleting) {
    this.deleting(deleting.name);
    console.log("Showing modal");
    $('#modal').modal('show');
  };
  this.deleting = ko.observable(null);
  this.buyer = {
    name: 'Franklin',
    credits: 250
  };
  this.seller = {
    name: 'Mario',
    credits: 5800
  };
};
ko.applyBindings(new viewModel);
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="template: { name: 'person-template', data: buyer }"></div>
<div data-bind="template: { name: 'person-template', data: seller }"></div>

<script type="text/html" id="person-template">
  <h3 data-bind="text: name"></h3>
  <p>Credits: <span data-bind="text: credits"></span>
  </p>
  <button data-bind="click: $root.showModal.bind($root)" class="btn">Delete</button>
</script>

<div id="modal" class="modal fade" tabindex="-1" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
        </button>
        <h4 class="modal-title">Delete item</h4>
      </div>
      <div class="modal-body">
        <p>Really delete <span data-bind="text: deleting"></span>?</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary">Delete</button>
      </div>
    </div>
    <!-- /.modal-content -->
  </div>
  <!-- /.modal-dialog -->
</div>
<!-- /.modal -->
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • The `click` binding is automatically bound to `$data`. You don't have to do this. Other than that, my answer is just a duplicate of yours. If you update yours, I'll delete mine. – Roy J Dec 21 '15 at 14:32
  • @RoyJ: Yes, we need `bind`, for the reason I gave: Because they're using `$root.showModal`. If they were just using `showModal`, it would have the right `this`. We can use `bind` to avoid doing the `self` trick you're doing. – T.J. Crowder Dec 21 '15 at 14:33
  • @T.J.Crowder Is there any good reason to avoid the "self trick"? (Also, why is it a "trick" in the first place?) – Tomalak Dec 21 '15 at 14:35
  • @RoyJ: But you're right about not needing to use `$data` there, I've fixed it. The wording was misleading as well. – T.J. Crowder Dec 21 '15 at 14:36
  • @Tomalak: I don't care for unnecessary use of it, not least because it impacts the ability of the JavaScript engine to optimize the context away. But either way is fine, really. – T.J. Crowder Dec 21 '15 at 14:38
  • 1
    Okay, point taken. What exactly can the engine optimize when I use `bind()` over pinning `this` to a local variable? (Really interested here. I thought the closure was there anyway.) – Tomalak Dec 21 '15 at 14:49
  • @Tomalak: It is *in theory*, but say you have `function foo() { var bar = blah(); return function () { /* ...do something not using 'bar' here... */}; }` In theory, the context of the call to `foo` and its associated lexical environment object are kept in memory as long as the caller keeps a reference to the returned function. But for optimization, unless `debugger`, `eval`, or `new Function` are used in the function, the JS engine **knows** `bar` isn't used and in fact nothing else from the context is used, and can release it. But it can't if `bar` (e.g., `self`) is used in that function. – T.J. Crowder Dec 21 '15 at 15:18
  • 1
    @Tomalak: V8 actually does this, and I expect SpiderMonkey and others as well, within some pretty narrow limits. V8 starts out with things like `bar` on the stack, then only creates a true lexical environment if it needs to (because the returned function refers to `bar`). **That said**, barring a real problem, it's a micro-opt. :-) And I haven't looked into the effect of arrow functions (which close over `this`) on it, though I suspect that'll get optimized someday (once ES6 support is done and they have time to go back and tune). – T.J. Crowder Dec 21 '15 at 15:21
  • @T.J.Crowder Seems that `click: $root.showModal.bind($root)` changes the `$root` to the value of the item clicked on. This is not bad, but it may cause some problems so I might go with @RoyJ solution, which doesn't change `$root`. I think it's just a matter of preference. – Peter G. Dec 21 '15 at 16:31
  • @PeterGerhat: `click: $root.showModal.bind($root)` has no effect whatsoever on `$root`. What are you observing that makes you think that? – T.J. Crowder Dec 21 '15 at 16:37
  • @T.J.Crowder the button inside the modal does not respond when everything is configured properly. It's the same for both your and the other answers, therefore the problem does not relate to this question. – Peter G. Dec 21 '15 at 17:08
  • @T.J.Crowder Thanks for the explanation. – Tomalak Dec 21 '15 at 18:15
4

You need to have a variable in your viewmodel to indicate which one is selected. Then in the click binding, you set it based on the data that is passed (automatically!) to the click binding.

var viewModel = function() {
  var self = this;
  this.showModal = function(data) {
    console.log("Showing modal", data);
    self.selected(data.name);
    $('#modal').modal('show');
  }
  this.selected = ko.observable();
  this.buyer = {
    name: 'Franklin',
    credits: 250
  };
  this.seller = {
    name: 'Mario',
    credits: 5800
  };
};
ko.applyBindings(new viewModel);
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="template: { name: 'person-template', data: buyer }"></div>
<div data-bind="template: { name: 'person-template', data: seller }"></div>

<script type="text/html" id="person-template">
  <h3 data-bind="text: name"></h3>
  <p>Credits: <span data-bind="text: credits"></span>
  </p>
  <button data-bind="click: $root.showModal" class="btn">Delete</button>
</script>

<div id="modal" class="modal fade" tabindex="-1" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
        </button>
        <h4 class="modal-title">Delete item</h4>
      </div>
      <div class="modal-body">
        <p>Really delete <span data-bind="text:selected"></span>?</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
        <button type="button" class="btn btn-primary">Delete</button>
      </div>
    </div>
    <!-- /.modal-content -->
  </div>
  <!-- /.modal-dialog -->
</div>
<!-- /.modal -->
Roy J
  • 42,522
  • 10
  • 78
  • 102