Chapter 6. Lists and Query Browsing

6.1. Introduction

A common task in web applications is displaying tables. This is a simple task if the tables are small and do not contain a lot of rows. The whole table can be visible at once and there is no need to split the data into pages as well as provide an option to order the table by new column or display a search form that can be used to filter the data. In such cases where these features must be available, Aranea provides widget org.araneaframework.uilib.list.ListWidget and some support classes. In this chapter we will introduce these widgets and supporting API and show how to use and extend them.

ListWidget is used to define the list columns, the way the list can be filtered and ordered, how many items are shown per pages etc. ListWidget uses org.araneaframework.uilib.list.dataprovider.ListDataProvider to get the current list items range that also match with the current filter and order conditions. ListDataProvider can cache whole table and provide the ListWidget with the appropriate item range or fetch only the specific item range from database. Aranea provides two implementations:

org.araneaframework.uilib.list.dataprovider.MemoryBasedListDataProvider
This is the memory-based solution that must be provided with the whole data at once. It does filtering , ordering and paging memory-based. The data source is not restricted here. This is a very fast and easy-to-use solution for tables with few (typically less than 100) rows.
org.araneaframework.uilib.list.dataprovider.BackendListDataProvider
This is the database solution that will cache only the current item range and executes a new database query each time a filtering , ordering or paging conditions change. This is a powerful solution for tables with more than 500 rows.

6.2. Lists API

6.2.1. A Typical List

A typical list will be created used like this:

...
private BeanListWidget myList;
...
protected void init() {
  ...
  myList = new BeanListWidget(MyModel.class);

  myList.setOrderableByDefault(true);
  myList.addField("name", "#Name").like();
  myList.addField("surname", "#Surname").like();
  myList.addField("phone", "#Phone no").like();
  myList.addField("birthdate", "#Birthdate").range();
  ...
  myList.setInitialOrder("name", true);
  myList.setListDataProvider(new MyListDataProvider());  
  addWidget("myList", myList);
  ...
}
...

Note that here we basically do following things:

Create the list
The line new BeanListWidget(MyModel.class) creates a new list widget that is associated with the JavaBean model class MyModel .
Make fields orderable
The line myList.setOrderableByDefault(true) configures the following fields as orderable.
Add list fields
The line myList.addField("name", "#Name").like() adds a field associated with the JavaBean property "name" (this is also the identifier of the field), with a label "Name", makes the field filterable by Like filter.
Set the initial list order
The line myList.setInitialOrder("name", true) sets the list to be ordered by field "name" by default.
Set the list data provider
The line myList.setListDataProvider(new MyListDataProvider()) sets the data provider for the list.
Register the list
The line addWidget("myList", myList) initializes and registers the list allowing it to function.

Now that we have created the list we show how to build a simple data provider. The following example code should be in the same widget as the previous:

...
private class MyMemoryBasedListDataProvider extends MemoryBasedListDataProvider {
  protected MyMemoryBasedListDataProvider() {
    super(MyModel.class);
  }
  public List loadData() throws Exception {             
    return lookupMyService().findAllMyModel();
  }
}
...

The line super(MyModel.class) associated this MemoryBasedListDataProvider with the JavaBean model class MyModel. The method List loadData() implements loading all table rows and returning it as a List object.

Later, we will also discover using org.araneaframework.uilib.list.dataprovider.BackendListDataProvider.

6.2.2. Fields

As the list may be displayed as a table, it is basically an ordered collection of items. In the previous example, we defined a list of MyModel.class typed items that have fields 'name', 'surname', 'phone' and 'birthdate'. By listing of MyModel.class, we also told ListWidget the corresponding field types, e.g String.class, String.class, Long.class and Date.class. In fact this feature of reflection is the only distinction between the ListWidget and BeanListWidget.

