18

I need to show modal dialog for editing item in single page app when I select an item from list.

Problem: I used visible binding, but that turned out to be cumbersome, and it does not work properly, as it shows only dialog, without overlay, and fade (if any) does not work.

Html:

<div class="modal hide fade" data-bind="visible:selectedItem, with:selectedItem">
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
    <h3 data-bind="text:name"></h3>
  </div>
  <div class="modal-body">
    <form data-bind="submit:deselectItem">
        <!-- editor for item here -->
    </form>
  </div>
  <div class="modal-footer">
    <a href="#" class="btn" data-bind="click:deselectItem">Close</a>
  </div>
</div>

Model for this is simple object with observableList, obervable selectedItem, and deselectItem function which sets selectedItem to null.

Goran Obradovic
  • 8,951
  • 9
  • 50
  • 79

4 Answers4

42

As I figured out, the (probably) best way to do this is to create a ko binding handler, I called it showModal:

ko.bindingHandlers.showModal = {
    init: function (element, valueAccessor) {},
    update: function (element, valueAccessor) {
        var value = valueAccessor();
        if (ko.utils.unwrapObservable(value)) {
            $(element).modal('show');
            // this is to focus input field inside dialog
            $("input", element).focus();
        }
        else {
            $(element).modal('hide');
        }
    }
};

Usage is like this:

<div class="modal hide fade" data-bind="showModal:selectedItem, with:selectedItem">
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
    <h3 data-bind="text:name"></h3>
  </div>
  <div class="modal-body">
    <form data-bind="submit:deselectItem">
        <!-- editor for item here -->
    </form>
  </div>
  <div class="modal-footer">
    <a href="#" class="btn" data-bind="click:deselectItem">Close</a>
  </div>
</div>
Ben Cottrell
  • 5,741
  • 1
  • 27
  • 34
Goran Obradovic
  • 8,951
  • 9
  • 50
  • 79
  • 1
    Indeed binding handlers are the way to go. Just change the `if (value())` with `if (ko.utils.unwrapObservable(value))`. Doing this will enable you to use your binding with `showModal: true` or `showModal: !selectedItem()` or any other javascript expression. – Angel Yordanov Feb 04 '13 at 15:20
  • I am curious if this solution provides the opposite feedback - if the modal is closed, will 'selectedItem' reflect the closing? – G. Stoynev Feb 27 '15 at 01:40
  • 3
    If you want opposite feedback you can subscribe to any event in 'init' part of the binding and delete selected item on closing (not sure if you want same action on cancel and on submit though) – Goran Obradovic Feb 27 '15 at 07:37
  • This is great, worked like a charm for me. Thank you. – JoannaFalkowska Jul 13 '16 at 08:26
  • This answer is indeed what I was looking for – Kent Aguilar Nov 15 '16 at 07:18
5

I like Gorans simple approach. Only problem is, that the html will not work in Bootstrap 3+.
The "hide" class is now enabled by default, and having it as class on modal div, will result in the div not showing. See http://www.bootply.com/bootstrap-3-migration-guide
This works:

<div class="modal fade" data-bind="showModal:selectedItem, with:selectedItem">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                            <h3 data-bind="text:TextItem"></h3>
            </div>
            <div class="modal-body">
                <form data-bind="submit:$root.accept">
                    <!-- editor for item here -->
                    <div class="form-group">
                        <label>Text</label>
                        <input type="text" class="form-control" data-bind="value: TextItem" placeholder="Text">
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button class="btn" data-dismiss="modal">Luk</button>
                <button class="btn" data-bind="click:$root.accept">Gem</button>
            </div>
        </div>
    </div>
</div>

In this example click:$root.functionName will call a function in your ViewModel.

Hersker
  • 547
  • 5
  • 11
0

It gets a lot easier if you apply the following pattern:

<form class="modal hide fade" data-bind="visible:selectedItem, with:selectedItem, submit:deselectItem">
    ...
</form>

That way, you get the form back in your submit handler and can properly close it there:

deselectItem = function(form){
  ...
  $(form).modal('hide');
}
bert bruynooghe
  • 2,985
  • 1
  • 20
  • 18
  • 2
    Thanks, but I don't like to have jQuery selectors inside my viewmodel, it kinda breaks the purpose of using knockout. Main benefit as I see it is having clean js viewmodels and logic inside that is completely decoupled from html structure. Only jQuery feature that I would use in my viewmodel would be $.ajax/post/get, but even that is better when aliased so you can have dependency in single point. – Goran Obradovic Nov 01 '13 at 09:54
  • ($(form) is not a selector, it is a wrapper. Doing so, it is not coupled to any styling logic you included.) I can see your point of not to depend too hard on jQuery in your model, but since it's called viewModel, I think it has a right to be here, especially since your layout is so tightly coupled to bootstrap. Moreover, I think that jQuery is lot wider accepted than knockout, so I'd rather try not depend too hard on knockout than trying not to depend on jQuery. – bert bruynooghe Nov 03 '13 at 20:22
  • 1
    Yes, but this guy explained it lot better than I would http://stackoverflow.com/a/15012542/408643 :) – Goran Obradovic Nov 04 '13 at 06:35
0

I have used this custom binding handler for bootstrap modals. Also in the modal i have added two moment datepicker. but when I use this binding handler the modal is visible on click event but datepicker div also visible

if I don't use this and use

$('.classname_of_bootstrap_modal').modal('show')

The modal works perfectly fine.

So, i guess there is some issue in using this binding handler and not able to get it.

I commented this

$("input", element).focus();

On doing that the datepicker div's are not displaying automatically. they are visible on when i click on them but issue on removing this is if i click one of the datepicker, it closes the bootstrap modal. but it shouldnt close it.

Suraj Singh
  • 4,041
  • 1
  • 21
  • 36
Haridarshan
  • 1,898
  • 1
  • 23
  • 38