KO Popover Labels

Show how to bind knockout to the contents of a Bootstrap popover

by Jason Butz

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js"></script>
<script src=" https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.5/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<div class="container" style="margin-top: 80px;">
    <!-- ko template: {afterRender: afterRender} -->
    <button class="btn btn-lg btn-primary" data-bind="attr:{ id: btnId}" data-toggle="popover" data-contentx="Hello World">
        Click Me
    </button>
    <!-- /ko -->
    <pre id="output"></pre>
</div>



<script id="demoPopoverTemplate" type="text/template">
    <div>
        <span>Hello World!</span>
        <div data-bind='foreach: popoverLabels'>
        	<div>
            <input type="checkbox" data-bind="checked: selected">
            <span class="" data-bind="text: text"></span>
            </div>
        </div>
    </div>
</script>

SCSS

@import url(https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.5/css/bootstrap.min.css);
#output {
    margin-top: 25px;
    height: 500px;
}

JavaScript

class App {
    constructor() {
        this.btnId = Date.now();
        this.showType = 'TEAM';
		this.myLabels = ko.observableArray([
        	{
            	text: ko.observable('One'),
                selected: ko.observable(false),
                type: ko.observable('TEAM')
            },
            {
            	text: ko.observable('Two'),
                selected: ko.observable(true),
                type: ko.observable('PROJECT')
            },
            {
            	text: ko.observable('Three'),
                selected: ko.observable(true),
                type: ko.observable('TEAM')
            }
        ]);
        
        this.popoverLabels = ko.pureComputed(() => {
        	return this.myLabels().filter((label) => {
            	label.selected(); // Bind the computed to this value
            	return label.type() === this.showType;
            })
        })
        .extend({ rateLimit: { timeout: 1000, method: "notifyWhenChangesStop" } });
        
        this.popoverLabels.subscribe(() => {
        	this.save();
        });
    }
    save() {
		var outputEl = document.getElementById('output');
        var contents = outputEl.innerText;
        
        contents += `${Date()} | ${ko.toJSON(this.myLabels)}\n`;
        outputEl.innerText = contents;
    }
    afterRender(elements, viewModel) {
        // We need to have a single DOM element that contains everything we want in the popover
        //  A div with nothing on it should be fine
        // Here I am using jQuery to create the DOM elements for me
        //  from a string. It doesn't matter how they are created, they
        //  just must be DOM elements for KO to do the magic

        // Get the template element
        var templateEl = document.getElementById('demoPopoverTemplate');
        // Turn the template element's contents into a jquery object then extract the DOM
        //  elements
        var content = $(templateEl.innerText).get(0);
        // Set the content for a our...