Each list field have its own Id, label and type. The labels are used to automatically create a corresponding title row above the displayed table. The types are used to describe how to order or filter the whole data using this field. E.g String.class is treated differently than other types, because usually one would prefer to order by this field ignoring the case. Both the labels and types are also used to build a corresponding search form - an automatically built FormWidget - for the list.

If we would like to add some list fields that are not MyModel.class fields, we can pass it's type to the ListWidget like following:

myList.addField("income", "#Income", BigDecimal.class);

Here the myList could be just a ListWidget rather than a BeanListWidget.

When adding a list field, we can also provide this field-related ordering and filtering information.

6.2.3. Ordering

Each list field can be orderable or not. We already discovered ListWidget's method setOrderableByDefault(boolean) that switch whether to configure fields that are added afterwards orderable or not. This method can be used several times in the list configuration.

Another way is to set each field individually orderable or not when they are added to the list. In such case add additional boolean argument to the addField() method such as:

myList.addField("phone", "#Phone no", false);

Notice the false as third parameter. true means that the list can be ordered by this field and false means the opposite. By not providing this parameter, simply the last value is used which has been set by setOrderableByDefault(boolean) method.

In addition, we already used method setInitialOrder(String, boolean). It sets a specified field (the first argument) to be ordered by default. true as the second argument tells the ordering should be ascending, false would mean descending. By not providing this information, the list is displayed in the original order.

6.2.4. Filtering

Filtering means that we only display a certain list items. The list can be filtered using its fields and data provided by the search from of this list.

For this, we must provide the ListWidget with the corresponding org.araneaframework.uilib.list.structure.ListFilters and FormElements. As the form elements are dummy "boxes" that hold search data, each ListFilter is related to a certain filter test, e.g. equality, greater than comparison etc. Each ListFilter also knows what information it must consider. In general, one list field is compared against a value provided by the search form. It's also assumed that a blank search field means that this particular ListWidget is currently disabled.

Fortunately, in most cases it's unnecessary to add these search fields manually. Instead, if one is adding a list field, he or she can assign both the ListFilter and FormElement for this field very simply:

myList.addField("address", "#Address").like();

Here we simply add an 'Address' field providing it with label and telling there's should be a Like filter for this field. By this, we automatically add a TextControl into the search form. By filling it with value 'Paris', we will see only rows which 'Address' field contain 'Paris', 'paris', 'PARIS' etc.

To describe, how this works, we show a longer version of the previous code:

myList.addField("address", "#Address");
myList.getFilterHelper().like("address");

So there's a special class org.araneaframework.uilib.list.structure.filter.FilterHelper that is used to add list filters. All ListWidget.addField() methods just return a little different version of this helper class, called a FieldFilterHelper. It's methods do not need a field Id and thus make one not to repeat the same field Id for each filter. In general, the shorter usage is recommended of course. However some filters are more complicated and may be related to more than one list field. For those, one must use the FilterHelper instead.

By default all filters that deal with the Strings are case insensitive. To configure some filters to be different, use the following:

myList.addField("country", "#Country").setIgnoreCase(false).like();
myList.addField("city", "#City").like();
myList.addField("address", "#Address").setIgnoreCase(true).like();

This can be explained following: Before adding a Like filter for the 'country' field, we switched to the case sensitive mode. And before adding a filter for the 'address' field, we switched to the case insensitive mode. Thus the city's filter is case sensitive as the country's but the address' filter does ignore the case.

This state is held by the FilterHelper and can be modified either by calling a method of the FilterHelper or the FieldFilterHelper. In such way, the following parameters can be set:

Case sensitivity
By using setIgnoreCase(boolean) one assigns new filters to ignore case (default) or not. This applies to filters that use String comparison.
Strict/non-strict
By using setStrict(boolean) one assigns new filters to disallow equality or not. By default equality is not allowed (strict). This applies to filters such as GreaterThan, LowerThan, Range etc.

Now, let's show which filters we have got:

