Using the Knockout API

In the 'Getting started' guide, we've created an application and had a first look how it works. Now let's have a closer look at the Knockout APIs. We've chosen Knockout for our databinding, because it's small and it does exactly what we need for DukeScript. Other Frameworks like JavaFX require imperative Bindings in code, even when using a declarative UI-Format. Knockout supports declarative bindings, which allows a much cleaner separation of UI and business code. If you like Acronyms, feel free to refer to it as the 'MVVM' (Model View ViewModel) pattern. Knockout also supports templating, and it's easily extensible, so if you want to integrate a new component and bind it's data it's really straightforward. And Knockout is also well supported and documented, which allows us to concentrate on other features of the API, and it didn't change it's API incompatibly in the past, which is something we value and respect in fellow API designers. We will only give you an overview of the relevant parts and techniques here. Specifically we'll show here how to use Knockout bindings and templates with Java and our API. For a deeper understanding of all the nitty gritty details of Knockout itself check out the aforementioned excellent documentation.

Model, View, ViewModel (MVVM)

The Model

The Model in MVVM is the DataModel, the stored data and operations of our application, representing e.g. a device or entries in a database. It has nothing to do with the UI.

The ViewModel

The ViewModel is different. This is the model used by the View. It represents the state of the View. It's the unsaved data the user works with. It's important to understand the difference, because Model and ViewModel may seem interchangeable. Used carefully they can even use the same classes sometimes. But you need to understand the different roles in the MVVM pattern. Imagine a Person class of the Model. Person objects are stored somewhere in a database. Now we want to change the age of a Person using a form. If we use the Model object directly in the View and bind the ui to it, a user update will directly operate on the data. If the user decides to cancel there's no way back, because the data has been changed already, directly and immediately. That's why we need a different Object as the ViewModel.

And ViewModels can add View specific aspects to the data we manipulate. They can control which page is displayed, or in our sample application they control the rotation of the words.

Still it's important to know that the ViewModel doesn't have any knowledge of the ui itself. It doesn't have any reference to Buttons or text input fields. It doesn't know HTML and in our case it's just a Java class exposing functions and Properties.

The View

The View is defined in a declarative format, HTML. The dynamic parts of the view are bound to the ViewModel, so they display data from the ViewModel and update whenever that model changes. The View can also bind actions like Mouseclicks to function calls. While it is possible to add code snippets directly into HTML, it is not recommended. Such embedded code is hard to unit test and eliminates benefits of separations of concerns. Declarative description of the UI (e.g. the HTML page) should remain declarative - if for nothing else, then because it is then easier to process by various UI design tools.

Defining the ViewModel

In Knockout.js the ViewModel is defined using JavaScript. In DukeScript it's defined in Java. This is what the @Model annotation is there for. Annotate a class with @Model and a ViewModel will be created for you. Why do we use an Annotation here? Can't we just create a regular class for that? We could, but typically in Java you would have to define setters and getters for the Properties. We didn't want you to suffer from the verbosity of Java. We wanted to make an API that is at least equally productive to it's JavaScript equivalent. With an Annotation the framework can take care of generating boilerplate code. You just declare your Properties and their type and the class is generated for you. Compact, focused on the goal and compared to the JavaScript version you have the added benefit of a typesafe API.

Let's create a ViewModel. To do so, first delete the DataModel class in your project. Now create a new class named ViewModel instead:

import net.java.html.json.Model;
import net.java.html.json.Property;

@Model(className = "Person",properties = {
  @Property(name = "name", type = String.class),
  @Property(name = "age", type = int.class)
})
public class ViewModel {

}

Note that the name of the annotated class and the generated class (e.g. Person) have to be different. Now let's check out what has been generated. Open the Main class and delete the content of the onPageLoad method. Instead type something like this:

public static void onPageLoad() throws Exception {
    Person person = new Person("Bob", 122);
    person.setName("Robert");
    person.setAge(person.getAge()+1);
    person.applyBindings();
}

