I am using Knockout.js to populate a set of HTML5 <details>
elements. Here is the structure:
<div class="items" data-bind="foreach: Playlists"> <details class="playlist-details" data-bind="attr: {id: 'playlist-details-' + $index(), open: isOpen}"> <summary> <span data-bind="text: name"></span> - <span data-bind="text: count"></span> item(s) <div class="pull-right"> <button data-bind="click: $parent.play, css: {disabled: count() == 0}, attr: {title: playbtn_title}" class="btn"><i class="icon-play"></i> Play</button> <button data-bind="click: $parent.deleteList" class="btn btn-danger"><i class="icon-trash"></i> Delete</button> </div> </summary> <div class="list" data-bind="with: items" style="padding-top: 2px;"> ... </div> </details> </div>
The data in the ViewModel looks something like this:
var VM = { Playlists: [ { name: "My Playlist1", count: 3, items: [<LIST OF SONG ID'S>], playbtn_title: "Play this playlist", isOpen: true }, { name: "My Playlist2", count: 5, items: [<LIST OF SONG ID'S>], playbtn_title: "Play this playlist", isOpen: null }, { name: "My Playlist3", count: 0, items: [], playbtn_title: "You need to add items to this list before you can play it!", isOpen: null } ] };
I have added the ability to remember the open or closed state of the details view using the isOpen
property of the ViewModel and an attr
binding (As originally described here).
However, when I click the <summary>
to expand the details, the ViewModel does not get updated – unlike value
bindings, attr
bindings aren’t two-way.
How can I get this binding to update when the attribute value changes?
I know that the browser triggers a DOMSubtreeModified
event when the element is opened or closed, but I;m not sure what I would put there – several things I have tried (including .notifySubscribers()
, if (list.open()) ...
, etc.) cause looping, where the property being changed makes the event trigger again, which changes the property again, which triggers the event again, etc.
Advertisement
Answer
The way that I found in the end that works is simply to have the DOMSubtreeModified
“manually” update the value:
$(document).on('DOMSubtreeModified', 'details.playlist-details', function(e) { var list = ko.dataFor(this); list.open(this.getAttribute('open')); });
(Somehow, this does not cause the looping that more-complex constructs I tried had caused.)