FilterHelper methodListFilter classDescription
eq()EqualFilterTests if the value of a certain list field is equal to the value of a certain search form field. The filter is disabled if the search field is blank.
eqConst()EqualFilterTests if the value of a certain list field is equal to a certain constant. This filter is always enabled.
gt(), lt()GreaterThanFilter, LowerThanFilterTests if the value of a certain list field is greater than (lower than) the value of a certain search form field. This filter is disabled if the search field is blank.
gtConst(), ltConst()GreaterThanFilter, LowerThanFilterTests if the value of a certain list field is greater than (lower than) a certain constant. This filter is always enabled.
like()LikeFilterTests if the pattern in a certain search form field matches with the value of a certain list field. This corresponds to the LIKE expression in SQL with some modifications. By default, it takes '%' and '*' symbols as any-string wildcards and '_', '.' and '?' as any-symbol wildcards. In addition, the pattern does not have to match with the whole string ('%' is automatically added before and after the pattern string). The wildcards and their automatic adding is configured by the org.araneaframework.uilib.list.util.like.LikeConfiguration which is found from the Aranea org.araneaframework.uilib.ConfigurationContext. This filter is identical in memory-based and database backend usage. This filter is disabled if the search field is blank.
likeConst()LikeFilterTests if a certain constant pattern matches with the value of a certain list field. This filter is always enabled.
isNull(), notNull()NullFilterTests if the value of a certain list field is null (is not null) if the value of a certain search form field equals to a specified value.
isNullConst(), notNullConst()NullFilterTests if the value of a certain list field is null (is not null). This filter is always enabled.
range()RangeFilterTests if the value of a certain list field is between two values of certain search form fields. The filter is identical to the greater than or lower than filter in case of one of the search fields is blank. This filter is disabled if both search fields are blank.
fieldRangeInValueRange(), valueRangeInFieldRange(), overlapRange()RangeInRangeFilterTests if two values of certain list fields are between two values of certain search form fields, vice-versa or do they have a non-empty intersection. This filter is disabled if both search fields are blank.
sqlFunction()SqlFunctionFilterTests if the value returned from a certain SQL function is equal (or is greater than or is lower than) to the value of a certain list field, search form field or a constant. The arguments of the SQL function can also be chosen among the values of list fields, search form fields and constants. This filter cannot be used memory-based. This filter is always enabled.

By default the FormElements added into the search form have the same identifiers as the list fields. Therefore there can be only one search field per list field. If one would like to override the used Id for FormElement, any filter could be added like following:


        myList.addField("country", "#Country").like();
        myList.getFilterHelper().like("country", "anotherCountry");

The first line adds a list field 'country' and a Like filter associated with it as well as a new FormElement with Id of 'country'. The second line adds another Like filter associated to the list field 'country' and a new FormElement with Id of 'anotherCountry'.

By adding a filter, the corresponding FormElement is automatically created and added to the search form of the list. Now we cover the properties of the few FormElement describing their default values and showing how to customize them:

PropertyDefault valueCustomizing
IdSame as the list field Id.Call addField(...).<filter>("myCustomId");
LabelSame as the label of the associated list field.Call addField(...).useCustomLabel("myCustomLabel").xxx(...);
ControlIs selected considering the type of the associated list field:
TypeControl
java.lang.String TextControl
java.math.BigInteger, java.lang.Long, java.lang.Integer, java.lang.Short, java.lang.Byte NumberControl
java.math.BigDecimal, java.lang.Double, java.lang.Float FloatControl
Other subclasses of java.lang.Number FloatControl
java.util.Date, java.sql.Date DateControl
java.sql.Time TimeControl
java.sql.Timestamp DateTimeControl
java.lang.Boolean CheckboxControl
All others TextControl
Call addField(...).xxx(new MyCustomControl());
DataCorresponds to the type of the associated list field.Call addField(...).useFieldType(MyType.class).xxx(...);
Initial valueAlways null to disable the filter by default.After adding the field and the filter call myList.getForm().setValueByFullName("fieldId", customInitialValue); or add a custom Formelement.
MandatoryAlways false as all search conditions are optional.After adding the field and the filter call myList.getForm().getElementByFullName("fieldId").setMandatory(true); or add a custom Formelement.
FormElementSee all properties above.To use a custom Formelement, call addField(...).xxx(new MyCustomFormElement(...));. To disable adding it at all, call addField(...)._xxx(); (notice the underscore).