It seems that DukeScript has magically created the class "Person" for you. You can instantiate it and access and modify it's properties. This is how you interact with the model from the code side. When something happens, e.g. a year is up you can programatically increment the age, and when Robert changes his name to Roberto, because he suddenly turned Italian (such things happen), you can also update the value via a setter. "...Like a Bean, so what?" you might be tempted to ask. But the real interesting part is the call to applyBindings(). This method tells Knockout to bind the View to the ViewModel.

In the View you can now reference Bob's Properties. We'll test that! Delete the index.html and create a new HTML-Document named "PersonView.html":

<html>
    <head>
        <title>PersonView</title>
    </head>
    <body>
     <div> <span data-bind="text: name"></span> is <span data-bind="text: age"></span> years old.</div>
    </body>
</html>

Now just fix the Main class to load the right page in it's main method:


public static void main(String... args) throws Exception {
    BrowserBuilder.newBrowser().
    loadPage("pages/PersonView.html").
    loadclass(Main.class).
    invoke("onPageLoad", args).
    showAndWait();
System.exit(0);
}

You can now run the application and you'll see it display:

Robert is 123 years old.

Congratulations you've successfully created your first Knockout Binding with Java and a very old person! You can also try out this example in a Browser here. We don't need to launch a Browser using BrowserBuilder in DEW, so the code is slightly different when running in dew. The onPageLoad method has been moved to the ViewModel and is invoked from a static block.

Arrays

Let's assume that you wish to list name and age of a whole group of Persons. In that case we'll use an ObservableArray instead of a single Object. Doing so is fairly simple. You just need to create a model with a Property of type Person.class and mark it as an array:


@Model(className = "PersonList", properties = {
    @Property(name = "persons", type = Person.class, array = true)
})
public class ViewModel {

    @Model(className = "Person", properties = {
        @Property(name = "name", type = String.class),
        @Property(name = "age", type = int.class)
    })
    public static class PersonModel {}
}

Now you can create as many persons as you want in the Main class and use them as the ViewModel:


public static void onPageLoad() throws Exception {
    PersonList personList = new PersonList(new Person("Bob", 123));
    personList.getPersons().add(new Person("Bill", 132));
    personList.getPersons().add(new Person("Peter", 111));
    personList.applyBindings();
}

In order to reference them in the View you need to slightly change it. We'll make use of the 'foreach' binding. The foreach binding copies a section of markup foreach object in an array and binds the markups attributes to the objects Properties. There already is a div around our bound values. We can simply use that and add a 'foreach' binding:


<html>
    <head>
     <title>PersonView</title>
    </head>
    <body>
     <div data-bind="foreach: persons" > <span data-bind="text: name"></span> is <span data-bind="text: age"></span> years old.</div>
    </body>
</html>

Now the application will show a list of our persons. Here's the example running in the Browser. We should probably beautify that a bit and make it a real HTML-List or a table. That's no problem, because foreach will duplicate the whole content enclosed in the div. Let's try that! Change the content of body to:


<ol data-bind="foreach: persons">
    <li><span data-bind="text: name"></span> is 
    <span data-bind="text: age"></span> years old.</li>
</ol>

Here's the updated example running in DEW. Now our persons will be displayed as an ordered List.

Computed Properties

The last thing you need to learn regarding the Observables are ComputedProperties. ComputedProperties are Properties derived from other Properties. This also means that whenever one of the Properties it is depending on changes, our ComputedProperty changes as well. We've already seen one in the sample application. The 'words' Property depends on the 'message' Property. The 'words' method has been annotated as @ComputedProperty. The argument it takes is a String named 'message'. This String actually references the Property 'message' of our ViewModel. That looks a bit error prone, since we could easily have a typo in the name. But actually the AnnotationProcessor takes care of that. If the name of the Arguments don't match the names of known Properties, the code won't compile and you'll get an error message telling you exactly what the problem is.

A simple example would be a derived Property that combines two other Properties.


@ComputedProperty
public static String summary(String name, int age){
    return name+" ("+age+")";
}

