RactiveJS Nested List Expand/Collapse Children
-
02-01-2020 - |
Question
I've been playing with ractive.js and am trying to get a sense of what it is capable of. I have an example that is fairly common, a view with some internal state logic that I'd like to cleanly keep track of. After going through the tutorials this seems like something Ractive would be really good at, but I'm having a hard time figuring this out.
Update: I've revised my test case below based on feedback in the first answer I got, to clarify the exact problem I've been having. You can see the complete example here: http://jsfiddle.net/e7Mjm/1/
First, I have a ractive template:
<div id="toc-view"></div>
<script id="ractive-toc" type="text/ractive">
<ul>
{{#chapters}}
<li class="{{type}}">
<span class="ordinal">{{ordinalize(ordinal)}}</span>
<a data-id="{{element_id}}">{{title}}</a>
{{#(sections.length > 0)}}
<a class="{{open ? 'expand open' : 'expand'}}" on-click="toggleSections"></a>
{{#open}}
<ul class="{{open ? 'sections open' : 'sections'}}">
{{#sections}}{{>section}}{{/sections}}
</ul>
{{/open}}
{{/()}}
</li>
{{/chapters}}
</ul>
<!-- {{>section}} -->
<li class="{{type}}">
<span class="ordinal">{{ordinalize(ordinal)}}</span>
<a data-id="{{element_id}}">{{title}}</a>
</li>
<!-- {{/section}} -->
</script>
I have some simple CSS styles to format this:
ul { list-style: none; padding: 0; margin: 0}
ul.sections { padding-left: 20px; }
a.expand { color: red; }
a.expand:before { content: "+"; }
a.expand.open { color: blue; }
a.expand.open:before { content: "-"; }
And the following Javascript to make it all work:
data = [
{
id: "smith-about",
title: "About this book",
type: "front-matter"
},
{
id: "smith-preface",
title: "Preface",
type: "front-matter"
},
{
id: "smith-ch01",
title: "Intro to Biology",
ordinal: "1",
type: "chapter",
sections: [
{
id: "smith-ch01-s01",
title: "What is biology?",
ordinal: "1.1",
type: "section"
},
{
id: "smith-ch01-s02",
title: "What is a biologist?",
ordinal: "1.2",
type: "section"
},
{
id: "smith-ch01-s03",
title: "So you want to be a biologist?",
ordinal: "1.3",
type: "section"
}
]
},
{
id: "smith-ch02",
title: "Applied Biology",
ordinal: "2",
type: "chapter",
sections: [
{
id: "smith-ch02-s01",
title: "Biology in the lab",
ordinal: "2.1",
type: "section"
},
{
id: "smith-ch02-s02",
title: "Biology in the field",
ordinal: "2.2",
type: "section"
},
{
id: "smith-ch02-s03",
title: "Biology in the classroom",
ordinal: "2.3",
type: "section"
}
]
}
]
ractive = new Ractive({
el: 'toc-view',
template: '#ractive-toc',
data: {
chapters: data,
ordinalize: function(ordinal) {
return ordinal ? ordinal + "." : "▸";
}
}
});
ractive.on('toggleSections', function(event) {
event.context.open = !event.context.open;
this.update();
});
If you try out the JS Fiddle you'll see the template renders correctly but the interaction behavior isn't quite right: If you click on a.expand
it does open that section, but it also changes the class of all the other a.expand
icons, not just the one that was clicked.
This is the real issue I've been having, in the ractive event binding there doesn't seem to be a very good way for me to define an interaction that affects only the particular data object the user is interacting with, instead the interaction seems to affect all the data.
Any insight into how to scope this correctly?
Solution
Here is a example http://jsfiddle.net/e7Mjm/
I changed to
{{#open}}{{#sections}}{{>section}}{{/sections}}{{/open}}
Now it will render "on demand" when you open
and you can use event.context
. Just do
event.context.open = !event.context.open;
this.update();