The xxx marks any filter adding method. As one can count, there are 6 overridden methods for each list filter: 2 versions for providing a custom Id or not and 3 versions for providing a custom FormElement, Control or neither of them. In addition there are methods that start with an _ for disabling adding a form element. Using the FilterHelper instead of FieldFilterHelper is analogous except all filter adding methods take the list field Id as the first argument in addition.

It's import to notice that xxxxConst methods do not create a form element because they are independent of the search form at all - they are constant. However they can actually take a value Id for the defined constants as well. These Ids can be used later to convert specific values when creating a database query. Of course non-constant filters have the same Ids but just use them mainly to get values from the search form. xxxxConst filters have 2 overridden add methods depending on whether the custom value Id is provided or not. By default it's the same as the field Id.

6.2.5. Backend Data Provider

Now that we have demonstrated defining lists and also creating MemoryBasedListDataProvider, we will discover using BackendListDataProvider. The following example code should be in the same widget as constructing of the related ListWidget.

...
private class MyBackendListDataProvider extends BackendListDataProvider {
  public MyBackendListDataProvider() {
    super(true);
  }
  protected ListItemsData getItemRange(ListQuery query) throws Exception {
    return lookupMyService().findMyModel(query);
  }
}
...

The line super(true) constructs BackendListDataProvider with cache enabled (only used when there are no change in query). Notice that there is no association with any JavaBean class here. The method ListItemsData getItemRange(ListQuery query) implements loading current item range according to the range indexes and filtering and ordering conditions. org.araneaframework.backend.list.mode.ListQuery and org.araneaframework.backend.list.mode.ListItemsData may be thought as being input and output of each list data query.

ListQuery is a simple JavaBean that holds the following properties:

List item range indexes
This is 0-based start index and items count ( Long objects) that define the range. By default, lists are shown by pages. Although all items can be shown at once also. Then the start index is zero and items count is omitted.
Filter expression
This could be thought as an abstraction of previously defined bunch of ListFilter objects that are provided with the bound values from filter form . This is a basis of constructing a SQL WHERE clause later.
Order expression
This could be thought as an abstraction of previously defined ordered columns that are provided with the current ordering condition. This is a basis of constructing a SQL ORDER BY clause later.

Generally, this whole object is just passed to org.araneaframework.backend.list.helper.ListSqlHelper class that is used to generate SQL statements and fetching the results from database. Latter is hold in ListItemsData object which is a simple JavaBean that holds the following properties:

List items range
Model objects that are the result of the query .
Total count
Total count ( Long object) of the list. This is important information for navigating through the whole list. Notice that this depends only on filtering conditions.

Notice that BackendListDataProvider actually do not depend on using databases. It just provides a simple query object and expects a simple result to be returned. Thus, you have the power to use it as you like. At the same, Aranea provides a very useful class org.araneaframework.backend.list.helper.ListSqlHelper that generate SQL statements and fetches the results from database. We strongly recommend it together with its subclasses that support different database systems. Currently Oracle (OracleListSqlHelper) and HSQL (HsqlListSqlHelper) databases are supported.

The following example discovers the simplest usage of ListSqlHelper. The following code should be in a service class instead of previously discovered Widget:

public class MyService {
  ...
  private DataSource dataSource;
  ...
  public ListItemsData findMyModel(ListQuery query) {
    ListSqlHelper helper = new OracleListSqlHelper(this.dataSource, query);

    helper.addMapping("name", "NAME");
    helper.addMapping("surname", "SURNAME");
    helper.addMapping("phone", "PHONE_NO");

    helper.setSimpleSqlQuery("PERSON");
    return helper.execute(MyModel.class);
  }
  ...
}

