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