Lesson Weekend

Today we’ll get started on our bigger project for the week - a blog. We’ll build this app together for the rest of the week to practice and demonstrate new concepts and slowly increase functionality! Great stuff.

One of the benefits of Handlebars is that we can actually execute logic in our templates! This makes our projects even more flexible and adaptive. Handlebars can loop through collections with its #each helper. It can also branch through information with #if and #else helpers. Both Handlebars looping and branching are similar to looping and branching in Java, but the syntax is a little different. We'll briefly review both in this lesson.

Then, in an upcoming lesson, we'll walk through integrating looping and branching in Handlebars templates together when we add this functionality to our Blog application. Let’s get to it.

Note on Accessing Data in Handlebars Templates

Before we begin, remember that anything you want access to in a Handlebars template must be added to the model HashMap in the Spark route that utilizes that template! If you want to use information to branch, or loop through a collection of information, this information must be added to model in the route that uses that template. You can’t make a sandwich without all the necessary ingredients!

Looping and POJOs

Let's pretend we have a Spark app that creates Rectangle objects out of user-provided measurements. Imagine that we created a data model that looked like this:

Rectangle.java
public class Rectangle {
   private int height;
   private int width;
   private static ArrayList<Rectangle> mInstances = new ArrayList<>();
   private Boolean shape;

   public Rectangle(int height, int width) {
       this.height = height;
       this.width = width;
       this.shape = isRectSquare();
       this.instances.add(this);
   }

   public int getHeight() {
       return height;
   }

   public int getWidth() {
       return width;
   }
   public static ArrayList<Rectangle> getAll() {
       return mInstances;
   }

   public boolean getShape(){
       return shape;
   }

   public boolean isRectSquare(){
       if (height == width){
        return true;
       }
       else {
           return false;
       }
   }

}

This simple class definition contains two properties that we get from our user, and then use to make an object through a constructor. We also have two getters to retrieve information about our instantiated objects.

Note: If the above is feeling confusing, please stop, and revisit relevant lessons on objects and encapsulation from week 1 before you proceed.

The above is called a POJO in Java slang, which stands for Plain Old Java Object. As you get more experience in Java, you’ll find that writing POJOs becomes second nature.

Let’s say we want our app to display a list of all Rectangles we have created within our application.

First, we need to include all Rectangles in our model HashMap. And, since looping is used to cycle through collections of data, like ArrayLists, we would create an ArrayList containing all Rectangle objects. Then, we would add that ArrayList to our model HashMap, just like we add information about our templates.

This is what the code in our App class would look like:

public class App {
   public static void main(String[] args) {

       get("/", (req, res) -> {
           //just for testing - make two new objects so we have something to retrieve
           Rectangle rectangle = new Rectangle(3,2);
           Rectangle otherRectangle = new Rectangle(12, 12);

           Map<String, ArrayList<Rectangle>> model = new HashMap<>();
           ArrayList myRectangleArrayList = Rectangle.getAll();
           model.put("myRectangles", myRectangleArrayList);
           return new ModelAndView(model, "index.hbs");
       }, new HandlebarsTemplateEngine());
   }
}

This adds a key-value pair to our model HashMap. The key is "myRectangles" and the value is myRectangleArrayList. This line of code would need to be included in whichever Spark route(s) use templates that require access to all Rectangles.

Therefore, when we say myRectangles in the Handlebars template, we fetch the value we store under the myRectangles key. That is, we fetch myRectangleArrayList. We can then use #each to cycle through the contents of this ArrayList, like so:

{{#each myRectangles}}
 <p><h3>Your rectangle's height is {{height}}</h3></p>
 <p><h3>Your rectangle's width is {{width}}</h3></p>
{{/each}}

But what is this magic? Why can we simply write {{height}} instead of using our getter? It’s actually simple - Handlebars is smart enough to know when it is dealing with a POJO - and as long as you follow standard naming conventions, it knows exactly what you are expecting it to do! Awesome timesaver.

Before we move on, let's consider a second example. Pretend we've created a blog using Spark. Each blog post is a Post object. Post is going to be a POJO just like Rectangle was. If we wanted to display multiple blog posts that we have created using a loop, we could do the following in our template:

{{#each allMyBlogPosts}}
  <div class="well">
    {{name}}
    <hr>
    <small>Posted at {{time}}</small>
  </div>
{{/each}}

Then, in whatever route renders that template, we would just need to make sure that an ArrayList containing all blog posts was stored in the model HashMap under the key used in the template (that is, allMyBlogPosts).

As you can see in the examples above, the Handlebars {{#each}} loop works similar to a Java for each loop:

  • We don’t explicitly need to state the singular, temporary object in the collection like we need to do in Java, Handlebars is smart enough to abstract this.
  • Then, we specify the collection provided in our model, myRectangles or allMyBlogPosts
  • Finally, between {{#each}} and {{/each}} we provide the code that we want to run on each member of the collection.

Branching

We can also use if and else statements directly in Handlebars templates, too! They look something like this:

{{#if username}}
  Welcome {{username}}
{{else}}
  It doesn't look like you're logged in!
{{/if}}

As you can see, this is similar to the manner we use branching in Java. In the example above, if model contains a key called username, Handlebars will use it to fetch that key's corresponding value, and display a welcome message to the user. If there is no key called username, Handlebars will display the message It doesn't look like you're logged in!.

Much like Java, we can even combine looping and branching:

{{#each myRectangles}}
 <p><h3>Your rectangle's height is {{height}}</h3></p>
 <p><h3>Your rectangle's width is {{width}}</h3></p>

   {{#if shape}}
     <p><h3>Your rectangle is a square!</h3></p>
  {{else}}
     <p><h3>Your rectangle isn't a square!</h3></p>
  {{/if}}
{{/each}}

Here, we cycle through all Rectangle entries in an ArrayList called myRectangles. We display each Rectangle's height and width. Then, if the Rectangles shape property is true, the template also displays "Your rectangle is a square!". If it is not true, it displays "Your rectangle isn't a square!".

We'll walk through a hands-on example of using Handlebars looping and branching in upcoming lessons to display information in our blog. If you are curious what else Handlebars can do, we recommend browsing through this list of Handlebars helpers.


Example GitHub Repo for Rectangle Code