Search code examples
javascriptjsontemplate-enginemustache

Mustache flat array into nested elements


I'm isolating the problem and creating a simple case in here. It's basically an Image Accordion (CSS3 animation based) and in order to use this plugin my HTML structure has to be nested as shown below. In their samples the HTML was hardcoded - I need to use JSON data to generate the output.

Suppose an object like this,

[{imageurl:"link1"}, {imageurl: "link2"}, {imageurl: "link3"}]

I want the output to be

<figure>
    <img src="link1" />
    <figure>
        <img src="link2" />
        <figure>
           <img src="link3 />
        </figure>
    </figure>
 </figure>

I'm trying to think what kind of template can help to achieve this?


Solution

  • Because the Mustache language is "logic-less", views that require logic or complex nesting may need to be broken up into different views and included via partials or created outside of Mustache and then reinserted.

    Anyhow, one way to produce the view that you desire is by reversing the array and working inside-out to nest the figures (jsfiddle):

    <!-- If your desired output is so:
    <figure>
        <img src="link1">
        <figure>
            <img src="link2">
            <figure>
               <img src="link3">
            </figure>
        </figure>
     </figure>
    -->
    <script id="entriesTemplate" type="text/x-mustache-template">
        <figure>
            <img src="{{{imageurl}}}">
            {{{figure}}}
        </figure>
    </script>
    

    You can then nest the above template via a small snippet of JS:

    var figs = [
        {url: "http://placehold.it/10x10"},
        {url: "http://placehold.it/10x10"},
        {url: "http://placehold.it/10x10"}
    ];
    
    var ft = document.querySelector('#entriesTemplate').innerText.trim();
    Mustache.parse(ft);
    
    console.log(
        figs.slice(0) // Make a copy of the array as the next call to `.reverse()` will work in situ
            .reverse()
            .reduce(function (previous, current, index, array) {
                return fig = Mustache.render(ft, {
                    imageurl: current.url,
                    figure: previous
                });
            }, undefined)
    );
    

    When you pass undefined into the first template render, Mustache will produce no nested figure. Each subsequent render you are passing the output from the past to the outer figure etc. Instead of logging the value, you can then insert the chunk of HTML as need.