Please note how the editor checks what you are doing here. If you get the name of the base Properties wrong, you'll get an error message. You can use this Property like any other Property. In the background a Listener will register for changes in the base Properties, so the derived value will be invalidated as well.

Functions

Observables are one part of MVVM. But we also need a way to invoke methods from the UI. This allows us to invoke business logic. As mentioned before we want the business logic to be part of the Java code. So it's either part of the ViewModel, or the ViewModel is the entry point and calls something else. In order to be able to call a function from the ui you need to expose it. To do that you'll use the @Function Annotation:


@Function
public static void increaseAge(Person person){
    person.setAge(person.getAge()+1);
}

The arguments of this method define what will be passed to it. You can also create Functions without arguments. The turnRotationOff in the sample app is a good example of that. Other than that, the method can accept an instance of the Model it belongs to. That's 'Person' in our case. But you can also get some information about the event that caused the call. You can simply specify what Property of the event you're interested in. E.g. if you're interested in the X-coordinate of the event, add it as a parameter. We'll extract it for you and pass it as an argument:


@Function
public static void increaseAge(Person person, int x){
    person.setAge(person.getAge()+x);
}    

Believe me, you'll find this kind of magic injection very useful, when you need more complex view logic.

Bindings

Now you know how the basics of creating ViewModels and how to access their data using data-bind. But actually Bindings are much more powerful than what we've used so far. In this section you'll learn a bit more about how you can control the View. We won't go into much detail though, because this is extensively documented in the the Knockout.js documentation

Controlling style and content

There's a set of bindings controlling what is displayed, how it's displayed, or if it's showing at all.

bindingexamplepurpose
visible data-bind="visible: show" The enclosed section is visible only when 'show' property of the ViewModel evaluates to true.
text data-bind="text: message" Show a text message. We've used that already.
html data-bind="html: content" Like text, but treats the text as HTML markup.
css data-bind="css: { 'rotate' : income() < 0 } " Binds a css class to the element depending on a condition.
style data-bind="style: { color: temperature() < 0 ? 'blue' : 'red' }" Similar to css but directly adds an inline style.
attr data-bind="attr: { href: url, title: details }" Can bind any Attribute of the DOM Element to a property of the ViewModel.

Responding to Input

The bindings you've seen so far are read-only, they observe a value and change in response of it's changes. But obviously there must be a way for users to enter and edit values, and also to call the Functions we defined. Here's an overview of what's available. Check out Knockout documentation for the details.

bindingexamplepurpose
click data-bind="click: increaseAge" defines a function to be invoked when the element, e.g. a Button, is clicked
event data-bind="event: { mouseover: enableDetails, mouseout: disableDetails }" Like click, but for any event like keypress, mouseover or mouseout.
submit data-bind="submit: doSomething" Typically used on a form Element. Defines a function to call on submit.
enable data-bind="enable: rotating" enables the element depending on a boolean. Useful for buttons and other inputs.
disable data-bind="disable: rotating" Does the opposite of the enable-binding.
value data-bind="value: name" Binds the value of an input to a model property or field. In case of property it sets up a two-way binding.
textInput data-bind="textInput: name" Similar to 'value', but with immediate updates. 'value' by default only updates on focus lost.
hasFocus data-bind="hasFocus: isSelected" This binding will focus the enclosing element, when the value is true. This is also a two-way binding, so if you focus the element, the bound property will become 'true'.
checked data-bind="checked: wantsSpam" Like 'value', but specifically for CheckBoxes and RadioButtons.
options data-bind="options: ides" Used with <select>-Elements. Uses specified array as values for the Element.
selectedOptions data-bind="options: ides, selectedOptions: chosenIdes" Used with select-Elements. Binds the chosen elements to an array.

Custom Bindings