Method ListItemsData findMyModel(ListQuery query) does the following:

Constructs and initializes the helper
The line ListSqlHelper helper = new OracleListSqlHelper(this.dataSource, query) constructs OracleListSqlHelper - an Oracle specific ListSqlHelper - and passes it the DataSource and ListQuery data.
Adds column mappings
The line helper.addMapping("name", "NAME") defines that identifier of column "name" will be converted into "NAME" when used in an SQL statement. There may be lot of differnece between JavaBean properties names and database fields names. The same database identifier ( "NAME" ) is used when fetching data from ResultSet by default. This could also have another identifier set by providing it as the third argument.
Provides the helper with a simple SQL query
The line helper.setSimpleSqlQuery("PERSON") sets the whole SQL query with parameters using only the given database table name. Filtering and ordering is added automatically according to the ListQuery data.
Executes the query and retrieve the data
The line return helper.execute(MyModel.class) executes and retrieves data of both total count and items range queries. The ResultSet is read using the default BeanResultReader .

The following example discovers the custom usage of ListSqlHelper.

public class MyService {
  ...
  private DataSource dataSource;
  ...
  public ListItemsData findMyModel(ListQuery query) {
    ListSqlHelper helper = new OracleListSqlHelper(this.dataSource, query);

    helper.addMapping("name", "NAME");
    helper.addMapping("surname", "SURNAME");
    helper.addMapping("phone", "PHONE_NO");

    StringBuffer s = new StringBuffer();
    s.append("SELECT ");
    s.append(helper.getDatabaseFields());
    s.append(" FROM PERSONS");
    s.append(helper.getDatabaseFilterWith(" WHERE ", ""));
    s.append(helper.getDatabaseOrderWith(" ORDER BY ", ""));

    helper.setSqlQuery(s.toString());
    helper.addStatementParams(helper.getDatabaseFilterParams());
    helper.addStatementParams(helper.getDatabaseOrderParams());

    return helper.execute(MyModel.class);
  }
  ...
}

Method ListItemsData findMyModel(ListQuery query) does the following:

Constructs and initializes the helper
The line ListSqlHelper helper = new OracleListSqlHelper(this.dataSource, query) constructs OracleListSqlHelper - an Oracle specific ListSqlHelper - and passes it the DataSource and ListQuery data.
Adds column mappings
The line helper.addMapping("name", "NAME") defines that identifier of column "name" will be converted into "NAME" when used in an SQL statement. There may be lot of differnece between JavaBean properties names and database fields names. The same database identifier ( "NAME" ) is used when fetching data from ResultSet by default. This could also have another identifier set by providing it as the third argument.
Gets SQL substrings from the helper
The line helper.getDatabaseFields() returns just the comma-separated list of database column identifiers that were just defined in the mapping. This does not depend on the original set of list columns at all. The line helper.getDatabaseFilterWith(" WHERE ", "") returns the WHERE clause body with the provided prefix and suffix. It returns an empty string if there is no filter condition currently set (it does not mean there are no filters defined). Notice that we only deal with SQL strings here. As ListSqlHelper uses PreparedStatement objects to execute queries, there must be provided statement parameters in addition to the SQL string. This generally provides better performance of executing similar queries.
Constructs SQL query string
StringBuffer is used to construct the whole SQL query string. Notice that the helper does not construct it totally by itself. This lends user more power for complex queries. It is very important that the constructed query is for getting all rows that match with the current filter and order conditions, but not the range conditions. ListSqlHelper always executes two queries: one for getting the items count and another for getting the items range. Generally, both of these can be easily constructed from this one provided query. This implementation depends on the database system and therefore the concrete ListSqlHelper subclass.
Gets SQL parameters from the helper
The line helper.getDatabaseFilterParams() returns SQL parameters of WHERE clause or empty list if there are none.
Provides the helper with the SQL query
The line helper.setSqlQuery(...) sets the SQL string and the line helper.addStatementParams(...) adds the query parameters ( ListSqlHelper uses PreparedStatement s). Of course, the order of parameters must match with the SQL string.
Executes the query and retrieve the data
The line return helper.execute(MyModel.class) executes and retrieves data of both total count and items range queries. The ResultSet is read using the default BeanResultReader.

