The lifecycle of creating an EntityListBackingBean:
Start to Refresh Data Model | Authorize Access (access control) | Init Pageable Entity List | Build ViewConfig | Build Property Model | Build Row Command Menu | Build Header and Footer Menus | Post Refresh Data ModelUser must have "View" permission on the entity list before a pageable entity list is initialized from entity list data source. ViewConfig configures the UI view of the pageable entity list.
The following example creates an EntityListBackingBean from a list of entities in memory:
List<Employee> employees = ...; // a list of employees
DataDescriptor dd = new DataDescriptor(Employee.class, true, true);
MemoryEntityList<Employee> employeeList = new MemoryEntityList<Employee>(employees, dd);
EntityDataSource<Employee> eds = new EntityDataSource<Employee>(employeeList);
EntityListBackingBean<Employee> employeeListBackingBean = ContainerBean.createEntityListBackingBean(
parentComponent, Employee.class, eds, viewConfig,
manageMode, containerBean);
The following example creates an EntityListBackingBean from a QueryCriteria: full time employees hired after a certain date, ordering by employee id ascending.
CriteriaElement[] propertyQueryElements = new CriteriaElement[]{
DetachedCriteria.eq("type", Employee.Type.FULL_TIME),
DetachedCriteria.ge("hiredDate", aDate),
DetachedCriteria.asc("nid")
};
QueryCriteria<Employee, Employee> queryCriteria = new QueryCriteria<Employee, Employee>(Employee.class, propertyQueryElements);
EntityDataSource<Employee> eds = new EntityDataSource<Employee>(queryCriteria);
EntityListBackingBean<Employee> employeeListBackingBean = ContainerBean.createEntityListBackingBean(
parentComponent, Employee.class, eds, viewConfig,
manageMode, containerBean);
public class EmployeeBean extends EntityBackingBean<Employee> {
@Override
protected EntityListViewConfig buildEntityListViewConfig(
EntityListBackingBean<Employee> entityListBackingBean,
EntityListViewConfig viewConfig) throws SystemException {
viewConfig = super.buildEntityListViewConfig(entityListBackingBean, viewConfig);
// hide employee photos in list view
viewConfig.addPropertiesToHide("photos");
return viewConfig;
}
}
@Override
protected void postRefreshEntityListPropertyModel(EntityListBackingBean<T> entityListBackingBean,
List<EntityProperty<T>> entityProperties) throws SystemException {
}
@Override
protected void addRowCommandMenuNodes(EntityListBackingBean<T> entityListBackingBean,
MenuBean menuBean) throws SystemException {
super.addRowCommandMenuNodes(entityListBackingBean, menuBean);
MenuNode downloadMenuNode = new MenuNode(menuBean, COMMAND_DOWNLOAD, RenderStyle.COMMAND_LINK);
downloadMenuNode.getPartialBehaviorSupport(true).disableActionAjax();
menuBean.add(downloadMenuNode);
}
To handle row command menu node action:
@Override
protected PageNavigation clickEntityRowCommand(EntityListBackingBean<T> entityListBackingBean,
T entity, EntityProperty<T> property, MenuNode menuNode) throws SystemException {
String command = menuNode.getCommand();
if (command.equals(COMMAND_DOWNLOAD)) {
// handle action
}
return super.clickEntityRowCommand(entityListBackingBean, entity, property, menuNode);
}
The nested viewConfig name for row command menu is "rowCommands", and it can also be accessed by
MenuViewConfig rowCommandMenuViewConfig = entityListViewConfig.getRowCommandMenuViewConfig(true);
So the menu can be configured in buildEntityListViewConfig().
@Override
protected void addMenuNodes(boolean groupQuery) throws SystemException {
super.addMenuNodes(groupQuery);
// add upload menu node
if (isHasCreatePermission()) {
MenuBean menuBean = getMenuBean(COMMAND_UPLOAD, false);
if (menuBean != null) {
MenuNode uploadMenuNode = new MenuNode(menuBean,
COMMAND_UPLOAD, RenderStyle.COMMAND_BUTTON);
uploadMenuNode.setIcon(IconMap.UI_ICON_UPLOAD);
menuBean.add(uploadMenuNode);
}
}
}
To handle menu node actions,
@Override
public PageNavigation clickMenuNode(MenuNode menuNode) throws SystemException {
String command = menuNode.getCommand();
if (command.equals(COMMAND_UPLOAD)) {
...
return null;
}
return super.clickMenuNode(menuNode);
}
Like an EntityBackingBean, header or footer MenuViewConfig can be configured to
show/hide commands and specify which menu nodes are shown in header or footer menu.
@Override
protected MenuViewConfig getHeaderMenuViewConfig() {
MenuViewConfig viewConfig = MenuViewConfig(MenuViewConfig.MenuStyle.MENU_BAR, RenderStyle.COMMAND_LINK, true);
// show some menu nodes
viewConfig.setMenuNodesToShow(...);
viewConfig.addMenuNodesToShow(...);
//or hide some menu nodes
viewConfig.setMenuNodesToHide(...);
viewConfig.addMenuNodesToHide(...);
return viewConfig;
}
@Override
protected MenuViewConfig getFooterMenuViewConfig() {
MenuViewConfig viewConfig = MenuViewConfig(MenuViewConfig.MenuStyle.MENU_BAR, RenderStyle.COMMAND_BUTTON, false);
// show some menu nodes
viewConfig.setMenuNodesToShow(...);
viewConfig.addMenuNodesToShow(...);
//or hide some menu nodes
viewConfig.setMenuNodesToHide(...);
viewConfig.addMenuNodesToHide(...);
return viewConfig;
}
Another way to specify whether a command is supported, override the isCommandSupported(...)
method of EntityBackingBean. If a command is not supported, it will not be in header or footer menu.
@Override
protected boolean isCommandSupported(PersistenceDataBackingBean<T> backingBean, String command) throws SystemException {
}
The nested ViewConfig name for header menu is "headerMenu", and the nested ViewConfig name for footer menu
is "footerMenu". And they can also be accessed by:
MenuViewConfig headerMenuViewConfig = entityListViewConfig.getHeaderMenuViewConfig(true);
MenuViewConfig footerMenuViewConfig = entityListViewConfig.getFooterMenuViewConfig(true);
So they can be configured in buildEntityListViewConfig().
public boolean isEntityListEditInPlace(EntityListBackingBean<T> entityListBackingBean)
In-place create by appending editable new rows:
public boolean isEntityListCreateInPlace(EntityListBackingBean<T> entityListBackingBean)
In-place create by inserting editable new rows:
public boolean isEntityListInsertBeforeEnabled(EntityListBackingBean<T> entityListBackingBean)
Change the order of child entities in the list:
public boolean isEntityListMoveUpDownEnabled(EntityListBackingBean<T> entityListBackingBean) throws SystemException
To maintain the order of child entities, the child entity type must
include sequenceNo in persistence by annotation. For example,
@Table("MyType")
@Entity("MyType")
public class MyType extends PersistenceEntityBase implements ChildEntity {
...
@Override
@Column(nullable=false)
public Integer getSequenceNo() {
return sequenceNo;
}
}
If the list of child entities has the JPA OrderBy annotation, the change of entity order will
be ineffective. For example.
@OneToMany(cascade={CascadeType.ALL}, orphanRemoval=true, fetch=FetchType.LAZY,
mappedBy="expenseClaim", targetEntity=ExpenseClaimItem.class)
@OrderBy(ExpenseClaimItem.PROPERTY_DATE + " DESC")
public List<ExpenseClaimItem> getExpenseClaimItems() {
return expenseClaimItems;
}
The viewConfig elements entityCountToAdd and askEntityCountToAdd control the behavior of the AddRows command. For example,
Build View Config:viewConfig.setEntityCountToAdd(1); viewConfig.setAskEntityCountToAdd(false);XML for an embedded entity list:
<viewConfig> <viewConfig name="orderItems"> <entityCountToAdd>1</entityCountToAdd> <askEntityCountToAdd>false</askEntityCountToAdd> </viewConfig> </viewConfig>For non-Parent/Child relationship such as ManyToMany, a dialog will open for user to search and select one or more entities to add.
Override the following method in the EntityListBackingBean to handle barcode scanning:
@Override
protected PageNavigation scanBarcode(String barcode) throws SystemException {
super.scanBarcode(barcode);
...
}
Call the following static method of EntityListCommandHandler to open input dialog:
public static <E extends PersistenceEntity, I extends PersistenceEntity> boolean askValuesBeforeAddingChildEntity(
final EntityBackingBean<E> parentEntityBean, final I childEntity, String childEntityListPropertyName,
List<String> extraProperties, EntityPropertyInitializer<I> initializer,
final ContainerBean containerBean)
The dialog listener must be configured to add the child entity if the askValues dialog is opened.
For example,
if (EntityListCommandHandler.askValuesBeforeAddingChildEntity(...)) { DialogBean dialogBean = containerBean.getDialogBean(); DialogListener dialogListener = new DialogListener() { @Override public PageNavigation dialogClosed(DialogBean dialogBean, Object data) throws SystemException { // add the entity to the entity list return null; } }; dialogBean.setDialogListener(dialogListener) }To enable barcode scanning, set parameter scan.barcode to true. The nested viewConfig name is "addEntity.askValues" for the child entity input bean. For example,
<viewConfig> <viewConfig name="orderItems"> <param name="scan.barcode" value="true"/> <viewConfig name="addEntity.askValues"> <propertiesToShow>employee</propertiesToShow> </viewConfig> </viewConfig> </viewConfig>If the childEntity to add is an EntityAttrValueSupport, and there is any entityAttrValue whose value is null, the dialog will be opened regardless of the nested viewConfig. For example, Product has attribute color. When adding a product to shopping cart, a dialog will open for user to select color.
H1(H1.1, H1.2(H1.2.1, H1.2.2), H1.3), H2(H2.1, H2.2, H2.3)
The leaf header nodes must match the entity properties of EntityListBackingBean.
Custom column headers can be set in ViewConfig:
EntityListViewConfig viewConfig = new EntityListViewConfig(ViewType.ENTITY_LIST_WIDE);
ColumnHeaders columnHeaders = new ColumnHeaders("H1(H1.1, H1.2(H1.2.1, H1.2.2), H1.3), H2(H2.1, H2.2, H2.3)");
viewConfig.setColumnHeaders(columnHeaders);
A column header can have attributes such as style and class. For example,
Purchase[class="ui-title"](Quantity, Unit Price, Total[style="text-align:right"])A special attribute "data-property" is used to associate a leaf header node with an entity property for the header node to be adaptive. That is, if the associated entity property is not visible, the header node will be removed. For example,
Product,UOM,Attributes[data-property="attributes"], Purchase[class="ui-title"](Quantity[class="ui-align-right"], Unit Price[class="ui-align-right"], Total[class="ui-align-right"])
Alternatively, a ColumnHeaders can be created programmatically using ColumnHeaders Node API.
ColumnHeaders columnHeaders = new ColumnHeaders();
columnHeaders.addToTop("Product", "UOM");
Node attributesNode = new Node("Attributes");
attributesNode.setProperty("attributes");
columnHeaders.addToTop(attributesNode);
Node purchaseNode = new Node("Purchase",
new String[]{"Quantity", "Unit Price", "Total"});
purchaseNode.setStyleClasses("ui-title", "ui-align-right");
columnHeaders.addToTop(purchaseNode);
columnHeaders.finish();
To set top-level node priorities for column toggle, set attribute data-priority
or call Node API. For example,
Product,UOM,Attributes[data-property="attributes",data-priority="2"]
or
attributesNode.setPriority(2);
EntityListViewConfig viewConfig = new EntityListViewConfig(ViewType.ENTITY_LIST_WIDE);
viewConfig.setRowExpansionSupported(true);
RowExpansion rowExpansion = new RowExpansion(new String[]{"hiredDate", "type"}, Layout.FLOW);
viewConfig.setRowExpansion(rowExpansion);
EntityListViewConfig viewConfig = new EntityListViewConfig(ViewType.ENTITY_LIST_WIDE);
viewConfig.setLayoutType(LayoutType.ROW_ENTITY_VIEW);
viewConfig.setRowEntityViewLayoutType(RowEntityViewLayoutType.TAB_VIEW);
The nested viewConfig name for row EntityBackingBean is "navigateToEntity", and can
also be accessed by:
EntityViewConfig entityViewConfig = entityListViewConfig.getNavigateToEntityViewConfig(true);
So it can be configured in buildEntityListViewConfig() or XML.
<ul>
<li>Entity A</li>
<li>Entity B</li>
</ul>
The layout of entity properties on each list item can be specified with
listItemLayoutCode of entity list viewConfig. For example,
EntityListViewConfig viewConfig = new EntityListViewConfig(ViewType.ENTITY_LIST_WIDE);
String listItemLayoutCode =
"#{photos}<br/>" +
"#{nid} #{name}<br/>" +
"#{type}, #{hiredDate}<br/>" +
"#{department}";
viewConfig.setListItemLayoutCode(listItemLayoutCode);
With listItemLayoutCode set, ListView will be added to layoutType selection on UI view.
Use context menu (long hold or right click) to open settings dialog for enabling or disabling "Table Reflow" and "Column Toggle". There are "Global Settings" and "Local Settings" for current entity list, and local settings override global settings. If column toggle is enabled, column visibility can be toggled.
Property priorities for column toggle can be set in property descriptors in ViewConfig. For example,
@Override
protected EntityListViewConfig buildEntityListViewConfig(
EntityListBackingBean<Employee> entityListBackingBean,
EntityListViewConfig viewConfig) throws SystemException {
viewConfig = super.buildEntityListViewConfig(entityListBackingBean, viewConfig);
PropertyDescriptor pd = viewConfig.getPropertyDescriptor("type", true, false);
pd.setPriority(2);
return viewConfig;
}
Or configure property priorities in viewConfig XML for embedded objects. For example,
<viewConfig>
...
<property name="type">
<priority>2</priority>
</property>
</viewConfig>
To set priorities for custom headers, see Column Headers.
Valid priority is a number from 1 to 6, and a smaller number has a higher priority. By default, the first three columns are assigned to property 1 and every two subsequent columns will be assigned a different bigger priority number. For example,
Column1(1) Column(1) Column3(1) Column4(2) Column5(2) Column6(3) ...
The numbers in parentheses are priority values. The entity selection column will
be assigned the lowest priority. If the entity list is for entity selection, the row command
column with "Select" command will be assigned the highest priority(1).
When device width becomes larger, the columns with higher priorities(smaller numbers) will be visible before those with lower priorities become visible by CSS media query.
EntityListViewConfig viewConfig = new EntityListViewConfig(ViewType.ENTITY_LIST_WIDE);
viewConfig.setPageable(true);
viewConfig.setPageSize(30);
Show all entities in one page, not pageable:
viewConfig.setPageable(false);
viewConfig.setPageSize(null);
The paginator of an EntityListBackingBean will be enabled only if
its entity list is pageable. Pagination can also be achieved by
calling the following methods:
EntityListBackingBean entityListBackingBean = ...
entityListBackingBean.firstPage();
entityListBackingBean.lastPage();
entityListBackingBean.nextPage();
entityListBackingBean.previousPage();
entityListBackingBean.jumpToPage(5);
Every time pagination occurs, entities are added, or entity are deleted from the entity list,
the EntityBackingBean.paginationChanged(...) will be called.
public void paginationChanged(EntityListBackingBean<T> entityListBackingBean,
Integer pageCount, int currentPageIndex, PaginationChangeReason reason) throws SystemException;
protected <Q> void pageRetrievedFromPersistence(EntityListBackingBean<T> entityListBackingBean,
QueryCriteria<Q, T> criteria, int pageIndex, Integer pageIndexBeforeChange,
PageableEntityList<T> pageableEntityList) throws SystemException;
For example, calculate the total expenses of each employee when showing a list
of employees.
@Override
protected void pageRetrievedFromPersistence(EntityListBackingBean<Employee> entityListBackingBean,
QueryCriteria<Employee, Employee> criteria, int pageIndex, Integer pageIndexBeforeChange,
PageableEntityList<Employee> pageableEntityList) throws SystemException {
List<Employee> employees = pageableEntityList.getPageEntityList();
for (Employee employee : employees) {
BigDecimal totalExpense = ...; // calculate expenses for employee
employee.setTotalExpense(totalExpense);
}
}
See Entity Export/Import for detail.