Sometimes Knockout doesn't have the binding you need. No need to worry though, because it's simple to create a custom Binding. I won't go into details here about how you define the custom binding. A detailed description is in the Knockout Docs. What is interesting to users of the Java API is, how this binding can be registered. And that's what you'll learn here. Let's assume we want to paint on a canvas. For that we need the x and y coordinate of the mouse event. Since JQuery makes your life easier when dealing with elements, let's first add that. That's not directly related to custom bindings, but it's nice to learn something on the side anyway. Create a new project for this. Define a class JQuery and add the JavaScript-library to your project. Now you can reference it using @JavaScriptResource. This is how you manage JavaScript libraries in DukeScript:


@JavaScriptResource("jquery-1.11.0.min.js")
public class JQuery {
    
    @JavaScriptBody(args = {},body="")
    public static native void init();
    
}

The init method is there to make sure the library is loaded. You can call it in your Main class. Now let's define a simple ViewModel that responds to mousePressed events:


@Model(className = "GraphicsController", properties = {
    @Property(name = "color", type = String.class),
})
final class GraphicsControllerModel {


    @Function
    static void mousePressed(GraphicsController model, int realX, int realY) {
        GraphicsContext2D gc = HTML5Graphics.getOrCreate("canvas");
        gc.setFillStyle(new Style.Color(model.getColor()));
        gc.fillCircle(realX, realY, 5);
    }

}

But now for the custom binding. The important part for us is how to register it. We do this via the @JavaScriptBody annotation. This annotation helps you to execute JavaScript:


 public static void onPageLoad() throws Exception {
        JQuery.init();
        registerMouseBinding();
    }

    @JavaScriptBody(args = {}, body = "ko.bindingHandlers['mousepressed'] = {\n"
            + "    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {\n"
            + "        var allBindings = allBindingsAccessor();\n"
            + "        $(element).mousemove(function (event) {\n"
            + "             if (event.which==1){"
            + "             var el = window.document.getElementById('canvas');"
            + "             var rect = canvas.getBoundingClientRect();"
            + "             event.realX = event.clientX - rect.left;"                
            + "             event.realY = event.clientY -rect.top;"                
            + "             allBindings['mousepressed'].call(viewModel,null, event);\n"
            + "            return false;}"
            + "            return true;\n"
            + "        });\n"
            + "    }\n"
            + "};")
    public static native void registerMouseBinding();

That's it, we've added mousepressed as a new binding to Knockouts list of registered BindingHandlers. We can now use it in our data-bind directives:


    <body>
        <canvas class="unselectable"  id="canvas" width="600" height="600" data-bind=" mousepressed : mousePressed" ></canvas>
        <div >
            Select Color:
            <div><input type="radio" name="color" value="#ff0000" data-bind="checked: color" /> Red</div>
            <div><input type="radio" name="color" value="#000000" data-bind="checked: color" /> Black</div>
            <div><input type="radio" name="color" value="#ffffff" data-bind="checked: color" /> White</div>
        </div>
    </body>

That's all you need to know about bindings to get started.

Controlling the Context

The ViewModel may get quite complex for more advanced applications. That's why we need a way to work with scopes. We need a way to define the data context of a part of the view, so we do not have to specify the whole path through the hierarchy in order to access a Property. Instead we want to define once what Object to use in a certain section of the code, and then simply access it's properties directly

The foreach-Binding

In our example you already saw the foreach-Binding. This is one way to do that. The foreach-Binding loops through an Array of Model Objects, duplicates the nested DOM-Elements and sets the current Model Object as the context for each copy. This way we can directly access it's properties.

The with-Binding

The with-Binding is for named properties what foreach is for arrays. It allows you to define a scope for nested elements. Actually our model is too flat for this, since our Person only has primitive Properties, so let's add an Address. First define the type an then add it as a Property to the Person Model:


@Model(className = "Address", properties = {
    @Property(name = "city", type = String.class),
    @Property(name = "street", type = String.class)
})
public static class AddressModel{}

