List view
default settings of a list view:
-
user must have read allowable action
-
renders values as LIST_VALUE
-
shows all readable properties
-
adds update/delete buttons for every item if the user as update and delete action respectively
-
supports paging and sorting
-
allows configuring sortable properties and the default sort
-
includes a form at the top that can be used for adding filters
-
a create button for the entity if the user has create action allowed
-
supports global feedback messages set with the
EntityViewPageHelper
default processors
Customizing a list view
TODO
Customizing the sort behaviour of a specific property can be done by setting a Sort.Order.class
attribute on the EntityPropertyDescriptor
.
You can use this to sort on a different property instead, or to specify behaviour for null handling and case sensitivity.
Filtering a list view
By default a list view is not filtered.
If your entity has an EntityQueryExecutor
available, you can easily activate a simple yet powerful EQL-based filter.
Activating EntityQuery
based filtering can be done through the builders:
@Override
public void configure( EntitiesConfigurationBuilder entities ) {
// Activate the EQL-based filter on every entity that has an EntityQueryExecutor
entities.matching( c -> c.hasAttribute( EntityQueryExecutor.class ) )
.listView( lvb -> lvb.entityQueryFilter( true ) );
}
See this chapter for information on how to configure a custom filter for a list view.
Filtering by access permissions
By default, list views are accessible via the AllowableAction.READ
action, and links to the default views are present if their corresponding action is available.
This means, that regardless of the permissions on a specific item on the list view, the data presented on a list view will be always be accessible if the user has access to the list view itself.
To filter these items based on an action, simply specify the showOnlyItemsWithAction
property on the list view.
This will filter all items on the view itself, so that only items with the specified AllowableAction
are accessible.
AllowableAction
@Override
public void configure( EntitiesConfigurationBuilder entities ) {
entities.withType( Book.class )
.listView( lvb -> lvb.showOnlyItemsWithAction( AllowableAction.READ ) ); (1)
}
1 | Only items for which the authenticated principal has the AllowableAction.READ action will be present in the list view. |
The filtering based on allowable actions happens after the various items have been fetched. As such, a list view can be filtered to search for specific items, and that result set will then be filtered based on the specified action.
See the relevant chapter in security and permissions to learn more about securing views.
Adding a custom filter to a list view
A filter on a list view usually consists of 2 parts:
-
an
EntityViewProcessor
that provides the filtering options on the list view -
an
EntityViewProcessor
that fetches the items using the filtering options-
a custom form is usually added to the
EntityViewCommand.addExtension()
for both postback and optional validation
-
Both parts can easily be combined in a single EntityViewProcessor
.
Custom filter example
The following code illustrates adding a simple filter to a view. The filter uses a separate repository method to lookup entities by name. The filter options are added as a form on top of the list view, the form in this case rendered via a custom Thymeleaf template.
private static class GroupFilteringProcessor extends EntityViewProcessorAdapter
{
@Autowired
private GroupRepository groupRepository;
@Override
public void initializeCommandObject( EntityViewRequest entityViewRequest,
EntityViewCommand command,
WebDataBinder dataBinder ) {
command.addExtension( "filter", "" );
}
@Override
protected void doControl( EntityViewRequest entityViewRequest,
EntityView entityView,
EntityViewCommand command,
BindingResult bindingResult,
HttpMethod httpMethod ) {
String filter = command.getExtension( "filter", String.class );
Pageable pageable = command.getExtension( PageableExtensionViewProcessor.DEFAULT_EXTENSION_NAME, Pageable.class );
if ( !StringUtils.isBlank( filter ) ) {
entityView.addAttribute( "items", groupRepository.findByNameContaining( filter, pageable ) );
}
else {
entityView.addAttribute( "items", groupRepository.findAll( pageable ) );
}
}
@Override
protected void postRender( EntityViewRequest entityViewRequest,
EntityView entityView,
ContainerViewElement container,
ViewElementBuilderContext builderContext ) {
Optional<ContainerViewElement> header = find( container, "entityListForm-header", ContainerViewElement.class );
header.ifPresent(
h -> {
Optional<NodeViewElement> actions
= find( h, "entityListForm-header-actions", NodeViewElement.class );
actions.ifPresent( a -> a.addCssClass( "pull-right" ) );
h.addChild( new TemplateViewElement( "th/entityModuleTest/filters :: filterForm" ) );
}
);
}
}
<fragments xmlns:th="http://www.w3.org/1999/xhtml">
<div class="list-header form form-inline" th:fragment="filterForm">
<div class="form-group">
<label for="group-name-filter">Filter by name:</label>
<input id="group-name-filter" name="extensions[filter]" th:value="${entityViewCommand.extensions['filter']}" type="text" class="form-control" />
</div>
<input type="submit" class="btn btn-default" value="Apply filter" />
</div>
</fragments>
@Override
public void configure( EntitiesConfigurationBuilder entities ) {
entities.withType( Group.class )
.listView( lvb -> lvb
.entityQueryFilter( false ) // optional - disable the previously activated entity query filter
.filter( groupFilteringProcessor() ) // register the custom filter
);
}
List summary view
It is possible to activate a detail view inline in a list view.
If the EntityConfiguration
or EntityAssociation
has a view named listSummaryView a summary pane will automatically become available when clicking on the item row in the table.
The summary pane is called using AJAX and only the content fragment of the page will be rendered.
@Override
public void configure( EntitiesConfigurationBuilder entities ) {
// Activate a summary view in the main user results table using a custom Thymeleaf template
entities.withType( User.class )
.view( EntityView.SUMMARY_VIEW_NAME, vb -> vb.template( "th/myModule/userSummary" ) );
}