Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collapsable panel with bootstrap and knockout

I can't make an accordion with KnockoutJS, and Bootstrap to work properly. I have defined it like so:

<div class="panel-group" id="accordion" data-bind="foreach: Advertisers()">
    <div class="panel panel-default">
        <div class="panel-heading">
            <h4 class="panel-title">
                <span data-toggle="collapse" data-bind="html: $data, attr: { 'data-target': '#' + $data }"></span>
            </h4>
        </div>
    </div>
    <div class="panel-collapse collapse" data-parent="#accordion" data-bind="attr: { id: $data }">
        <div class="panel-body">
             ...content...

"Advertisers" is an observable array of strings, and hence $data is a string. I get one "row" for each advertiser.

All rows are initially collapsed, and clicking a row expands the content below. So far so good.

The problem is that when I click another row I would expect the previous expanded to collapse, but that's not happening. (I couldn't make a fiddle to work either, with Bootstrap and KnockoutJS...)

Edited the code.

like image 510
burktelefon Avatar asked Jan 01 '26 07:01

burktelefon


1 Answers

What about a simple custom binding, which also allows you to unclutter your view a bit:

ko.bindingHandlers.bootstrapAccordion = {
  init: function(elem, value, allBindings) {
    var options = ko.utils.unwrapObservable(value()),
        handleClass = '[data-toggle]',
        contentClass = '.collapse',
        openItem = ko.utils.unwrapObservable(options.openItem) || false,
        itemClass = '.' + ko.utils.unwrapObservable(options.item) || '.accordion-group',
        items = $(elem).find(contentClass);

    // toggle: false required to hide items on load
    items.collapse({ parent: elem, toggle: false });
    if (openItem > -1) items.eq(openItem).collapse('show');

    // if the array is dynamic, the collapse should be re-initiated to work properly
    var list = allBindings.get('foreach');
    if (ko.isObservable(list)) {
      list.subscribe(function() { 
        $(elem).find(contentClass).collapse({ parent: elem, toggle: false });              
      });
    }

    $(elem).on('click', handleClass, function() {
        $(elem).find(contentClass).collapse('hide');
        $(this).closest(itemClass).find(contentClass).collapse('show');
    });
  }
};

This binding takes 2 parameters (className for container, and optionally, an item to open on load), eg: bootstrapAccordion: {item: 'panel-group', openItem: 0}, and should be set on the same element which has a foreach binding. It assumes that collapsible sections have a collapse class, and the handles to toggle them have a data-toggle attribute.

See it in action here: http://jsfiddle.net/pkvn79h8/22/

like image 59
webketje Avatar answered Jan 05 '26 06:01

webketje



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!