19

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

Cronk
  • 453
  • 1
  • 6
  • 16

2 Answers2

36

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>
pdjota
  • 3,163
  • 2
  • 23
  • 33
  • Can you clarify what you mean by Handlebars "doesn't have eachs"? http://handlebarsjs.com/#iteration – salsbury Jun 19 '13 at 18:53
  • Should have said need, thanks for the clarification. I usually prefer to use #collection instead of iterators. – pdjota Jun 19 '13 at 19:14
  • Ah, makes sense. I think that I had thrown the 'Chapters' object in there in order to give the 'each' function something to grab onto. I.e. "for each object-labeled-'Chapter'...". Removed that same-label thing as you suggested, and modified my template and it is working now. Thanks! – Cronk Jun 19 '13 at 20:17
  • You did a very nice job of answering, staying with my request of figuring out this method. Sorry, but I can't give your answer props, as I don't have the Rep yet. – Cronk Jun 19 '13 at 20:26
  • 1
    Shouldn't ' first you shouldn't write an object with several keys' be ' first you shouldn't write an object with several **identical** keys'? – mikemaccana Apr 27 '15 at 14:13
  • I have tried all of the suggestions but still my inner loop is not working at all. The control is not entering the inner loop. – Farrukh Sarmad Apr 10 '19 at 05:28
16

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>
salsbury
  • 2,777
  • 1
  • 19
  • 22
  • 1
    Tayler, thanks for posting. Reading through your, and PDJota's, responses clarified things for me. Yours had the added bonus of explaining why I was only getting data for the last element I was defining: each element was being overwritten by the one defined next. There must be some default effect going on with Handlebars, though, as I was able to use a template without 'This' for most things (see next comment, even though it doesn't format very well): – Cronk Jun 19 '13 at 20:18
  • – Cronk Jun 19 '13 at 20:21
  • You did a very nice job of answering, staying with my request of figuring out this method. Sorry, but I can't give your answer props, as I don't have the Rep yet. – Cronk Jun 19 '13 at 20:26