Overview

The user interface for Verne is decoupled from logic on the server. On the server, Verne operations execute Business Services by means of the Service Transaction which contains the View Tree.  The View Tree represents the service form and contains a hierarchy of View Nodes with all data applied. When Verne wants to display the form to the user, the server transforms the View Tree into a flattened JSON tree known as Render State before sending it to the browser.  The Render State contains only representations of View Nodes that are both active and visible.  This Render State is then consumed by a components in the Glue Component Layer.  The Glue components contain little or no formatting and their purpose is to translate the Render State and use the appropriate UI Components from the Verne Component Library.  The Glue Layer components are also responsible for handling any events and communicating with the server.

The following diagram represents the interaction between components of the Verne UI architecture:

User Interface Architecture Overview

Render State

The state of the service is maintained on the server as the View Tree within the Service Transaction.  An action on the front end will send a request which will update the Service Transaction and the updated Render State flows back to the browser.  On initial page load the entire visible and active view tree will be output as Render State.  When subsequent requests are sent to the server only changes to the Render State are returned to the client to ensure minimal network usage and processing overhead on both the server and client browser.

Render state contains only the information from the view tree that is required to actually render the screen.  The View Tree is flattened to a JSON map keyed by node ids with the node’s render state representation as the value.  Additionally, a special "root" key is added to identify which id is the start node.  Any container node (boxes, repeaters, records, etc) will have a "children" attribute containing a list of its visible children. 

The following is a simple example of Render State.

{
  "1426f40eab21d27b": {
    "id": "1426f40eab21d27b",
    "nodetype": "box",
    "dos": [
      "css-lightContainer",
      "css-pl"
    ],
    "ro": false,
    "text": {
      "label": "Box heading"
    },
    "kv": {"ui-some-key", true},
    "children": [
      "8d900e4b94b4ecc0",
      "e6754d84d6afde1e"
    ]
  },
  "8d900e4b94b4ecc0": {
    "id": "8d900e4b94b4ecc0",
    "nodetype": "text",
    "ro": false,
    "text": {
      "label": "A piece of text"
    },
  },
  "e6754d84d6afde1e": {
    "id": "e6754d84d6afde1e",
    "nodetype": "attribute",
    "attribute": "Name"
    "ro": false,
    "text": {
      "label": "Enter the name"
    },
    "kv": {"ui-maxLength": 15},
  }
  "root": "1426f40eab21d27b",
}
  • The “root” key identifies that the renderer should start rendering at node “1426f40eab21d27b”
  • The renderer would select “1426f40eab21d27b” from the map and determine the type of component needed to render it.  
  • Node “1426f40eab21d27b” is of type box.  It has the following properties:
    • nodetype: The type of node can be used by the renderer to determine how to render the node.  
    • dos: Display Options.  In this case there are 2 utility classes being added to the box.  The css- prefix is added during configuration of the form item to allow customisation of the display.  These classes will be added to the resulting HTML without the css- prefix.
    • text: Text values.  Any text values that should be displayed with the node.  The values have been resolved into the appropriate locale.
    • ro: Read-only indicator.
    • kv: Key Values. Only configured key values prefixed with ui- will make it to the render state.  Key values can be used to control behaviour of the UI component. e.g., ui-maxLength would inform the renderer that a text field can only be up to a certain length.
    • children: An array of ids for the box’s children.  These should be rendered inside the box.
  • When rendering the children the renderer repeats the process of selecting each node from the map, choosing the appropriate renderer and output it and any children.

As well as the node data there are two other types of data that sit within the map:  Common text keys and data constraint information.   These are prefixed the TK and DC prefixes to ensure no collision with node information and ensure that shared text and constraints are only sent to the client once.  Text keys are flagged as common by adding the text key to the "skin" group in configuration.  

For example:

<text group="skin" key="skin.optional">Optional</text>

Will be added to the Render State map as:

{
  ....
  "TK_skin_optional":"Optional"
}

Similarly, data constraints used in the form will be added with the DC_ prefix :

{
  ...
  {
    "name": "gender",
    "restrict": true,
    "type": "string",
    "values": {
      "F": "Female",
      "M": "Male"
    }
  }
}

Glue

The above description of Render State mentions a "renderer" and the Glue layer is the rendering engine.  It takes the Render State and converts it into a UI.  Whereas the Render State comes from the Verne engine the Glue is part of the Verne Skin.  The default skin for Verne is VueGlue and uses the Vue javascript framework to enable very flexible rendering.  The Glue layer is also responsible for all requests back to the server using the Verne API. 

