knockout.jsknockout-binding-handlers

Knockoutjs custom bindinghandlers methods not firing


I'm trying to make knockoutjs and bootstrap modals play together by creating a custom bindingHandler in knockout, but for some reason the bindingHandler init & update functions never fire (i added prints to verify if this runs, which confirms it doesn't)

Here's my bindingHandler declaration:

require(['jquery', 'knockout', 'restaurant/ViewModel', 'bootstrap'], function ($, ko, ViewModel) {
    console.log('running require');

    ko.bindingHandlers.modal = {
        init: function (element, valueAccessor) {
            console.log('initializing bootstrap modal');
            $('#restaurant-new-modal').modal();
        },
        update: function (element, valueAccessor) {
            console.log('unwrapped valueAccessor:' + ko.unwrap(valueAccessor()));
            if (ko.unwrap(valueAccessor())) {
                $('#restaurant-new-modal').modal('show');
                $('input#restaurant-new-name').focus();
            }
            else {
                $('#restaurant-new-modal').modal('hide');
            }
        }
    };

    var viewModel = new ViewModel();

    $.ajax({
        url: 'http://localhost:8000/api/restaurants'
    }).done(function (data) {
        console.log('ajax query done');
        for (var i = 0; i < data.length; i++) {
            viewModel.addRestaurantToList(data[i]);
        }

    }).fail(function () {
        console.log('AJAX query failed');
    });

    console.log('comes after ajax');

    ko.applyBindings(viewModel, $('#restaurant-list-wrapper')[0]);

});

And my html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Restaurants</title>
    <link type="text/css" rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css"/>
    <link type="text/css" rel="stylesheet" href="/css/style.css"/>
    <script data-main="/js/restaurant/main" src="/lib/requirejs/require.js" type="application/javascript"></script>
</head>
<body>

<h1>Restaurants You Might Like</h1>

<div id="restaurant-list-wrapper" style="display: none;" data-bind="visible: restaurantList().length > 0">
    <ul id="restaurant-list" data-bind="foreach: restaurantList">
        <li class="restaurant">
            <div class="restaurant-info">
                <span data-bind="text: 'Restaurant #' + $data.store.id() + ': '"></span>

                <a href="#" data-bind="text: $data.store.name, visible: !isEditable()"></a>
                <input class="restaurant-input" type="text"
                       data-bind="visible: isEditable(), textInput: $data.tempName"/>
            </div>

            <div class="restaurant-action-bar">
                <a href="#" class="restaurant-update-button btn btn-success btn-sm"
                   data-bind="text: 'Update', visible: isEditable(), click: $root.updateHandler"></a>
                <a href="#" class="restaurant-edit-button btn btn-primary btn-sm"
                   data-bind="text: isEditable() ? 'Cancel' : 'Edit', click: $root.editHandler"></a>
                <a href="#" class="restaurant-delete-button btn btn-danger btn-sm"
                   data-bind="text: 'Delete', click: $root.deleteHandler"></a>
            </div>
        </li>
    </ul>
    <div id="vertical-toolbar">
        <a href="#" id="restaurant-new-button" class="glyphicon glyphicon-plus round green" data-bind="click: function() { showModal(true); }"></a>
    </div>

</div>

<div id="restaurant-new-modal" class="modal fade" role="dialog" data-bind="modal: showModal">
    <div class="modal-dialog modal-lg">
        <div class="modal-header">
            <h3 class="modal-title">Create New Restaurant</h3>
        </div>
        <div class="modal-content">
            <ul>
                <li>
                    <label for="restaurant-new-name">Enter new restaurant name</label><input id="restaurant-new-name"
                                                                                             type="text"/>
                    <a href="#" class="btn btn-success btn-lg" data-bind="click: newHandler">Create</a>
                </li>
            </ul>

        </div>
        <div class="modal-footer">

        </div>
    </div>
</div>

</body>
</html>

Any ideas?


Solution

  • You're applying bindings to #restaurant-list-wrapper but your modal binding is not inside it. You should also apply bindings to #restaurant-new-modal (you can apply the same view model twice to different containers or wrap both containers into another one and apply binding to it only).