Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handlebars nested 'each' syntax - not iterating over each element

I am brand new at this Javascript/JSON/Handlebars thing, and I am having trouble getting a JSON object, with two nested levels, to work in a Handlebars template.

I have validated the JSON object with JSONLint, so it is valid JSON code, but I don't know if I have the correct JSON format to make the template work correctly. :) (I am building the JSON manually in another system.) Or perhaps it is the syntax of the template that I have incorrect. That's what I hope to find out...

The short description: this object is a table of contents. I have Chapters and then the movies within each Chapter. So the Movies are nested elements of each Chapter element.

I want HTML output similar to:

Chapter1:  ChapterName
       Movie1: MovieName
       Movie2: MovieName
Chapter2:  Chaptername
       Movie1: MovieName
       Movie2: MovieName
       Movie3: MovieName

I seem to end up with only 1 instance of the data (the last element in my JSON object), or I get nothing at all. (Depends on which little tweak or version I try.) The browser console doesn't show any errors.

Here's all the code that I have been trying to use so far (scripts, HTML, template, etc):

<!DOCTYPE html>
<html>
<head>  <meta charset="UTF-8">
    <title>Handlebars Demo</title>
    <!-- dependant files -->
    <script src="Handlebars.js"></script>
</head>

<!-- template -->
<script id="template2" type="text/x-handlebars-template">
    <div>Chapter stuff:</div>
    <ul>{{#each Chapter}}
        <ol>{{@index}} {{ChapterName}}
        {{#each movies}}
            <li>Movie ID:{{movieIDnum}}</li>
        {{/each}}
        </ol>
        {{/each}}
    </ul>
</script>


<body><div id="main"></div></body>

<script>
    var source = document.getElementById('template2').innerHTML;
    var template = Handlebars.compile(source);
    var data = {
        "Chapter" : {
                "ChapterName" : "Introduction",
                "chapterNum" : "1",
                "movies" : [
                        {
                        "movieIDnum" : "16244028",
                        "movieName" : "Update Test Movie 0",
                        "movieFileName" : "Test0.mov",
                        "moviePositionInChapter" : "1"
                        }
                ]
        },

        "Chapter" : {
            "ChapterName" : "Welcome",
            "chapterNum" : "2",
            "movies" : [
                    {
                    "movieIDnum" : " 17322365",
                    "movieName" : "Update Test movie 1",
                    "movieFileName" : "Test1.mov",
                    "moviePositionInChapter" : "1"
                    },
                    {
                    "movieIDnum" : " 17326267",
                    "movieName" : "Update Test movie 3",
                    "movieFileName" : "Test3.mov",
                    "moviePositionInChapter" : "2"
                    }
            ]
        },

        "Chapter" : {
            "ChapterName" : "The new Interface",
            "chapterNum" : "2",
            "movies" : [
                {
                "movieIDnum" : " 1732123476",
                "movieName" : "Update Test movie 12",
                "movieFileName" : "Test12.mov",
                "moviePositionInChapter" : "1"
                },
                {
                "movieIDnum" : " 173262373",
                "movieName" : "Update Test movie 9",
                "movieFileName" : "Test9.mov",
                "moviePositionInChapter" : "2"
                },
                {
                "movieIDnum" : " 173273474",
                "movieName" : "Update Test movie 10",
                "movieFileName" : "Test10.mov",
                "moviePositionInChapter" : "3"
                }
            ]
        },

        "Chapter" : {
            "ChapterName" : "What is an Update?",
            "chapterNum" : "4",
            "movies" : [
                {
                "movieIDnum" : " 177342131",
                "chapterNum" : "4",
                "chapterName" : "What is an Update?",
                "movieName" : "Test movie again",
                "movieFileName" : "Test13.mov",
                "moviePositionInChapter" : "1"
                }
                ]
                },
        "Chapter" : {
            "ChapterName" : "Editing",
            "chapterNum" : "5",
            "movies" : [
                {
                "movieIDnum" : " 173290878",
                "movieName" : "Update Test movie 14",
                "movieFileName" : "Test14mov",
                "moviePositionInChapter" : "1"
                },
                {
                "movieIDnum" : " 177344914",
                "movieName" : " Movie 15 Test",
                "movieFileName" : "Test233.mov",
                "moviePositionInChapter" : "2"
                }
            ]
        }

    }

    var result = template(data);
    document.write(result);

</script>
</html>

I would like to know why THIS doesn't work, not just a "Here's how to solve your problem using 4 other different things in a totally different format". It is my understanding that this SHOULD be workable with the tools I am trying to use. I would like to better understand these tools and learn from the process, not just get a solution. (You know, teach a man to fish... :) )

Thanks, J

like image 897
Cronk Avatar asked Jun 19 '13 17:06

Cronk


2 Answers

There are a couple of changes to suggest, first you shouldn't write an object with several keys { Chapter:, Chapter:, etc...}

The other suggestion is to review the way Handlebars works, it doesn't need each in every case. Hope it clarifies.

Try these changes:

<!DOCTYPE html>
<html>
<head>  <meta charset="UTF-8">
    <title>Handlebars Demo</title>
    <!-- dependant files -->
    <script src="handlebars.js"></script>
</head>

<!-- template -->
<script id="template2" type="text/x-handlebars-template">
    <div>Chapter stuff:</div>
    <ul>{{#Chapters}}
        <ol>{{@index}} {{ChapterName}}
        {{#movies}}
            <li>Movie ID:{{movieIDnum}}</li>
        {{/movies}}
        </ol>
        {{/Chapters}}
    </ul>
</script>


<body><div id="main"></div></body>

<script>
    var source = document.getElementById('template2').innerHTML;
    var template = Handlebars.compile(source);
    var data = [
        {
                "ChapterName" : "Introduction",
                "chapterNum" : "1",
                "movies" : [
                        {
                        "movieIDnum" : "16244028",
                        "movieName" : "Update Test Movie 0",
                        "movieFileName" : "Test0.mov",
                        "moviePositionInChapter" : "1"
                        }
                ]
        },
        {
            "ChapterName" : "Welcome",
            "chapterNum" : "2",
            "movies" : [
                    {
                    "movieIDnum" : " 17322365",
                    "movieName" : "Update Test movie 1",
                    "movieFileName" : "Test1.mov",
                    "moviePositionInChapter" : "1"
                    },
                    {
                    "movieIDnum" : " 17326267",
                    "movieName" : "Update Test movie 3",
                    "movieFileName" : "Test3.mov",
                    "moviePositionInChapter" : "2"
                    }
            ]
        },
        {
            "ChapterName" : "The new Interface",
            "chapterNum" : "2",
            "movies" : [
                {
                "movieIDnum" : " 1732123476",
                "movieName" : "Update Test movie 12",
                "movieFileName" : "Test12.mov",
                "moviePositionInChapter" : "1"
                },
                {
                "movieIDnum" : " 173262373",
                "movieName" : "Update Test movie 9",
                "movieFileName" : "Test9.mov",
                "moviePositionInChapter" : "2"
                },
                {
                "movieIDnum" : " 173273474",
                "movieName" : "Update Test movie 10",
                "movieFileName" : "Test10.mov",
                "moviePositionInChapter" : "3"
                }
            ]
        },
        {
            "ChapterName" : "What is an Update?",
            "chapterNum" : "4",
            "movies" : [
                {
                "movieIDnum" : " 177342131",
                "chapterNum" : "4",
                "chapterName" : "What is an Update?",
                "movieName" : "Test movie again",
                "movieFileName" : "Test13.mov",
                "moviePositionInChapter" : "1"
                }
                ]
        },
        {
            "ChapterName" : "Editing",
            "chapterNum" : "5",
            "movies" : [
                {
                "movieIDnum" : " 173290878",
                "movieName" : "Update Test movie 14",
                "movieFileName" : "Test14mov",
                "moviePositionInChapter" : "1"
                },
                {
                "movieIDnum" : " 177344914",
                "movieName" : " Movie 15 Test",
                "movieFileName" : "Test233.mov",
                "moviePositionInChapter" : "2"
                }
            ]
        }
    ];

    var result = template({Chapters: data});
    document.write(result);

</script>
</html>
like image 132
pdjota Avatar answered Oct 29 '22 12:10

pdjota


pdjota is right on. This answer is just to show how to use the each block helper if you wanted.

Like pdjota wisely pointed out, you are overwriting the elements of your object because they all have the same key i.e., "Chapter". So turn data into an array of objects instead of an object of objects that have the same key.

If you do that, you can still pass data into template(). What you were missing was how to reference properties of objects used in the each block. That is done with this. For example, if you are in the movies iterator, properties of the movies object are referenced like this: this.movieIDnum, this.movieName, etc.

<!DOCTYPE html>
<html>
<head>  
    <meta charset="UTF-8">
    <title>Handlebars Demo</title>
    <!-- dependant files -->
    <script src="Handlebars.js"></script>
</head>

<!-- template -->
<script id="template2" type="text/x-handlebars-template">
    <div>Chapter stuff:</div>
    <ul>
        {{#each this}}
            <ol>Chapter{{this.chapterNum}}: {{this.ChapterName}}
                {{#each movies}}
                    <li>Movie{{this.moviePositionInChapter}}: {{this.movieName}}</li>
                {{/each}}
            </ol>
        {{/each}}
    </ul>
</script>


<body><div id="main"></div></body>

<script>
    var source = document.getElementById('template2').innerHTML;
    var template = Handlebars.compile(source);
    var data = [
        {
                "ChapterName" : "Introduction",
                "chapterNum" : "1",
                "movies" : [
                        {
                        "movieIDnum" : "16244028",
                        "movieName" : "Update Test Movie 0",
                        "movieFileName" : "Test0.mov",
                        "moviePositionInChapter" : "1"
                        }
                ]
        },
        {
            "ChapterName" : "Welcome",
            "chapterNum" : "2",
            "movies" : [
                    {
                    "movieIDnum" : " 17322365",
                    "movieName" : "Update Test movie 1",
                    "movieFileName" : "Test1.mov",
                    "moviePositionInChapter" : "1"
                    },
                    {
                    "movieIDnum" : " 17326267",
                    "movieName" : "Update Test movie 3",
                    "movieFileName" : "Test3.mov",
                    "moviePositionInChapter" : "2"
                    }
            ]
        },
        {
            "ChapterName" : "The new Interface",
            "chapterNum" : "2",
            "movies" : [
                {
                "movieIDnum" : " 1732123476",
                "movieName" : "Update Test movie 12",
                "movieFileName" : "Test12.mov",
                "moviePositionInChapter" : "1"
                },
                {
                "movieIDnum" : " 173262373",
                "movieName" : "Update Test movie 9",
                "movieFileName" : "Test9.mov",
                "moviePositionInChapter" : "2"
                },
                {
                "movieIDnum" : " 173273474",
                "movieName" : "Update Test movie 10",
                "movieFileName" : "Test10.mov",
                "moviePositionInChapter" : "3"
                }
            ]
        },
        {
            "ChapterName" : "What is an Update?",
            "chapterNum" : "4",
            "movies" : [
                {
                "movieIDnum" : " 177342131",
                "chapterNum" : "4",
                "chapterName" : "What is an Update?",
                "movieName" : "Test movie again",
                "movieFileName" : "Test13.mov",
                "moviePositionInChapter" : "1"
                }
                ]
        },
        {
            "ChapterName" : "Editing",
            "chapterNum" : "5",
            "movies" : [
                {
                "movieIDnum" : " 173290878",
                "movieName" : "Update Test movie 14",
                "movieFileName" : "Test14mov",
                "moviePositionInChapter" : "1"
                },
                {
                "movieIDnum" : " 177344914",
                "movieName" : " Movie 15 Test",
                "movieFileName" : "Test233.mov",
                "moviePositionInChapter" : "2"
                }
            ]
        }
    ];

    var result = template(data);
    document.write(result);

</script>
</html>
like image 30
salsbury Avatar answered Oct 29 '22 12:10

salsbury