This layer could be switched out for a different skin, framework and glue if desired.  The rest of this documentation is assuming that the default skin is being used.  Some of the concepts may require a working knowledge of Vue.

Glue components live within the Verne VFS in resources/vueglue/templates and sub directories.  Each glue component consists of a template file (.vue), an optional javascript implementation (will use a default if not provided) and 2 additional files for documentation purpose (.md and .json):

glue-box.vue
glue-box.js
glue-box.md
glue-box.json

The JavaScript used in the js files is ES5 meaning it needs no transpilation.  Unlike typical Vue development, we separate out the vue template file from its JavaScript implementation file.  This enables projects to customise a template in their project without needing to copy, alter, or understand the JavaScript implementation.

The main task of components in this layer is selecting the correct UI Component from the Design Library, configuring it for display and reacting to any events from that library by calling the server using the Verne API.  There should be no, or minimal, direct rendering of HTML and styling at this layer.

The example below shows the template for glue-box.  It directly delegates to the cat-box component from Design Library.  It knows which text keys are appropriate for which cat-box properties and knows how to translate any display options into meaning changes in behaviour.

<cat-box :id="nodeid" :errors="node.errors" v-if="visible" :label="text('label')" :hint="hint" :class="_classString" :section="hasDisplayOption('section')" :help="help" :readonly="node.ro || readonly">
  <widget v-for="child in node.children" :viewtree="viewtree" :nodeid="child" :key="child"/>
  <div v-if="hasText('intro')" v-html="text('intro')" slot="html"></div>
</cat-box>

The glue-box doesn’t have a glue-box.js file so in this case it will fall back to using the default implementation.

Vue.component("${vueComponent}", {
  mixins: [gluebase],
  template: "${vueTemplate}"
});

When the component is sent to the browser the ${vueComponent} and ${vueTemplate} variables are replaced with the name of the component and the content of the .vue file respectively.  All Glue components use the "gluebase" mixin to provide a set of common methods useful in working with Render State.  It also has a set of 3 required props which must be passed into each Glue component.  These are:

  • viewtree: The top-level map containing all nodes
  • nodeid: Id of the node to render
  • node: The node to render

In the example template above there are a number of common functions and usages to note:

  • ‘text’ method is being used to retrieve any configured label text and propagate that as the label in the cat-box
  • ‘hint’ computed property is being passed into the appropriate prop of the cat-box
  • ‘hasDisplayOption’ method is being used to check the presence of a particular display option
  • ‘hasText’ method is being used to determine whether to add text a slot on the cat-box
  • ‘widget’ component is being used to render all children of the node. (see below)

The ‘widget’ Component

The "widget" component is a functional Vue component.  It does not render anything directly but instead makes a decision based on the node selected which Glue component to render.  It usually only takes 2 properties, nodeid and viewtree, and is responsible for selecting the node from the view tree and rendering the Glue component with its required 3 props (see above). 

The decision tree that "widget" uses is based on:

  • Presence of a “widget” attribute on the node mapping it to a specific Glue component
  • The nodetype on the node
  • Whether the node is read-only

Similarly, there is a specific decision tree for "attribute" type nodes where additionally the following also help determine the component used:

  • The data constraint attached to the node

Verne API

Verne exposes a command API for interacting with the Service Transaction on the server.  This enables the setting of attribute values, key values, and firing events.  With each request to the server any changes to the Render State will be returned and merged back into the viewtree used by the browser.

The most common function for sending data to the server is the processUpdate function.  It allows for the sending of a list of commands to the server to be executed.  The example below sends a "ui-closePanel" event to the server.

Catalyst.processUpdate({
  nodeid: this.nodeid,
  type: "view-node-fire-event",
  commands: [
    {
      type: "view-node-fire-event",
      id: this.nodeid,
      name: "ui-closePanel"
    }
  ]
});

The processUpdate function will return a JavaScript Promise object that will be resolved with the return Render State response upon completion.  Note that this will only have the changed items in the tree and not the entire view tree.  Using the Promise it is possible to chain commands.  

Verne Design System

The components in the Verne Design Library are intentionally unaware of Verne concepts. They could be used in authoring any Vue website.  As far as Verne (or at least the Glue layer) knows they are black boxes that perform a specific function.

The components available for use are showcased in the Verne Design System website.

The following digram shows how the render state is transformed into an HTML page and how user interaction updates the Service Transaction on the server.

0
0

Jump to Section