ListSqlHelper mappings and converters

All Aranea List filters that are propagated with values from the filter form construct an expression chain. This chain is built each time any condition is changed. E.g if one is searching for persons whose birthday is between July 6th, 1950 and Sept 2nd, 1990 then there's one value 'Birthday' and two values 'July 6th, 1950' and 'Sept 2nd, 1990' which have 'Birthday_start' and 'Birthday_end' as names. Ordering the list is done the same. When retrieving data from database all these information must be considered to build an appropriate query. Therefore all these variables must be mapped to database fields. When reading the query results Bean fields must be mapped to ResultSet columns. In general, these Bean fields match exactly with the variables. But considering more specific cases, they are not assumed to be the same.

The following list covers the terms that are used when configuring ListSqlHelper:

Expression variable
Variables can be thought as list columns that are used in filtering and ordering the list. Variable's name can be for example 'Birthday'.
Expression value
Values are the temporary information in the list filtering. '1980-08-21' is a value. 'Birthday_start' is a name of a value. In simple cases each variable matches with one value. In case of the range filter two different values (start and end of the range) are used. Also one value can be used together with two or more variables. A value identifier is used for optional converting before using it in a query. This is done by adding a Converter object to ListSqlHelper . E.g. booleans have to be converted into numeric (0 or 1) values.
Database field
Database field can be for example 'age' or 'company.name' as well as 'MAX(price)' or '(SELECT(COUNT(*) FROM document WHERE userId = user.id)' (an inner SELECT) - any expression that is part of a SQL string.
Database field alias
Database field alias is for example 'name', 'total_price' etc. It's just an identifier not a whole expression. In ListSqlHelper one can assign an alias for each database field or have it automatically generated. The result of a query is a table - a ResultSet - which columns have the same names as the aliases in the query. An alias can also be used in a custom filter condition (WHERE clause) to identify the same database field or expression that was added in the SELECT clause.
ResultSet column
ResultSet is a table that is retrieved from database as a result of executing the query. Generally all the columns that where added in a SELECT can be retrieved from the ResultSet by their aliases. But the ResultSet can also contain additional database-specific data such as rownum etc. ResultSet column can also be assigned a Converter . E.g. to convert numeric value into a Boolean. Here is the Converter used reversely.
Bean field
Bean field (or property) is for example 'phone' or 'address.city' that is an instance field of a Java object. During the ResultSet reading new instance of the given type is created and its fields are propagated with values. E.g. 'phone' is assigned by calling setPhone(String phone), 'address.city' is assigned by calling setAddress(new Address()) (if getAddress() == null) and getAddress().setCity(String city).

ListSqlHelper methods for configuring mappings:

MethodPurpose
addMapping(String variable, String dbField, String dbAlias, String beanField)Adds mapping between expression variable, database field and Bean field. Database field alias and Bean field are assigned manually.
addMapping(String variable, String dbField, String dbAlias)Adds mapping between expression variable, database field and Bean field. Database field alias is assigned manually. Bean field is assigned the same as the variable.
addMapping(String variable, String dbField)Adds mapping between expression variable, database field and Bean field. Database field alias is generated automatically. Bean field is assigned the same as the variable. (mostly used)
addDatabaseFieldMapping(String variable, String dbField, String dbAlias)Adds mapping between expression variable and database field. Database field alias is assigned manually.
addDatabaseFieldMapping(String variable, String dbField)Adds mapping between expression variable and database field. Database field alias is generated automatically.
addResultSetMapping(String rsColumn, String beanField)Adds mapping between ResultSet column (matches with database field alias) and Bean field. (rarely used)

