Switching Views

For a Swing or JavaFX developer switching between views in Single Page Applications (SPAs) might be a little strange at first. “Single Page” seems to imply, that everything needs to be in one large HTML-File. But that would be ugly, especially as applications tend to grow over time. Let’s have a look how we can structure an application with separate views. Let’s start with how to switch between different views.

Switching Views with in-place templates

The easiest way to switch between two views is to hide or show either view only when a condition is met. As an example let’s take the viewmodel from this demo application:

https://github.com/dukescript/design-experiment

@Model(className = "TaskListViewModel", properties = {
    @Property(name = "input", type = String.class),
    @Property(name = "tasks", type = Task.class, array = true),
    @Property(name = "editing", type = Task.class)
}, targetId = "body")

With this model we can switch the view when someone is editing a Task. Here’s the code that switches the view for the editing row in a list:

<ul class="todo-list" >
    <!-- ko foreach: tasks -->    
    <li>
        <!-- ko ifnot: $root.editing()===$data -->
        <input type="checkbox" name="" class="toggle" data-bind="attr:{checked: complete}"/>
        <span data-bind="text: title"></span>
        <span class="btns">
            <img src="images/icon-edit.png" alt="" data-bind="click: $root.editTask"  />
            <img src="images/icon-delete.png" alt="" data-bind="click: $root.deleteTask" />                        
        </span>
        <!-- /ko -->  
        <!-- ko if: $root.editing()===$data -->
        <form data-bind="submit: $root.stopEditing">
            <input type="text" data-bind="textInput: title"/>
        </form>
        <!-- /ko -->
    </li>
    <!-- /ko -->
    <!-- code omitted -->
</ul>

If the current item ($data) in the foreach loop is the editing Task of the viewmodel, a textfield is displayed. We have defined two different templates for displaying a task and depending on the condition “$root.editing()===$data” we display one of them. This is called in-place templates, as the templates are defined where they are used. This approach is good for small templates, and it’s very easy to understand what is happening. But this approach has limitations:

If the templates are getting bigger, readability suffers. Also there’s no way to reuse an in-place template. That’s when we start to use the template binding.

Switching Views with Template Binding

First we simply move the in-place templates to a script tag and give them an id. At the original location we place a tag with a template binding. The name parameter references the template id:

<ul class="todo-list" >
    <!-- ko foreach: tasks -->    
    <li>
        <!-- ko template: { name:'showtask', if: !($root.editing()===$data)} -->
        <!-- /ko -->
        <!-- ko template: { name:'edittask', if: $root.editing()===$data} -->
        <!-- /ko -->
    </li>
    <!-- /ko -->
    <!-- code omitted -->
</ul>

<script id="showtask">
  <input type="checkbox" name="" class="toggle" data-bind="attr:{checked: complete}"/>
  <span data-bind="text: title"></span>
  <span class="btns">
      <img src="images/icon-edit.png" alt="" data-bind="click: $root.editTask"  />
      <img src="images/icon-delete.png" alt="" data-bind="click: $root.deleteTask" />                        
  </span>
</script>    
<script id="edittask">
  <form data-bind="submit: $root.stopEditing">
    <input type="text" data-bind="textInput: title"/>
  </form>
</script>

This increases readability for large templates and allows us to reuse the same template at different locations. As an example we might use the same template for editing an existing item and for adding a new one.

If the application grows larger, you might want to put the templates into an external file. This keeps the html files small and allows you to further structure the application. There are several ways of doing this. We suggest you try our dynamic templates.

Dynamic Templates

A while ago we’ve announced dynamic templates, a small helper project that allows you to register templates at runtime. The immediate benefit of this project is, that you can load templates lazily.

https://github.com/dukescript/dynamic-templates

The github repository contains an example app using the API. To register a template you will make a call to a Java API:

Closeable templ = TemplateRegistration.registerTemplate("a", "a.html");

The template will only be loaded from an external file, when you first use it in a template binding.

Routing

Switching templates is only one part of the story. If you have a DukeScript based webapp, you also want to allow users to use bookmarks and history to navigate your app. In this blog entry we’ve described how you can do some simple routing with a DataModel that holds the current page, and a special “route” binding.

The DataModel looks like this:

@Model(className = "Data", targetId="", properties = {
    @Property (name = "page", type = String.class)
})
final class DataModel {
// code omitted ...
}

If you combine that approach with dynamic templates you can store your templates in separate html files and control which page is active through the page property of your datamodel:

<!DOCTYPE html>
<html>
    <head>
        <title>Routing Demo</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body data-bind="route: page">
        <ul>
            <li><a href="#/home">Home</a></li>
            <li><a href="#/page1">Page 1</a></li>
            <li><a href="#/page2">Page 2</a></li>
        </ul>
        <div data-bind="template: { name: page }"></div>  
        <!-- ${browser.bootstrap} -->
    </body>
</html>

Please note how the template binding’s name attribute is bound to the page property of the DataModel.

I hope this gives you some ideas how you can use templates and switch between views.