Skip to content

Development Guidelines

Luis Diogo Couto edited this page Feb 7, 2016 · 6 revisions

This guide provides instructions for building a new Tempo UI. Refer to the example folder in the main repo to follow along.

This guide assumes basic knowledge of DukeScript and the Remote Control feature described in Section 15.2 of the Overture User Guide.

Step 1: Building the Model API

The first step of TempoUI development is building an API for the UI to interact with the VDM model. The role of the Model API is to expose model functionality to the UI. We can provide an API for an existing model or define the API upfront and develop the model later. The latter option works well when developing the UI and model in parallel, provided everyone agrees on the API.

The ModelAPI (or whatever else we want to call it) is a handwritten Java class of mostly static methods that exposes the VDM model operations that we want to trigger from the UI. This class is handwritten for two reasons:

  • we only want to expose a subset of operation
  • we may need to do encoding values from the UI into VDM values

In the future, we would like to generate this class from a configuration and automate the encoding. But for now, it's manual. To expose a VDM operation named foo, use something like:

public static void foo(RemoteInterpreter ri) throws Exception {
  interpreter.execute(ROOT_NAME + ".bar()");
}

As you can see, the ModelAPI and VDM names don't have to match. Also, the ModelAPI methods are all static and the class itself has a static reference to the interpreter that is being controlled with the UI. The methods must be static in order to be accessed from the UI. The interpreter reference is needed to execute the VDM operations.

The VDM model itself has no static class restrictions but we will need to store the names of any VDM objects we want to call operations on. The easiest thing to do is to only cal operations on the top-level VDM object -- what roughly corresponds to the entry point. The name of this object can then be easily stored in a constant. Also, remember that there is no checking on the VDM strings that we submit to the interpreter so be careful.

Step 2: Building the View Model

Once we have the ModelAPI in place, we can build the View Model for the UI. This is a Java class that DukeScript binds to the UI using the Knockout library. This class is typically called some variation of Data Model (ex: CustomerModel) but should never be confused with the VDM model. In this guide we always refer to it as ViewModel

The ViewModel is defined through annotations and code generation provided by DukeScript. When defining the ViewModel, we need to use operations from the ModelAPI and also declare properties to hold any VDM variables that we want to bind to the UI. Properties may be strings, enums, primitives or other classes generated as View Models. If we want to bind a property in the ViewModelto a VDM variable, then their names must match.

For example, the following snippet defines a basic ViewModel with a single property (ok). Later on, this property will be bound to a VDM variable of the same name.

@Model(className = "VdmData", targetId="",properties = {
        @Property(name = "ok", type = boolean.class),
})
final class UiModelData {
}

In order to expose methods to the UI, we use the @Function annotation. The annotated method must be static. The generated data class (VdmData in this case) is always passed as a parameter.

@Function
static void foo(VdmData vdm) throws Exception {
  ModelAPI.foo();
}

Step 3: Binding the HTML5 UI

Tempo UIs are built with HTML5 and JavaScript. We will not discuss how to build the UI except to say that we can use any standard HTML5 technology when building the UI. In addition, the UI can be designed and developed independently from the model.

For connecting the UI to model, the Maven archetype will have created a file src/main/webapp/pages/index.html. This is the page that is loaded in the browser when the model is launched. The UI must start from this page.

The HTML5 UI elements are bound to the ViewModel properties using Knockout. Refer to the Knockout documentation (http://knockoutjs.com/documentation/introduction.html) for information on what kinds of bindings are available.

As an example, here is a trivial HTML snippet that binds a status message to the ok property:

<p>
  Status is: <em data-bind="if: ok"style="color:green">Ok</em>  <em data-bind="if: !ok"style="color:red">Not Ok</em>
</p>

When launched, this UI would look something like:

Once the underlying VDM variable changes the false, the UI will automatically update to reflect this.

Step 4: Building the Remote Controller

In regular DukeScript applications, the application logic is a "plain" Java application. In the TempoUI, the application logic is a VDM Model wrapped in a Remote Control class, as described in section 15.2 of the Overture user guide. Each TempoUI project must provide its own Remote Control class that subclasses TempoRemoteControl.

The remote control class is also responsible for connecting the VDM Model and the ViewModel. This is done in a public static void method named onPageLoad(). This method handles the final stages of connecting the UI to the VDM model.

Broadly speaking, onPageLoad must do the following:

  • connect to the interpreter
  • initialise the ViewModel
  • construct a list of VDM model variables to be bound to ViewModel variables
  • attach UI binding listeners to those variables (TempoUI provides classes to do this).

The snippet below shows a sample page loader method.

public static void onPageLoad() throws Exception {
  // Connect UI to Model Interpreter
  ModelAPI.setInterpreter(interpreter);

  // Create  ViewModel and bind it to the UI
  VdmData uiData = new VdmData();
  uiData.applyBindings();

  // Acquire top-level value
  String root = "foo";
  String rootConstructor = "new Foo()";
  interpreter.create(root, rootConstructor);
  Value v = interpreter.valueExecute(root);
 
  // Create list of VDM variables to bind
  List<ListenerAttacher.VarBindInfo> vars = new ArrayList<>(1);
  vars.add(ListenerAttacher.VarBindInfo.create("ok", new ABooleanBasicType()));

  // Attach listeners to notify ViewModel of variable changes
  ModelBinder mb  = new ModelBinder(v, vars, uiData);
  mb.bind();
}

And that's it!