Page tree

Have questions? Stuck? Please check our FAQ for some common questions and answers.

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 11 Next »


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

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

$ onos-create-app uitab meowster-app

Description of Template Files

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.

protected Collection<RequestHandler> createRequestHandlers() {
    return ImmutableSet.of(
            new SampleDataRequestHandler(),
            new SampleDetailRequestHandler()


(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:

private static final String SAMPLE_DATA_REQ = "sampleDataRequest";
private static final String SAMPLE_DATA_RESP = "sampleDataResponse";
private static final String SAMPLES = "samples";

private final class SampleDataRequestHandler extends TableRequestHandler {
    private SampleDataRequestHandler() {

Note the call to the super-constructor, which takes three arguments:

  1. request event identifier
  2. response event identifier
  3. "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():

// if necessary, override defaultColumnId() -- if it isn't "id"

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

private static final String MAC = "mac";
protected String defaultColumnId() {
    return MAC;

The sample table uses the default column identifier of "id", so can rely on the default implementation.


(2b) define column identifiers:

private static final String ID = "id";
private static final String LABEL = "label";
private static final String CODE = "code";

private static final String[] COLUMN_IDS = { ID, LABEL, CODE };

protected String[] getColumnIds() {
    return COLUMN_IDS;

Note that the column identifiers defined here must match the identifiers defined in the HTML snippet (sample.html) 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:

protected TableModel createTableModel() {
    TableModel tm = super.createTableModel();
    tm.setFormatter(CODE, CodeFormatter.INSTANCE);
    tm.setComparator(CODE, CodeComparator.INSTANCE);
    return tm;

See additional details about table models, formatters, and comparators.

The sample table relies on the default formatter and comparator.


(2d) implement populateTable() to add rows to the supplied table model:

protected void populateTable(TableModel tm, ObjectNode payload) {
    // ...
    List<Item> items = getItems();
    for (Item item: items) {
        populateRow(tm.addRow(), item);
private void populateRow(TableModel.Row row, Item item) {
        .cell(LABEL, item.label())
        .cell(CODE, item.code());

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:

protected void populateTable(TableModel tm, ObjectNode payload) {
    DeviceService ds = get(DeviceService.class);
    MastershipService ms = get(MastershipService.class);
    for (Device dev : ds.getDevices()) {
        populateRow(tm.addRow(), dev, ds, ms);

private void populateRow(TableModel.Row row, Device dev,
                         DeviceService ds, MastershipService ms) {
    DeviceId id =;
    String protocol = dev.annotations().value(PROTOCOL);

    row.cell(ID, id)
        .cell(MFR, dev.manufacturer())
        .cell(HW, dev.hwVersion())
        .cell(SW, dev.swVersion())
        .cell(PROTOCOL, protocol != null ? protocol : "")
        .cell(NUM_PORTS, ds.getPorts(id).size())
        .cell(MASTER_ID, ms.getMasterFor(id));


(3) define SampleDetailRequestHandler class to handle "sampleDetailRequest" events from the client. Note that this class extends the base RequestHandler class:

private static final String SAMPLE_DETAIL_REQ = "sampleDetailsRequest";

private final class SampleDetailRequestHandler extends RequestHandler {

    private SampleDetailRequestHandler() {



(3a) implement process(...) to return detail information about the "selected" row:

private static final String SAMPLE_DETAIL_RESP = "sampleDetailsResponse";
private static final String DETAILS = "details";
private static final String COMMENT = "comment";
private static final String RESULT = "result";

public void process(long sid, ObjectNode payload) {
    String id = string(payload, ID, "(none)");

    // SomeService ss = get(SomeService.class);
    // Item item = ss.getItemDetails(id)

    // fake data for demonstration purposes...
    Item item = getItem(id);

    ObjectNode rootNode = MAPPER.createObjectNode();
    ObjectNode data = MAPPER.createObjectNode();
    rootNode.set(DETAILS, data);

    if (item == null) {
        rootNode.put(RESULT, "Item with id '" + id + "' not found");
        log.warn("attempted to get item detail for id '{}'", id);

    } else {
        rootNode.put(RESULT, "Found item with id '" + id + "'");

        data.put(LABEL, item.label());
        data.put(CODE, item.code());
        data.put(COMMENT, "Some arbitrary comment");

    sendMessage(SAMPLE_DETAIL_RESP, 0, rootNode);




  • No labels