SPARQL Web Pages - SWA Forms

For TopBraid 4.3 and above, last updated on Feb 24, 2014

Authors:
Holger Knublauch <holger@topquadrant.com>

Abstract

This document explains how the SWA ontology can be used to describe customized form layouts. It covers the most commonly needed design patterns only, in tutorial style. Further details are covered by the SWA Help that is part of TopBraid Composer.

This document is part of the SPARQL Web Pages Specification.


Table of Contents

 

1 Motivation

One of the attractive features of RDF is that instance data and schema definitions are represented in the same format - as RDF triples. This makes it possible to query information about classes and properties using the same languages (in particular SPARQL). The collected information about classes and properties can be employed by user interface frameworks to present arbitrary RDF resources in an appropriate way. For example, the form display of a schema:Person below "knows" which values are present for that instance, and selects suitable renderings of its property values based on their datatype.

The screenshot above is showing the default form that is part of SWA and attached to rdfs:Resource. This default form is OK for many scenarios, but may not provide the best user experience. For example, enumerating all properties alphabetically is not what most users would expect - they'd probably much rather like to see first name, then last name, then date of birth etc. This document explains how to make such customizations.

2 Setting up a customized form

In order to have TopBraid use a customized form, you first need to create an SWP file for your ontology. In this example we'll customize a form for the schema.org namespace, so the file would be called schema.ui.ttlx. Open the following wizard using File > New > RDF/SWP File

Make sure to select the Extended Turtle format (.ttlx) because this will create more readable files than normal Turtle. Also make sure you import SWA.

In that file, add your schema (class and property definitions) to the Imports view.

Now you can navigate to the schema:Person class and find the property ui:instanceView. Add an empty row and create a valid HTML snippet that has ui:id="form":

If you have just created the .ui.ttlx file, you may need to tell TopBraid to update its SWP graph registry. In TopBraid 4.3 or above this is done via System > Refresh TopBraid system registries. In earlier versions this was called Model > Refresh global SWP graphs....

If you now navigate to an instance of your class, and open the Browser tab, you should see something like the following.

We are now ready to edit the actual form content in the ui:instanceView.

3 swa:Objects and swa:Object

SWA defines a collection of SWP elements that can be used as "HTML" tags. The most important one for forms is swa:Object and its plural form swa:Objects. The difference between them is that swa:Object will only allow users to enter a single value. While the system will heed cardinality restrictions on the properties, not all ontologies have this information, and it is then often easier to simply encode this information for the user interface.

    <div ui:id="form">
        <swa:Object arg:predicate="schema:givenName" arg:subject="{= ?this }" />
    </div>

Produces the following screen:

swa:Object has two required arguments: arg:predicate points to the property that shall be displayed, and arg:subject is the main resource (here: the currently displayed instance ?this). Since we will have many swa:Objects on our form and we do not want to repeat the same information over and over again, we can declare a default value for arg:subject in the surrounding div element:

    <div ui:id="form" default:subject="{= ?this }" default:tabular="{= true }">
        <swa:Object arg:predicate="schema:givenName" />
        <swa:Object arg:predicate="schema:familyName" />
    </div>

On this occasion we have also set the default value for arg:tabular to be true so that property values and labels are nicely aligned below each other. Note that we need to use the {= true } notation and cannot simply write default:tabular="true" because this would be the string value "true" and not the xsd:boolean value of true. The output is now:

4 swa:ObjectsEnum

In order to visually group similar properties together, we can use swa:ObjectsEnum.

    <div ui:id="form" default:subject="{= ?this }" default:tabular="{= true }">
        <swa:ObjectsEnum arg:label="Name and Titles">
            <swa:Object arg:predicate="schema:honorificPrefix"/>
            <swa:Object arg:predicate="schema:givenName"/>
            <swa:Object arg:predicate="schema:familyName"/>
            <swa:Object arg:predicate="schema:honorificSuffix"/>
        </swa:ObjectsEnum>
    </div>

The main role of swa:ObjectsEnum is to provide a uniform way to display a header above the section, as shown below.

swa:ObjectsEnum also supports a flag arg:openable that can display a small triangle next to the header so that users can open or close that section.

    <div ui:id="form" default:hideIfEmpty="{= true }" default:subject="{= ?this }" default:tabular="{= true }">
        <swa:ObjectsEnum arg:label="Name and Titles" arg:openable="true">
            <swa:Object arg:predicate="schema:honorificPrefix"/>
            <swa:Object arg:predicate="schema:givenName"/>
            <swa:Object arg:predicate="schema:familyName"/>
            <swa:Object arg:predicate="schema:honorificSuffix"/>
        </swa:ObjectsEnum>
    </div>

In the example above we have also set another default value for arg:hideIfEmpty to make sure that properties without a value do not show up:

There can be any number of swa:ObjectsEnums on a form.

    <div ui:id="form" default:hideIfEmpty="{= true }" default:subject="{= ?this }" default:tabular="{= true }">
        <swa:ObjectsEnum arg:label="Name and Titles" arg:openable="true">
            <swa:Object arg:predicate="schema:honorificPrefix"/>
            <swa:Object arg:predicate="schema:givenName"/>
            <swa:Object arg:predicate="schema:familyName"/>
            <swa:Object arg:predicate="schema:honorificSuffix"/>
        </swa:ObjectsEnum>
        <swa:ObjectsEnum arg:label="Contact Details">
            <swa:Object arg:predicate="schema:email"/>
            <swa:Object arg:predicate="schema:telephone"/>
            <swa:Objects arg:predicate="schema:address"/>
        </swa:ObjectsEnum>
        <swa:ObjectsEnum arg:label="Personal and Family Information">
            <swa:Object arg:predicate="schema:gender"/>
            <swa:Object arg:predicate="schema:nationality"/>
            <swa:Object arg:predicate="schema:birthDate"/>
            <swa:Object arg:predicate="schema:deathDate"/>
            <swa:Objects arg:predicate="schema:spouse"/>
            <swa:Objects arg:label="parents" arg:predicate="schema:parent"/>
            <swa:Objects arg:predicate="schema:knows"/>
        </swa:ObjectsEnum>
    </div>

If an object doesn't have a value for any of the properties in an enumeration, then the form will also suppress the header (here: Contact Details). Also note that the swa:Objects for schema:parent has an explicit arg:label "parents" that will be used instead of the declared label of the property "parent".

5 swa:ObjectsPlaceholder

If your class has other properties that do not fit into any enumeration category, or if you do now know which other properties may exist, you can insert an swa:ObjectsPlaceholder that will insert all properties that have not been explicitly enumerated elsewhere:

    <div ui:id="form" default:hideIfEmpty="{= true }" default:subject="{= ?this }" default:tabular="{= true }">
        <swa:ObjectsEnum arg:label="Name and Titles" arg:openable="true">
            <swa:Object arg:predicate="schema:honorificPrefix"/>
            <swa:Object arg:predicate="schema:givenName"/>
            <swa:Object arg:predicate="schema:familyName"/>
            <swa:Object arg:predicate="schema:honorificSuffix"/>
        </swa:ObjectsEnum>
        <swa:ObjectsEnum arg:label="Contact Details">
            <swa:Object arg:predicate="schema:email"/>
            <swa:Object arg:predicate="schema:telephone"/>
            <swa:Objects arg:predicate="schema:address"/>
        </swa:ObjectsEnum>
        <swa:ObjectsEnum arg:label="Personal and Family Information">
            <swa:Object arg:predicate="schema:gender"/>
            <swa:Object arg:predicate="schema:nationality"/>
            <swa:Object arg:predicate="schema:birthDate"/>
            <swa:Object arg:predicate="schema:deathDate"/>
            <swa:Objects arg:predicate="schema:spouse"/>
            <swa:Objects arg:label="parents" arg:predicate="schema:parent"/>
            <swa:Objects arg:predicate="schema:knows"/>
        </swa:ObjectsEnum>
        <swa:ObjectsPlaceholder arg:label="Other Properties"/>
    </div>

The output is as follows (pruned at the bottom - there are too many properties here).

To explicitly suppress certain properties, you can mark them by setting swa:hiddenProperty to true in either the domain model or a .ui.ttlx file.

6 Inverse properties with swa:Subjects

So far we have only looked at triples that point from a given subject to its objects. SWA also supports an element swa:Subjects that can be used to display (and edit) values in the other direction. In our example the property schema:parent has values that point from a child to his or her parents. However, we also want to go in the other direction, and point from a parent to his or her children:

    <div ui:id="form" default:hideIfEmpty="{= true }" default:subject="{= ?this }" default:tabular="{= true }">
        <swa:ObjectsEnum arg:label="Name and Titles" arg:openable="true">
            <swa:Object arg:predicate="schema:honorificPrefix"/>
            <swa:Object arg:predicate="schema:givenName"/>
            <swa:Object arg:predicate="schema:familyName"/>
            <swa:Object arg:predicate="schema:honorificSuffix"/>
        </swa:ObjectsEnum>
        <swa:ObjectsEnum arg:label="Contact Details">
            <swa:Object arg:predicate="schema:email"/>
            <swa:Object arg:predicate="schema:telephone"/>
            <swa:Objects arg:predicate="schema:address"/>
        </swa:ObjectsEnum>
        <swa:ObjectsEnum arg:label="Personal and Family Information">
            <swa:Object arg:predicate="schema:gender"/>
            <swa:Object arg:predicate="schema:nationality"/>
            <swa:Object arg:predicate="schema:birthDate"/>
            <swa:Object arg:predicate="schema:deathDate"/>
            <swa:Objects arg:predicate="schema:spouse"/>
            <swa:Objects arg:label="parents" arg:predicate="schema:parent"/>
            <swa:Subjects arg:label="children" arg:object="{= ?this }" arg:predicate="schema:parent"/>
            <swa:Objects arg:predicate="schema:knows"/>
        </swa:ObjectsEnum>
        <swa:ObjectsPlaceholder arg:label="Other Properties"/>
    </div>

Note that swa:Subjects requires the arg:object in addition to the arg:predicate, and in many cases you may want to specify an arg:label. The output now includes the children:

Putting it all together, the final form definition looks like: