自2016年6月Richfaces已停止更新,最近将一项目迁移到了Primefaces。Primefaces提供了更丰富的组件,使用更简单,界面更美观,性能更高。JSF应用现在越来越少,仅供有需要者参考。
Richfaces版本: 4.5.14.Final Primefaces版本: 6.2
第一步 删除Richfaces配置,引入Primefaces
如果您使用了maven,请从pom中删除richfaces配置,然后加入primefaces:
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>6.2</version>
</dependency>
然后将web.xml中的richfaces配置删除。
批量更新
- 将
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j"
替换为:
xmlns:p="http://primefaces.org/ui"
注意xmlns:p="http://primefaces.org/ui" 不能重复。
- 将 rich: 和 a4j: 替换为 p:。
- 将属性render替换为update,execute替换为process。
- 删除属性limitRender、bypassUpdates。
同名组件
Richfaces | Primefaces |
---|---|
a4j:ajax | <p:ajax> |
a4j:commandButton | <p:commandButton> |
a4j:commandLink | <p:commandLink> |
a4j:log | <p:log> |
a4j:outputPanel | <p:outputPanel> |
a4j:poll | <p:poll> |
a4j:repeat | <p:repeat> |
rich:calendar | <p:calendar> |
rich:contextMenu | <p:contextMenu> |
rich:dataGrid | <p:dataGrid> |
rich:dataTable rich:dataScroller | <p:dataTable> |
rich:extendedDataTable rich:collapsibleSubTable | <p:dataTable> |
rich:editor | <p:editor> |
rich:fileUpload | <p:fileUpload> |
rich:message | <p:message> |
rich:messages | <p:messages> |
rich:panel | <p:panel> |
rich:panelMenu | <p:panelMenu> |
rich:pickList | <p:pickList> |
rich:progressBar | <p:progressBar> |
rich:toolbar | <p:toolbar> |
rich:tooltip | <p:tooltip> |
rich:tree | <p:tree> |
异名组件
Richfaces | Primefaces |
---|---|
a4j:jsFunction | <p:remoteCommand> |
a4j:mediaOutput | <p:media> |
a4j:region | <p:fragment> |
a4j:status | <p:ajaxStatus> |
rich:validator | <p:clientValidator> |
rich:accordion rich:collapsiblePanel | <p:accordionPanel> |
rich:autocomplete | <p:autoComplete> |
rich:chart | Primefaces提供了十多种chart组件,自行选择 |
rich:dragSource | <p:draggable> |
rich:dropTarget | <p:droppable> |
rich:dropDownMenu | <p:menubar> |
rich:list | <p:dataList> |
rich:inplaceInput rich:inplaceSelect | <p:inplace> |
rich:inputNumberSlider | <p:slider> |
rich:inputNumberSpinner | <p:spinner> |
rich:notify | <p:notificationBar> |
rich:notifyMessage rich:notifyMessages | <p:growl> |
rich:orderingList | <p:orderList> |
rich:popupPanel | <p:dialog> |
rich:select | <p:selectOneMenu> |
rich:tabPanel rich:togglePanel | <p:tabView> |
不存在的组件
a4j:param a4j:actionListener a4j:push a4j:queue
说明:rich:dataScroller与<p:dataScroller>,虽然名称相同,但功能不同,一个是分页,一个是按需显示数据。
子组件、属性、事件
名称或功能相同的组件,子组件名称,支持的属性、事件可能不同,使用时需要仔细核对,如:
- rich:panelMenu -> <p:panelMenu> <p:panelMenu> 仅支持几个常用属性 rich:panelMenuGroup -> <p:submenu> rich:panelMenuItem -> <p:menuitem> label -> value
- rich:collapsiblePanel -> <p:accordionPanel> 要删除原定义的属性,增加子组件<p:tab>
Function
- #{rich:component('id')} -> PF('widgetVar')
<a4j:commandButton value="Cancel" oncomplete="#{rich:component('popupPanel')}.hide();"/>
<rich:popupPanel id="popupPanel">...</rich:popupPanel>
<a4j:commandButton value="Cancel" oncomplete="PF('dialog').hide()"/>
<p:dialog id="dialog" widgetVar="dialog">...</p:dialog>
- #{rich:clientId('id')} -> #{p:component('id')}
- jQuery 在Primefaces中可以直接使用jQuery,如:
<p:commandButton onclick="alert($('#form\\:panel\\:airport').val());return false;" value="test"/>
上面使用了转义字符,也可以使用PrimeFaces函数,注意不要加"#",如下:
<p:commandButton onclick="alert($(PrimeFaces.escapeClientId('form:panel:airport')).val());return false;" value="test"/>
Primefaces特有组件
Primefaces提供了很多新颖美观的组件,也可以替代Richfaces组件,大家可查看Primefaces showcase。 有一些比较常用的功能Richfaces没有提供,原来只能自己实现,现在不需要了,比如:
- <p:outputLabel> 自动为required增加*,为校验信息增加label
<p:outputLabel for="extended" value="Extended Label:" />
<p:inputText id="extended" required="true" />
- <p:panelGrid> 支持复杂表格,使用Richfaces时只能借助rich:dataTable较繁琐。
- <p:confirmDialog> confirmDialog示例:
<p:commandButton value="Destroy the World" actionListener="#{dialogView.destroyWorld}" update="message">
<p:confirm header="Confirmation" message="Are you sure?" icon="ui-icon-alert" />
</p:commandButton>
<p:confirmDialog global="true" showEffect="fade" hideEffect="fade">
<p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" icon="ui-icon-check" />
<p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no" icon="ui-icon-close" />
</p:confirmDialog>
验证
<h:inputText value="" required="true" id="test">
<p:ajax event="blur" update="msgTest"/>
</h:inputText>
<p:message for="test" id="msgTest" display="icon"/>
rich:message不支持tooltip形式显示message,<p:message>支持。
dataTable与LazyDataModel
<p:dataTable>实现了rich:dataTable、rich:extendedDataTable、rich:dataScroller的所有功能,定义更简单,且复杂表头时仍支持排序,支持单选、复选框选择,提供emptyMessage属性,不必再使用noData facet。 LazyDataModel比ExtendedDataModel实现更简单,性能更高。 下面LazyDataModel和自定义dataTable示例(org.jason是自定义的一些实现): PagingDataModel
import org.jason.base.dao.Filter;
import org.jason.base.dao.IQuery;
import org.jason.base.dao.Order;
import org.jason.base.dao.Pagination;
import org.jason.base.service.PagingService;
import org.jason.entity.BaseEntity;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.primefaces.model.SortOrder.ASCENDING;
//
public class PagingDataModel<T extends BaseEntity> extends LazyDataModel<T> {
private PagingService service;
private IQuery query;
private List<T> data;
private List<T> selectedItems = new ArrayList<>();
private T selectedItem;
@SuppressWarnings("rawtypes")
public PagingDataModel(PagingService service, IQuery query) {
this.service = service;
this.query = query;
}
@Override
public T getRowData(String rowKey) {
for (T entity : data) {
if (entity.getId() == Integer.parseInt(rowKey)) {
return entity;
}
}
return null;
}
@Override
public Object getRowKey(T object) {
return object.getId();
}
@Override
public List<T> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
data = service.find(query, new Pagination(first, pageSize, createFilter(filters), createOrders(sortField, sortOrder)));
setRowCount(service.count(query, createFilter(filters)));
return data;
}
public void refresh() {
setSelectedItem(null);
setSelectedItems(null);
load(0, getPageSize(), null, null, null);
}
private List<Filter> createFilter(Map<String, Object> filters) {
if (filters == null) {
return Collections.emptyList();
}
return filters.entrySet().stream().map(filter -> new Filter(filter.getKey(), filter.getValue())).collect(Collectors.toList());
}
private List<Order> createOrders(String sortField, SortOrder sortOrder) {
if (sortField == null || sortOrder == null) {
return Collections.emptyList();
}
List<Order> orders = new ArrayList<>();
orders.add(new Order(sortField, sortOrder == ASCENDING));
return orders;
}
public List<T> getSelectedItems() {
return selectedItems;
}
public void setSelectedItems(List<T> selectedItems) {
this.selectedItems = selectedItems;
}
public T getSelectedItem() {
return selectedItem;
}
public void setSelectedItem(T selectedItem) {
this.selectedItem = selectedItem;
}
}
自定义dataTable
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<composite:interface>
<composite:attribute name="emptyMessage" default="#{messages['general.noData']}"/>
<composite:attribute name="paginator" default="true"/>
<composite:attribute name="paginatorPosition" default="bottom"/>
<composite:attribute name="rows" default="10"/>
<composite:attribute name="rowsPerPageTemplate" default="5,10,15"/>
<composite:attribute name="rowStyleClass" default="even,odd"/>
<composite:attribute name="selection"/>
<composite:attribute name="selectionMode"/>
<composite:attribute name="style"/>
<composite:attribute name="styleClass" default="list-table"/>
<composite:attribute name="value" type="org.iata.ios.web.pagination.PagingDataModel" required="true"/>
<composite:attribute name="onRowClick"/>
<composite:facet name="header"/>
<composite:facet name="footer"/>
</composite:interface>
<composite:implementation>
<p:outputPanel layout="block" styleClass="list-panel">
<c:choose>
<c:when test="#{not empty cc.attrs.selectionMode}">
<p:dataTable id="dt" value="#{cc.attrs.value}" var="item" lazy="true" rows="#{cc.attrs.paginator ? cc.attrs.rows : 0}" paginator="#{cc.attrs.paginator}"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="#{cc.attrs.rowsPerPageTemplate}" paginatorPosition="#{cc.attrs.paginatorPosition}"
selection="#{cc.attrs.selection}" emptyMessage="#{cc.attrs.emptyMessage}"
style="#{cc.attrs.style}" styleClass="#{cc.attrs.styleClass}" rowStyleClass="#{cc.attrs.rowClasses}" onRowClick="#{cc.attrs.onRowClick}">
<composite:insertFacet name="header"/>
<p:column rendered="#{cc.attrs.selectionMode == 'single'}" selectionMode="single" style="width:16px;text-align:center"/>
<p:column rendered="#{cc.attrs.selectionMode == 'multiple'}" selectionMode="multiple" style="width:16px;text-align:center"/>
<composite:insertChildren/>
<composite:insertFacet name="footer"/>
</p:dataTable>
</c:when>
<c:otherwise>
<p:dataTable id="dt" value="#{cc.attrs.value}" var="item" lazy="true" rows="#{cc.attrs.paginator ? cc.attrs.rows : 0}" paginator="#{cc.attrs.paginator}"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="#{cc.attrs.rowsPerPageTemplate}" paginatorPosition="#{cc.attrs.paginatorPosition}" emptyMessage="#{cc.attrs.emptyMessage}"
style="#{cc.attrs.style}" styleClass="#{cc.attrs.styleClass}" rowStyleClass="#{cc.attrs.rowClasses}" onRowClick="#{cc.attrs.onRowClick}">
<composite:insertFacet name="header"/>
<composite:insertChildren/>
<composite:insertFacet name="footer"/>
</p:dataTable>
</c:otherwise>
</c:choose>
</p:outputPanel>
<ui:include src="css.xhtml"/>
</composite:implementation>
</html>
参考文档
PrimeFaces Showcase PrimeFaces Documentation Migrating From RichFaces 4 to PrimeFaces 3 Patching RichFaces 3.3.3 AJAX.js for IE11
附录 Seam升级到CDI
替换Annotation
- @Name -> @Named import org.jboss.seam.annotations.Name; -> import javax.inject.Named;
- @In -> @Inject import org.jboss.seam.annotations.In; -> import javax.inject.Inject;
- 删除@Out import org.jboss.seam.annotations.Out; 改为@Inject相应Scope的组件
- @Scope(ScopeType.APPLICATION) -> @ApplicationScoped @Scope(ScopeType.SESSION) -> @SessionScoped @Scope(ScopeType.CONVERSATION) -> @ConversationScoped @Scope(ScopeType.EVENT) -> @RequestScoped(或删除)
- @Create -> @PostConstruct import org.jboss.seam.annotations.Create; -> import javax.annotation.PostConstruct; @Destroy -> @PreDestroy import org.jboss.seam.annotations.Destroy; -> import javax.annotation.PreDestroy;
- @Observer("org.jboss.seam.postInitialization") 可以使用DeltaSpike Servlet module,替换如下:
public void init(@Observes @Initialized ServletContext context) {
...
}
引入DeltaSpike Servlet module:
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-servlet-module-api</artifactId>
<version>1.8.1</version>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-servlet-module-impl</artifactId>
<version>1.8.1</version>
</dependency>
在web.xml中增加:
<listener>
<display-name>EventBridgeContextListener</display-name>
<listener-class>org.apache.deltaspike.servlet.impl.event.EventBridgeContextListener</listener-class>
</listener>
- @Startup可使用ejb Singleton替换:
@Singleton
@Startup
- @RequestParameter和页面参数 可以替换为这样的组合:
<p:button value="Edit" outcome="edit">
<f:param name="id" value="#{...}"/>
</p:button>
<f:metadata>
<f:viewParam name="id" value="#{...}"/>
</f:metadata>
- @Logger 建议替换为slf4j
@Logger
private Log log;
替换为: private Logger log = LoggerFactory.getLogger(Test.class); 日志中的{0} {1} 均替换为{}。
- FacesMessages和StatusMessages改为使用javax.faces.application.FacesMessage
更新配置
- 删除components.xml
- seam.properties替换为beans.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
- 删除page.xml navigation调整到JSF 2 faces-config.xml中(navigation-rule)。 action可以调整到页面中:
<f:metadata>
<f:event type="preRenderView" listener="#{...}"/>
</f:metadata>
页面参数上面已提到过。
页面
xmlns:s="http://jboss.com/products/seam/taglib", 可以使用JSF 2或Primefaces标签替换
Seam | JSF 2/Primefaces |
---|---|
<s:div> | <p:outputPanel> |
<s:span> | <p:outputPanel layout="inline"> |
<s:enumItem> | <f:selectItem> label -> itemLabel enumValue -> itemValue |
<s:convertDateTime> | <f:convertDateTime> |
<s:fragment> | ui:fragment |
<s:conversationId> | <f:param name="cid" value="#{javax.enterprise.context.conversation.id}"/> |
参考文档
seam migration Migration from Seam 2 to Java EE and Alternatives