ListSqlHelper methods for configuring converters:

MethodPurpose
addDatabaseFieldConverter(String value, Converter converter)Adds converter for expression value.
addResultSetDeconverterForBeanField(String beanField, Converter converter)Adds deconverter for ResultSet column by Bean field that is mapped with that Column.
addResultSetDeconverterForColumn(String rsColumn, Converter converter)Adds deconverter for ResultSet column.

6.3. List JSP Tags

6.3.1. <ui:list>

Starts a list context. List view model, list sequence view model and list id are made accessible to inner tags as EL variables.

Attributes

AttributeRequiredDescription
idnoList widget id.
varSequencenoName of variable that represents list sequence info (by default listSequence).

Variables

VariableDescriptionType
listView model of list.ListWidget.ViewModel
listSequence (unless changed with varSequence attribute).View model of list sequence info.SequenceHelper.ViewModel
listIdId of list.String
listFullIdFull id of list.String

Examples

<?xml version="1.0" encoding="UTF-8"?>
<ui:list id="list">
    ...
</ui:list>

6.3.2. <ui:listFilter>

Represents list filter. Introduces an implicit form (<ui:form>), so one can place form elements under it.

This tag has no attributes.

Examples

<?xml version="1.0" encoding="UTF-8"?>
<ui:list id="list">
  <ui:listFilter>
    <ui:row>
        <ui:cell>
            <ui:textInput id="field1Filter"/>
        </ui:cell>
    
        <ui:cell>
            <ui:textInput id="field2Filter"/>
        </ui:cell>

        ...

    </ui:row>
  </ui:listFilter>
</ui:list>

6.3.3. <ui:listFilterButton> and <ui:listFilterClearButton>

<ui:listFilterButton> renders list's filter form filtering activation button and registers a keyboard handler, so that pressing ENTER key in any filter form field activates list filtering. <ui:listFilterClearButton> renders list's filter form clearing button, pressing it sends server-side event that clears all active list filters.

Both of these tags must be used inside <ui:listFilter> tag.

Attributes

AttributeRequiredDescription
renderModenoPossible values are button, input—filter button is rendered with corresponding HTML tags, or empty in which case JSP author must provide suitable content for this tag by themself (with an image, for example). Default rendermode is button.
onClickPreconditionnoPrecondition for deciding whether registered onclick event should go server side or not. If left unspecified, this is considered to be true.
showLabelnoIndicates whether button label is shown.Value should be true or false, default is false—using true is pointless with these particular tags, it only has some effect when specified renderMode is empty and tags body is left empty too.
Also have all common form element rendering attributes plus standard style and styleClass attributes.

Examples

<?xml version="1.0" encoding="UTF-8"?>
...
  <ui:listFilter>
    <ui:row>
      <!-- Bunch of filter fields in cells -->
      <ui:cell>
        <ui:listFilterButton/>
        <ui:listFilterClearButton/>
      </ui:cell>
    <ui:row>
  </ui:listFilter>

6.3.4. <ui:listRows>

Iterating tag that gives access to each row on the current list page. The row is by default accessible as EL variable row.

Attributes

AttributeRequiredDescription
varnoName of variable that represents individual row (by default "row").

Variables

VariableDescriptionType
row (unless changed with var attribute).Object held in current row.Object

Examples

<?xml version="1.0" encoding="UTF-8"?>
<ui:list id="list">
  <ui:listFilter>
    ...
  </ui:listFilter>
  
  <ui:listRows>
    <ui:row>
        <!-- In each row, object in this list row is accessible -->
        <ui:cell>
            <c:out value="${row.field1}"/>
        </ui:cell>

        <ui:cell>
            <c:out value="${row.field2}"/>
        </ui:cell>

        ...
    </ui:row>
  </ui:listRows>
