Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Integrating Knockout with Twitter Bootstrap Popover

4.17/5 (3 votes)
8 Mar 2014CPOL2 min read 31.7K  
Integrating Knockout with Twitter Bootstrap Popover

Introduction

Knockout is a powerful and pretty useful javascript library to have the 2 way binding MVVM (Model-View-ViewModel) pattern implementation along with the templates and built-in binding support to
bind the data to the HTML controls.<o:p>

Knockout also does support the developers to write the custom binding (out of the box) and can use those binding to bind the data to the HTML controls.<o:p>

Twitter Bootstrap is a web UI toolkit (HTML / CSS and Javascript) to build the rich and responsive web application.

To know more about the Twitter Bootstrap Popover click here

The below code explain how to bind the model (data) using Knockout to the Twitter Bootstrap Popover.

For this code snippet, I have used Knockout Custom Binding Handler. To know more about the Knockout Customer Binding Handler click here

<o:p>

Look & feel

1. The initial data displayed in the table.

Image 1

2. After clicking the "Edit", the popover with the corresponding person's data will be displayed.

Image 2

3. On changing any data in the popover, the corresponding data will be updated in the view.

Image 3

Using the code

Below is the working code for binding the knockout view model with the twitter bootstrap popover.

In the initial of the code, I have given the references to the CDN path for the Knockout, JQuery and Bootstrap.

Next is, I have created a Person ViewModel which is a collections of Persons (Observable Array) that will be bind to the Popover (View)

Now the key part is to define the Knockout Custom Binding Handler. The KO custom binding handler will have two methods i.e. init and update. Here I have set the properties in the init method like the element which binds this handler and passes the "id", the dynamic template content. Using bindingContext, we can attach the ViewModel to the Popover HTML content.

Also to make sure, the popover trigger property should be set to "manual" and binding will be handled in the click event.

Using the Knockout template ("person-template") and the View Model, the template will be rendered with the support of 2 way binding. Any changes made to any of the data in the popover will be reflected back to the view i.e. the Person Table.

C++
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Knockout and Bootstrap Popover</title>
    <link href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap-responsive.css" rel="stylesheet">
    <link href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
    <script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js"></script>
    <script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
</head>
<body>
    <script>
        var personViewModel = function () {
            var self = this;
            self.persons = ko.observableArray();
            self.persons.push({
                id: 1,
                firstName: ko.observable("John"),
                lastName: ko.observable("Doe"),
                email: ko.observable("john.doe@email.com")
            })
            self.persons.push({
                id: 2,
                firstName: ko.observable("Greg"),
                lastName: ko.observable("Smith"),
                email: ko.observable("greg.smith@email.com")
            })
            self.persons.push({
                id: 3,
                firstName: ko.observable("Tom"),
                lastName: ko.observable("Walter"),
                email: ko.observable("tom.walter@email.com")
            });

            self.closePopover = function (person) {
                $('#popover' + person.id + "_click").popover("hide");
            };

        }

    </script>

    <script>
        ko.bindingHandlers.popUp = {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                var attribute = ko.utils.unwrapObservable(valueAccessor());
                var templateContent = attribute.content;
                var popOverTemplate = "<div class='popOverClass' id='" + attribute.id + "-popover'>" + $(templateContent).html() + "</div>";
                $(element).popover({
                    placement: 'right',
                    content: popOverTemplate,
                    html: true,
                    trigger: 'manual'
                });
                $(element).attr('id', "popover" + attribute.id + "_click");
                $(element).click(function () {
                    $(".popOverClass").popover("hide");
                    $(this).popover('toggle');
                    var thePopover = document.getElementById(attribute.id + "-popover");
                    childBindingContext = bindingContext.createChildContext(viewModel);
                    ko.applyBindingsToDescendants(childBindingContext, thePopover);
                })
            }
        }
    </script>

    <br />
    <br /><br /><br /><br /><br /><br />
    <table border="1" cellpadding="0" cellspacing="0" data-bind="foreach: persons">
        <tr>
            <td><span data-bind="text: firstName"></span></td>
            <td><span data-bind="text: lastName"></span></td>
            <td><span data-bind="text: email"></span></td>
            <td><a href="#" data-bind="popUp: {content: '#person-template', id: id}">Edit</a></td>
        </tr>
    </table>
    <script type="text/html" id="person-template">
        <table border="1" cellpadding="0" cellspacing="0">
            <tr>
                <td>First Name</td>
                <td><input type="text" data-bind="value: firstName, valueUpdate:'keyup'" /></td>
            </tr>
            <tr>
                <td>Last Name</td>
                <td><input type="text" data-bind="value: lastName, valueUpdate:'keyup'" /></td>
             </tr>
            <tr>
                <td>Email Address</td>
                <td><input type="text" data-bind="value: email, valueUpdate:'keyup'" /></td>
            </tr>
        </table>
        <br>
        <div class="row-fluid">
            <center>
                <div class="span3 offset1">
                    <button class="btn btn-small btn-danger" data-bind="click:$root.closePopover">Close</button>
                </div>
            </center>
        </div>
    </script>
    <script>
        ko.applyBindings(new personViewModel());
    </script>
</body>
</html>

Conclusion

I hope it will helpful to the developers who are building the rich and responsive UI using knockout and Twitter bootstrap. Though I have used the pre-populated data but you can get data from any of the services.

Thanks for reading.


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)