@Model(className = "Person", properties = {
    @Property(name = "name", type = String.class),
    @Property(name = "age", type = int.class),
    @Property(name="address", type = Address.class)
})
public static class PersonModel {
//...
               

This will also update the constructor of the generated class. You need to fix the usage in Main:


PersonList personList = new PersonList(new Person("Geertjan", 22, new Address("Amsterdam", "Codersgracht 11001010")));
personList.getPersons().add(new Person("Toni", 19,new Address("Amsterdam", "Bergmannstr. 66 ")));
personList.applyBindings();

We can now work with this address and access it's properties like in the following code example:


<ol data-bind="foreach: persons">
    <li><span data-bind="text: name"></span> is 
        <span data-bind="text: age"></span> years old. <button data-bind="click: increaseAge">Increase Age</button>
        <div >
            <span data-bind="text: address().street"></span>,
            <span data-bind="text: address().city"></span>
        </div>
    </li>
</ol>

But this is inconvenient when we have many properties or when the hierarchies get deeper. So we'd rather access the properties directly. For this we need to define a context object for a scope. So for each person we can now add a with-Binding. This tells the system, that in the div tag we're dealing with the address Property. This defines the context, and we can directly access the properties of the current context.


<ol data-bind="foreach: persons">
    <li><span data-bind="text: name"></span> is 
        <span data-bind="text: age"></span> years old. <button data-bind="click: increaseAge">Increase Age</button>
        <div data-bind="with: address">
            <span data-bind="text: street"></span>,
            <span data-bind="text: city"></span>
        </div>
    </li>
</ol>

Checkout the example in DEW.

Accessing the parent

Now we know how to control the context and in our current scope we have direct access to the current context Object. Now think about the following scenario: We're showing a list of persons, and we want to have a delete Button for each. So we'll use the foreach-Binding to display them and create the Button. But deleting a Person means removing it from the list. And the list is part of the PersonList which is outside our scope. What if we want to call a method of the parent? For this we can use $parent to reference the parent.

Let's try it out. We'll first define a @Function for the PersonList in our ViewModel class:


@Function
public static void remove(PersonList model, Person data){
    model.getPersons().remove(data);
}

And now we can use it in our context with the $parent reference:


<ol data-bind="foreach: persons">
    <li><span data-bind="text: name"></span> is 
       <span data-bind="text: age"></span> years old. <button data-bind="click: $parent.remove">Delete</button>
        <span data-bind="text: address().city"></span>,
        <span data-bind="text: address().street"></span>
    </li>
</ol>

Here's the corresponding example in DEW. Now we can remove elements from the list. But we can also access our grandparent, grandgrandparent, and so on. For this there's the $parents reference which gives us an array of parents. $parents[0] is equivalent to $parent, $parent[1] is the grandparent, $parent[2] is... you get the idea. And in order to address the root of your ViewModel, we can use the $root reference.

Accessing the current Context Object

Perfect, so now we can access the whole view model from the comfort of our local context. Ha! Wrong! There's still one piece missing! We can access the Properties of our context object and it's parents up to the root, but how about the current context object itself? I can feel your despair, so I wont let you suffer: There's a solution for this too. The $data reference will let you access the context object directly. When is this helpful? Imagine you loop over an array of strings held in your data model. Then your Strings don't have Properties to access, but you want to print them directly. Let's add a list of tags to our Person to test it:

     
@Model(className = "Person", properties = {
    @Property(name = "name", type = String.class),
    @Property(name = "age", type = int.class),
    @Property(name = "address", type = Address.class),
    @Property(name = "tags", type=String.class, array = true)
})
public static class PersonModel {

Modify your Model creation code in the Main class and add some tags:

     
public static void onPageLoad() throws Exception {
        PersonList personList = new PersonList(new Person("Geertjan", 22, new Address("Amsterdam", "Codersgracht 11001010"), "NetBeans", "NetBeans Platform", "AngularJS"));
        personList.getPersons().add(new Person("Toni", 19,new Address("Amsterdam", "Bergmannstr. 66 "),"JavaFX", "NetBeans Platform", "OSGi"));
        personList.applyBindings();
}

Now in our page we'll access them in a foreach-Loop:

     
<ol data-bind="foreach: persons">
    <li><span data-bind="text: name"></span> is 
       <span data-bind="text: age"></span> years old. <button data-bind="click: $parent.remove">Delete</button>
        <span data-bind="text: address().city"></span>,
        <span data-bind="text: address().street"></span>
        <ul data-bind="foreach: tags"><li data-bind="text: $data"></li></ul>
    </li>
</ol>

And as a result you should see the tags displayed as a list.

Using Templates

Applications written with DukeScript typically are Single Page s, and the scope of a Model is a single page. Still we need a way to mimic the behaviour that you typically get in a web application with several linked HTML-pages. Take for example a typical CRUD application, where there's typically a "master" view showing all entries in a list. Each entry features a link that allows you to edit individual entries. For that a form will open in place of the list. There will probably also be a button that opens a form for adding new entries. In DukeScript we use Knockout templates. In the coming section we'll have a look at that.

The Template Binding

The template binding has a 'name' parameter. Knockout will look for a script tag with the same id as specified by the 'name' parameter.

 
        <ol data-bind="template: {name: 'person', foreach: persons }">
        </ol>
        <script type="text/html" id="person">
            <li>
            <span data-bind="text: name"></span> is 
            <span data-bind="text: age"></span> years old. <button data-bind="click: $parent.remove">Delete</button>
            <span data-bind="text: address().city"></span>,
            <span data-bind="text: address().street"></span>
            <ul data-bind="foreach: tags"><li data-bind="text: $data"></li></ul>
            </li>
        </script>
            

Make sure that the content of the script tag won't be executed as Javascript. For that specify type='text/html'. If you look at the result of the sample you see that it's equivalent to the previous sample. The 'foreach' parameter makes this a 'foreach'-binding. The difference is that we're using a reusable template here.

Controlling the view

The current version is an example how to reference templates. We didn't win much except the freedom to structure the view so the HTML gets more readable. Now let's use the template binding to switch between an editor and the list. For that we'll first add a new property to our model that represents the edited contact. We'll also add an 'edit' method, which sets the edited Person as a response to a button click, and a commit method that basically stops editing by setting the editing property to null again:

                
@Model(className = "PersonList", properties = {
    @Property(name = "editing", type = Person.class),
    @Property(name = "persons", type = Person.class, array = true)})
public class ViewModel {