</ui:list>

6.3.5. <ui:listRowButton>

Represents an HTML form button (not tied to any Control or FormElement). Default styleClass="aranea-button", rendered with HTML <button ...> tag.

Attributes

AttributeRequiredDescription
eventId no Event triggered when button is clicked.
id no Button id, allows to access button from JavaScript.
labelId no Id of button label.
onClickPrecondition no Precondition for deciding whether onclick event should go server side or not. If left unspecified this is set to return true;.
tabindexnoThis attribute specifies the position of the current element in the tabbing order for the current document. This value must be a number between 0 and 32767.
Also has standard styleClass, updateRegions and globalUpdateRegions attributes.

6.3.6. <ui:listRowLinkButton>

Represents a HTML link with an onClick JavaScript event. Default styleClass="aranea-link-button", rendered with HTML <a href="javascript:" ...> tag.

Attributes

AttributeRequiredDescription
eventId no Event triggered when link is clicked.
id no Link id, allows to access link from JavaScript.
labelId no Id of link label.
onClickPrecondition no Precondition for deciding whether onclick event should go server side or not. If left unspecified this is set to return true;.
tabindexnoThis attribute specifies the position of the current element in the tabbing order for the current document. This value must be a number between 0 and 32767.
Also has standard styleClass, updateRegions and globalUpdateRegions attributes.

Examples

<?xml version="1.0" encoding="UTF-8"?>
<ui:list id="list">
  ...  
  <ui:listRows>
    <ui:row>
      ...
      <ui:cell>
        <ui:listRowLinkButton eventId="edit">
          <img src="editButton.png"/>
        </ui:listRowLinkButton>
      </ui:cell>
      ...
    </ui:row>
  </ui:listRows>
</ui:list>

6.4. Editable Lists

EditableListWidget and EditableBeanListWidget are ListWidgets wrapped around FormListWidget (See Section 5.3, “Form Lists” about it) which gathers data with the help from ListWidget.

Both editable list widgets have just one constructor and one additional getter (compared to ListWidget):

public EditableListWidget(FormRowHandler rowHandler);
public EditableBeanListWidget(FormRowHandler rowHandler, Class beanClass);

// gets the wrapped form list
public BeanFormListWidget getFormList();

Most important component of editable lists is FormListWidget's RowHandler, refer to Section 5.3, “Form Lists” about implementing that interface. Other than required implementation of RowHandler, editable lists do not differ from ListWidgets.

public class SampleEditableListWidget {
  private EditableBeanListWidget list;
  
  protected void init() throws Exception {
    setViewSelector("sampleEditableListView");
    
    list = new EditableBeanListWidget(buildFormRowHandler(), SomeBean.class);
    list.setDataProvider(buildListDataProvider());
    list.setOrderableByDefault(true);
    // list has only two columns of which only one is editable
    list.addField("immutable", "#ImmutableColumnLabel", false);
    list.addField("mutable", "#MutableColumnLabel").like();

    addWidget("sampleEditableList", list);
  }
  
  private FormRowHandler buildFormRowHandler() throws Exception {
    // return formRowHandler, see the form list example
  };
  
  private private ListDataProvider buildListDataProvider() throws Exception {
    // return data provider
  }
}

JSP view for this sample widget is presented below:

...
<ui:formList id="sampleEditableList">
  <!-- List filter definition, usual -->
  <!-- Editable lists body -->
  <ui:formListRows>
    <ui:row>
      <ui:cell>
        <!-- Row object is accessible as 'row' just as in lists -->
        <c:out value="${row.immutable}"/>
      </ui:cell>
      <ui:cell>
        <!-- But the implicit form tag for current row form is also present, so... -->
        <ui:formElement id="mutable">
          <ui:textInput/>
        </ui:formElement>
      </ui:cell>
    </ui:row>
  </ui:formListRows>
</ui:formList>
...

Full editable list example is bundled with Aranea examples.