Cmobilecom AF 5.19 Developer Guide

7.2 Entity List Backing Bean

An EntityListBackingBean, simply called "EntityList Bean", is the backing bean for a pageable list of entities for managing entities interactively. It uses JPA for persistence, and property annotations for entity list view.

Entity List Bean Lifecycle

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 Model
User 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.

Entity List Data Source

Underneath an EntityListBackingBean is a pageable entity list(PageableEntityList) that can be MemoryEntityList or QueryCriteriaEntityList. All the entities of a MemoryEntityList are stored in memory, but only the entities in current page of a QueryCriteriaEntityList is retrieved from persistence.

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);

View Config

EntityListViewConfig configures the UI view(content, layout and commands) of a PageableEntityList. To override default viewConfig, override the following method in entityType's EntityBackingBean:

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;
	}
}

Build Property Model

Entity properties of the UI view are annotated in entityType's EntityBackingBean. The same property annotations are used for the views of entity, query and entity list. Property model(a list of EntityProperties) will be built during the lifecycle. To modify the built list of EntityProperties, override the following method in entityType's EntityBackingBean:

	@Override
	protected void postRefreshEntityListPropertyModel(EntityListBackingBean<T> entityListBackingBean,
		List<EntityProperty<T>> entityProperties) throws SystemException {

	}

Entity Row Command Menu

For an entity list, menu nodes can be added for each row, and they are called entity row commands. By default, View/Edit/Delete will be added to each row if current user has the corresponding permissions. To add more menu nodes to entity rows, override the following methods in its EntityBackingBean (not EntityListBackingBean). For example,

	@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(). Menu nodes can be added to header or footer menu by overriding the following method. For example,

	@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().

In-Place Create/Edit

For an entity list, entities can be added or edited in place. Its EntityBackingBean must override the following methods to enable the following in-place actions:

In-place edit:


	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;
	}

Add Rows

The command "AddRows" will add a number of rows(uninitialized child entities) to the entity list, and users can fill out the property values of the added entities in place.

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.

Scan Barcode

Similar to "AddRows" command, "ScanBarcode" command can add one new entity to the entity list by scanning barcode, but the entity added can be merged to an existing entity if any. A dialog will be opened for user to fill out property values if necessary before adding the new entity.

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.

Column Headers

The default layout type for an entity list is TABLE if not specified. By default, each top-level EntityProperty of an EntityListBackingBean is one column in the entity list table. But the column headers can be hierarchical, and a column header can have sub headers. Each header is called Node in the hierarchical tree. For example,

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);

Row Expansion

If there are too many properties to show in entity list table, some properties can be moved to expansion row:

	EntityListViewConfig viewConfig = new EntityListViewConfig(ViewType.ENTITY_LIST_WIDE);
	viewConfig.setRowExpansionSupported(true);
	RowExpansion rowExpansion = new RowExpansion(new String[]{"hiredDate", "type"}, Layout.FLOW);
	viewConfig.setRowExpansion(rowExpansion);

Row Entity View

In rowEntityView, an EntityBackingBean will be created for each entity in the entity list of current page. For example, to enable rowEntityView using ViewConfig, and layout the entities of current page in tab view:

	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.

List View

In list view, HTML UL and LI tags are used to display a list of entities with each list item for one entity.

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

Table Reflow and Column Toggle

Entity list responsive table layout can be table reflow or column toggle. If table reflow is enabled, when device width is less than a certain width(for example, 35em), the table rows will be shown in stacked style. If column toggle is enabled, a column will be shown or hidden depending on device width and its priority. Table reflow and column toggle can be enabled at the same time.

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.

Pagination

Entity list pagination can be controlled by its ViewConfig:

	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;

Page Listener

After a page is loaded from persistence, the EntityListBackingBean will be notified by the following method of its EntityBackingBean.

	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);
		}
  	}

Export To XML

All the entities of all pages can be exported as XML for download if enabled. The exported entities XML can be imported back to the same or a different DataAccessUnit if entity references can be resolved.

See Entity Export/Import for detail.

Export To Excel

The entity list of all the pages can be exported as Excel if user has the permission. Its access control can be described in XML ac.xml statically or in viewConfig dynamically. Note that: the Excel file can not be imported back to system.
Entity Backing BeanProperty AnnotationsFrames / No Frames