Tabular views, unsurprisingly, present data in a tabular form. As an example, here is a screenshot of the hosts view...
Applications can create (and inject into the GUI) their own view(s) of tabular data. This tutorial will step you through the process of creating an application that does just that - injects a tabular view into the ONOS GUI.
Application Set Up
Setting up the application is exactly the same as for the Custom View tutorial, with one minor difference: the choice of archetype in step (2) should be uitab instead of ui:
Currently, the uitab archetype has not been implemented, so the following command will not work until this has been fixed.
(2) Overlay the UI additional components
Building and Installing the App
From the top level project directory (the one with the pom.xml file) build the project:
Assuming that you have ONOS running on your local machine, you can install the app from the command line:
After refreshing the GUI in your web browser, the navigation menu should have an additional entry:
Clicking on this item should navigate to the injected Sample View:
Selecting a row in the table should display a "details" panel for that item:
The row can be de-selected either by clicking on it again, or by pressing the ESC key.
Pressing the slash ( / ) or backslash ( \ ) key will display the "Quick Help" panel. Press Esc to dismiss:
The following sections describe the template files, hopefully providing sufficient context to enable adaptation of the code to implement the needs of your application.
Description of Template Files - Server Side
These files are under the directory ~/src/main/java/org/meowster/app.
The descriptions for both AppComponent and AppUiComponent remain the same as in the Custom View tutorial.
This class extends UiMessageHandler to implement code that handles events from the (client-side) sample application view. Salient features to note:
(1) implement createRequestHandlers() to provide request handler implementations for specific event types from our view.
(2) define SampleDataRequestHandler class to handle "sampleDataRequest" events from the client. Note that this class extends TableRequestHandler, which implements most of the functionality required to support the table data model:
Note the call to the super-constructor, which takes three arguments:
- request event identifier
- response event identifier
- "root" tag for data in response payload
To simplify coding (on the client side) the following convention is used for naming these entities:
- For a given table view, the table is identified by a "tag" (in this example, that tag is "sample")
request event identifier is derived as: <tag> + "DataRequest"
- response event identifier is derived as: <tag> + "DataResponse"
- "root" tag is derived as: <tag> + "s"
(2a) optionally override defaultColumnId():
Typically, table rows have a unique value (row key) to identify the row (for example, in the Devices table it is the value of the Device.id() property). The default identifier for the column holding the row key is "id". If you want to use a different column identifier for the row key, your class should override defaultColumnId(). For example:
The sample table uses the default column identifier of "id", so can rely on the default implementation and does not need to override the method.
(2b) define column identifiers:
Note that the column identifiers defined here must match the identifiers defined in the HTML snippet (see sample.html below) for the view.
(2c) optionally override createTableModel() to specify custom cell formatters / comparators. The following example sets both a formatter and a comparator for the "code" column:
See additional details about table models, formatters, and comparators.
The sample table relies on the default formatter and comparator, and so does not need to override the method.
(2d) implement populateTable() to add rows to the supplied table model:
The sample table uses fake data for demonstration purposes. A more realistic implementation would use one or more services to obtain the required data. The device table, for example, has an implementation something like this:
(3) define SampleDetailRequestHandler class to handle "sampleDetailRequest" events from the client. Note that this class extends the base RequestHandler class:
(3a) implement process(...) to return detail information about the "selected" row:
The sample code extracts an item identifier (id) from the payload, and uses that to look up the corresponding item. With the data in hand, it then constructs a JSON object node equivalent to the following structure:
After which, it sends the message on its way by invoking sendMessage(...).
Description of Template Files - Client Side
Note that the directory naming convention must be observed for the files to be placed in the correct location when the archive is built. Since our view has the unique identifier "sample", its client source files should be placed under the directory ~/src/main/resources/app/view/sample.
|client files||client files for UI views||client files for "sample" view|
There are three files here:
Note again, the convention is to name these files using the identifier for the view; in this case "sample".
This is an HTML snippet for the sample view, providing the view's structure. Note that this HTML markup is injected into the Web UI by Angular, at the point where the view becomes "visible" because the user navigated to it.
Let's describe the different parts of the file in sections...
The outer <div> element should be given the id of "ov-" + <view identifier>, ("ov" standing for "Onos View"). Thus in this example the id is "ov-sample".
Stylesheet for the sample view. Again, a number of naming conventions are in use here: