2

All works except for collapsing the Bootstrap collapsible outside of this example on stackoverflow, I would appreciate some suggestions.

When any opened item is clicked to collapse, its class changes in the following sequence: panel-collapse collapse in > panel-collapse collapsing > panel-collapse collapse in. In the right sequence it should result in panel-collapse collapse collapsed. This is not yet solved. The problem is that collapse is called twice.

What would solution for this so I can use it as part of template?

UPDATE: Only one panel-group to be opened exclusively. This is now solved by removing the data-parent, it now allows multiple panels to be opened simultaneously.

I added logging in bootstrap.js (lines 608-628):

  // COLLAPSE DATA-API
  // =================

  $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {
    var $this   = $(this), href
    var target  = $this.attr('data-target')
        || e.preventDefault()
        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
    var $target = $(target)
    var data    = $target.data('bs.collapse')
    var option  = data ? 'toggle' : $this.data()
    var parent  = $this.attr('data-parent')
    var $parent = parent && $(parent)

    console.log("A",data);
    if (!data || !data.transitioning) {console.log("B1");
      if ($parent) { console.log("B2");
          $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
      }
      console.log("B3",$target.hasClass('in'));
      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
    }
    console.log("C", option);
    $target.collapse(option)
    console.log("D");
  })

The resulting log:

a) When collapse is pressed first time, it looks messed up

bootstrap.js:622 A undefined
bootstrap.js:623 B1
bootstrap.js:624 B2
bootstrap.js:627 B3 false
bootstrap.js:630 C Object {toggle: "collapse", bind: "text:displayLabel, attr:{href:idhash}"}
bootstrap.js:632 D
bootstrap.js:622 A Collapse {$element: jQuery.fn.jQuery.init[1], options: Object, transitioning: 1}
bootstrap.js:630 C toggle
bootstrap.js:632 D

b) When collapse is pressed any other time

bootstrap.js:622 A Collapse {$element: x.fn.x.init[1], options: Object, transitioning: 0}
bootstrap.js:623 B1
bootstrap.js:624 B2
bootstrap.js:627 B3 true
bootstrap.js:630 C toggle
bootstrap.js:632 D
bootstrap.js:622 A Collapse {$element: jQuery.fn.jQuery.init[1], options: Object, transitioning: 1}
bootstrap.js:630 C toggle
bootstrap.js:632 D

The correct output should be:

a) When collapse is pressed first time

bootstrap.js:622 A undefined
bootstrap.js:623 B1
bootstrap.js:624 B2
bootstrap.js:627 B3 false
bootstrap.js:630 C Object {toggle: "collapse", bind: "text:displayLabel, attr:{href:idhash}"}
bootstrap.js:632 D

b) When collapse is pressed any other time

bootstrap.js:622 A Collapse {$element: jQuery.fn.jQuery.init[1], options: Object, transitioning: 0}
bootstrap.js:623 B1
bootstrap.js:624 B2
bootstrap.js:627 B3 (true or false)
bootstrap.js:630 C toggle
bootstrap.js:632 D

The full code:

var confItems = {};
var childrenLength = 2;
confItems["children"] = new Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
  confItems.children[i] = {};
  confItems.children[i]["idhash"] = "#col-" + (i + 1);
  confItems.children[i]["id"] = "col-" + (i + 1);
  confItems.children[i]["displayLabel"] = "Item " + (i + 1);
  confItems.children[i]["children"] = new Array(childrenLength);
  for (var j = 0; j < childrenLength; j++) {
    confItems.children[i].children[j] = {};
    confItems.children[i]["idhash"] = "#col-" + (i + 1) + "-" + (j + 1);
    confItems.children[i]["id"] = "col-" + (i + 1) + "-" + (j + 1);
    confItems.children[i].children[j]["displayLabel"] = "Item " + (i + 1) + "." + (j + 1);
    confItems.children[i].children[j]["children"] = new Array(0);
  }
}
var viewModel = function() {
  this.tree = ko.observable(confItems);
};
ko.applyBindings(new viewModel());
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap-theme.min.css">
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="template: {name:'largeTemplate', data: tree}"></div>
<script id='largeTemplate' type='text/html'>
  <div class="panel-group">
 <div id="accordion" class="panel panel-default" data-bind="foreach: children">
      <div class="panel-heading">
        <h4 class="panel-title">
   <a data-toggle="collapse" data-parent="#accordion" data-bind="text:displayLabel, attr:{href:idhash}">Lorem</a>
  </h4>
      </div>
      <div data-bind="attr:{id:id}" class="panel-collapse collapse">
        <div class="panel-body" data-bind="foreach: children">
          <div class="panel-heading">
            <a data-bind="text:displayLabel">Donec</a>
          </div>
        </div>
      </div>
    </div>
  </div>
</script>
Peter G.
  • 7,816
  • 20
  • 80
  • 154

1 Answers1

0

A cheap solution would be to modify bootstrap.js and remove the '[data-toggle=collapse]'.

I'm still searching for the cause of this and what is happening, so I don't have to resort to modifying bootstrap.js. Even after the modification, the collapse gets called twice.

To that I didn't find the answer yet.

  // COLLAPSE DATA-API
  // =================

  $(document).on('click.bs.collapse.data-api', function (e) {
    var $this   = $(this), href
    var target  = $this.attr('data-target')
        || e.preventDefault()
        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
    var $target = $(target)
    var data    = $target.data('bs.collapse')
    var option  = data ? 'toggle' : $this.data()
    var parent  = $this.attr('data-parent')
    var $parent = parent && $(parent)

    console.log("A",data);
    if (!data || !data.transitioning) {console.log("B1");
      if ($parent) { console.log("B2");
          $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
      }
      console.log("B3",$target.hasClass('in'));
      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
    }
    console.log("C", option);
    $target.collapse(option)
    console.log("D");
  })

Updated log:

bootstrap.js:622 A null
bootstrap.js:623 B1
bootstrap.js:627 B3 false
bootstrap.js:630 C Object {}
bootstrap.js:632 D
bootstrap.js:622 A null
bootstrap.js:623 B1
bootstrap.js:627 B3 false
bootstrap.js:630 C Object {keycount: 0}
bootstrap.js:632 D
Peter G.
  • 7,816
  • 20
  • 80
  • 154