    @Function
    public static void edit(PersonList model, Person data) {
        model.setEditing(data);
    }
    
    @Function
    public static void commit(PersonList model, Person data) {
        model.setEditing(null);
    }
    //..
}
                
                

In this basic scenario we directly modify the actual data. For this simple example the direct editing is good enough, because we just want to demonstrate how we switch templates. In real live we would work on a copy of the data. You can get a copy of any viewmodel object by using it's clone() method. This is useful for being able to validate the content before writing it back and also for being able to rollback changes on cancel. You'll see that in action in a more advanced exampe later. We can now switch views depending on the presence of this edited contact.

                
<h1>Person List Demo</h1>
<ol data-bind="template: {name: 'person', foreach: persons, if: !editing() }"></ol>
<div data-bind="template: {name: 'editor', if: editing, data: editing}"></div>

<script type="text/ko" id="person">
    <li>
        <span data-bind="text: name"></span> is 
        <span data-bind="text: age"></span> years old. <button data-bind="click: $parent.edit">Edit</button>
        <span data-bind="text: address().city"></span>,
        <span data-bind="text: address().street"></span>
        <ul data-bind="foreach: tags"><li data-bind="text: $data"></li></ul>
    </li>
</script>
    
<script type="text/ko" id="editor">
    <form >
        <label for="name">Name:</label><input type="text" data-bind="value: name"></input>
        <label for="age">Age:</label><input type="text" data-bind="value: age"></input>
        <label for="city">City</label><input type="text" data-bind="value: address().city"></input>
        <label for="street">Street</label><input type="text" data-bind="value: address().street"></input>
        <button type="submit" data-bind="click: $parent.commit">Submit</button>  
    </form>
</script>