Simple Routing with DukeScript

If you’re using DukeScript for Web based applications, you might be interested in allowing users to use bookmarks and history to navigate your app. Here’s a very minimal example showing how to add that to an application.

First I created a new application named “routing” using the DukeScript wizard. I selected “Run in a Browser” as the target platform and the “Knockout 4 Java Maven Archetype” (without sample code).

This creates three Maven projects. We’ll first have a look at the “routing JavaScript Libraries”. In order to use knockout for the routing, we need to create a custom binding. This will allow us later to track the current active subpage in our viewmodel. First I deleted Dialog.java and it’s unit Test, because we won’t need that. Next I created a JavaScript file (‘com/dukescript/demo/routing/js/registerRouter.js’) in the resources directory:

window['ko']['bindingHandlers']['route'] = {
    'init': function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var callback = valueAccessor();
        window['onhashchange'] = function () {
            var url = location.hash.slice(2);
            callback(url);
        };
    }
};

This registers a new binding called “route”. We’ll use that in our html-page. In order to execute the JavaScript at the correct time I’ll also create a Java class (com/dukescript/demo/routing/js/Router.java):

@JavaScriptResource(value = "registerRouter.js")
public final class Router {

    private Router() {
    }

    @JavaScriptBody(
            args = {}, body
            = ""
    )
    public static native String registerBinding();
}

The JS-File will be loaded when I first call a method on this class. That’s it for the Custom Binding, lets forget about our short encounter with JavaScript and wash our hands in pure Java!

In the “routing General Client Code” I added a Property to the DataModel to track the route:

@Model(className = "Data", targetId="", properties = {
    @Property (name = "page", type = String.class)
})
final class DataModel {
    private static Data ui;
    //....
}

Also in the onPageLoad method I registered our new binding:

@Model(className = "Data", targetId="", properties = {
    @Property (name = "page", type = String.class)
})
final class DataModel {
    private static Data ui;
    /**
     * Called when the page is ready.
     */
    static void onPageLoad() throws Exception {
        ui = new Data("page1");
        Models.toRaw(ui);
        Router.registerBinding();
        ui.applyBindings();
    }
}

The call Models.toRaw(ui) initializes knockout, we need to do that before registering the binding. The View Model is ready. Now we can code the view (index.html):

<!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>

        <script type="text/html" id="home">
            <h3 >Home</h3>
            
        </script>
        <script type="text/html" id="page1">
            <h3 >Page 1</h3>
            
        </script>
        <script type="text/html" id="page2">
            <h3 >Page 2</h3>
            
        </script>
        
       
        <!-- ${browser.bootstrap} -->

    </body>
</html>

In the “body”-Element I bind the page property of DataModel with the route binding. This is how the changes of the location hash are synchronized with our model. Now we can use that property to control what page is displayed. That’s a nice usecase for knockout templates. We can model each page as a separate template and reference them via their id.

A “div”-Element will display a template depending on the page-Property. The knockout directive for this is ‘data-bind=”template: { name: page }”’.

The last step is to define the templates. Our custom binding removes the leading “#/” from the location hash, so we can use a simple string as the id or name of the template. I have defined 3 templates. Each of them displays a different heading.

That’s it. You can now navigate the page via the bookmarkable links on top of the page.

If you want to play with this, as always the demo project is available on github.

Have fun with DukeScript!