我正在尝试创建一个作为输入组件的复合组件,类似于本教程中介绍的组件:JSF 中的复合输入组件
该组件使用来自 primefaces 的 p:autocomplete 和 ap:selectonemenu,以及可选的 p:inputText 来接收一些额外的文本,然后它将返回到支持 bean 的对象,该对象将这些信息整齐地包装到 Web 应用程序的其他部分使用。不幸的是,我似乎永远无法恢复这些值。这是代码:
组件 xhtml:
<cc:interface componentType="personselector">
<!-- Subject ID for contact filtering -->
<cc:attribute name="subjectId" type="java.lang.String" required="false"/>
<!-- Whether or not to show the individual contacted free form field -->
<cc:attribute name="showIndividualContacted" type="java.lang.Boolean" required="false" default="false"/>
<!-- Whether or not to show the phone selector -->
<cc:attribute name="showPhoneSelector" type="java.lang.Boolean" required="false" default="true"/>
<!-- The style class to apply to the parent div -->
<cc:attribute name="styleClass" type="java.lang.String" required="false"/>
<!-- An optional listener for a newly selected person -->
<cc:attribute name="personSelectedListener" method-signature="void actionListener(javax.faces.event.ActionEvent)" required="false"/>
<!-- The label to display next to the person selector -->
<cc:attribute name="personLabel" type="java.lang.String" required="false" default="Person"/>
<!-- The label to display next to the individual contacted field -->
<cc:attribute name="individualLabel" type="java.lang.String" required="false" default="Individual Contacted"/>
<!-- The label to display next to the phone number selector -->
<cc:attribute name="phoneLabel" type="java.lang.String" required="false" default="Phone Number"/>
<!-- Whether or not to limit the phone number results to a certain type of number, valid options are "phone" or "fax" -->
<cc:attribute name="limitPhones" type="java.lang.String" required="false"/>
</cc:interface>
<cc:implementation>
<div id="#{cc.clientId}" class="personselector #{cc.attrs.styleClass}">
<h:panelGrid columns="2" styleClass="person-selector-grid">
<h:outputLabel styleClass="personselector-ui-label" value="#{cc.attrs.personLabel}" />
<p:autoComplete id="personSelector" value="#{cc.personSelected}" completeMethod="#{cc.searchPeople}"
var="item" itemLabel="#{item.name}" itemValue="#{item}" size="51" converter="personSearchConverter"
onSelectUpdate="@this" selectListener="#{cc.handlePersonSelect}"
styleClass="personselector-ui-autocomplete personselector-mainperson">
<p:ajax event="itemSelect" listener="#{cc.handlePersonSelect}" process=":#{cc.clientId}" update="phoneNumberSelector" />
<p:ajax event="itemSelect" listener="#{cc.attrs.personSelectedListener}" disabled="#{empty cc.attrs.personSelectedListener}"/>
... auto complete formatting ...
</p:autoComplete>
<h:outputLabel value="#{cc.attrs.individualLabel}" styleClass="personselector-ui-label" rendered="#{cc.attrs.showIndividualContacted}"/>
<p:inputText id="individualField" value="#{cc.individualName}" styleClass="personselector-individual-contacted personselector-ui-textfield"
rendered="#{cc.attrs.showIndividualContacted}"/>
<h:outputLabel value="#{cc.attrs.phoneLabel}" styleClass="personselector-ui-label"/>
<p:selectOneMenu id="phoneNumberSelector" value="#{cc.phoneNumber}" editable="true" var="n" style="width:150px"
styleClass="personselector-ui-selectone personselector-phone-selector">
<f:selectItems value="#{cc.findPersonContactNumbers}" var="number" itemValue="#{number.number}"/>
<p:column>
#{n.descr}
</p:column>
<p:column>
#{n.number}
</p:column>
</p:selectOneMenu>
</h:panelGrid>
</div>
</cc:implementation>
组件的支持 bean:
@FacesComponent("personselector")
public class PersonSelector extends UIInput implements NamingContainer{
public static final String PHONE_LIMIT ="phone";
public static final String FAX_LIMIT = "fax";
//Required for since we're extending UIInput and not UINamingContainer
@Override
public String getFamily() { return "javax.faces.NamingContainer"; }
//Returns this component as a submitted value rather than the individual sub component key values
// See http://weblogs.java.net/blog/cayhorstmann/archive/2010/01/30/composite-input-components-jsf
@Override
public Object getSubmittedValue() { return this; }
//Convert from the component to the SelectedPerson object we want to return
@Override
protected Object getConvertedValue(FacesContext context, Object newSubmittedValue) {
AutoComplete personSelector = (AutoComplete) findComponent("personSelector");
UIInput individualField = (UIInput) findComponent("individualField");
SelectOneMenu phoneSelector = (SelectOneMenu) findComponent("phoneNumberSelector");
PersonSearchData person = (PersonSearchData) personSelector.getValue();
String individual = (String) individualField.getValue();
PhoneNumber phone = (PhoneNumber) phoneSelector.getValue();
if(person != null || !StaticTools.isNullOrEmpty(individual) || phone!= null){
SelectedPerson sp = new SelectedPerson();
sp.setAlternateName(individual);
sp.setPersonSelected(person);
sp.setPhoneNumberSelected(phone);
return sp;
} else {
return null;
}
}
@Override
public void encodeBegin(FacesContext context) throws IOException{
SelectedPerson ps = (SelectedPerson) getValue();
if(ps != null){
AutoComplete personSelector = (AutoComplete) findComponent("personSelector");
UIInput individualField = (UIInput) findComponent("individualField");
SelectOneMenu phoneSelector = (SelectOneMenu) findComponent("phoneNumberSelector");
personSelector.setValue(ps.getPersonSelected());
personSelected = ps.getPersonSelected();
individualField.setValue(ps.getAlternateName());
individualName = ps.getAlternateName();
phoneSelector.setValue(ps.getPhoneNumberSelected());
phoneNumber = ps.getPhoneNumberSelected();
}
super.encodeBegin(context);
}
/** The state of the component. */
private Object[] state;
/** The person selected. */
private PersonSearchData personSelected;
/** The individual name. */
private String individualName;
/** The phone number selected. */
private PhoneNumber phoneNumber;
/** The subject id for filtering results. */
private String mySubjectId;
/** The limitation placed on the phone results. */
private String myPhoneLimit;
/* (non-Javadoc)
* @see javax.faces.component.UIComponentBase#saveState(javax.faces.context.FacesContext)
*/
@Override
public Object saveState(final FacesContext context){
if (state == null) {
state = new Object[4];
}
state[0] = super.saveState(context);
state[1] = personSelected;
state[2] = individualName;
state[3] = phoneNumber;
return state;
}
/* (non-Javadoc)
* @see javax.faces.component.UIComponentBase#restoreState(javax.faces.context.FacesContext, java.lang.Object)
*/
@Override
public void restoreState(final FacesContext context, final Object state) {
this.state = (Object[]) state;
super.restoreState(context, this.state[0]);
personSelected = (PersonSearchData) this.state[1];
individualName = (String) this.state[2];
phoneNumber = (PhoneNumber) this.state[3];
}
public String getMySubjectId(){
if(mySubjectId == null){
mySubjectId = (String) getAttributes().get("subjectId");
}
return mySubjectId;
}
/**
* Find respondent contacts.
*
* @param query
* the query
* @return the list
*/
public List<PersonSearchData> searchPeople(String query) {
... find people from the database...
}
/**
* Find matching contacts.
*
* @param queryLength the query length
* @param firstName the first name
* @param lastName the last name
* @return the list
*/
public List<PersonSearchData> findMatchingPeople(int queryLength,
String firstName, String lastName) {
... filter people ...
}
/**
* Handle respondent select.
*
* @param event
* the event
*/
public void handlePersonSelect(SelectEvent event) {
PersonSearchData data = (PersonSearchData) event.getObject();
setPersonSelected(data);
}
/**
* Gets the find respondent contact numbers.
*
* @return the find respondent contact numbers
*/
public List<SelectItem> getFindPersonContactNumbers() {
PersonSearchData respondentContact = getPersonSelected();
return findContactNumbers(respondentContact, getMyPhoneLimit(), getPhoneNumber());
}
/**
* Find contact numbers.
*
* @param psd the psd
* @param phoneLimit the phone limit
* @param current the current
* @return the list
*/
public List<SelectItem> findContactNumbers(PersonSearchData psd,
String phoneLimit,
PhoneNumber current) {
... find phone/fax numbers from the database ...
}
... getters and setters ...
}
用法:
<h:form>
<ppa:personselector id="mySelector" value="#{homePageBacking.sp}"/>
<h:commandButton action="#{homePageBacking.displayData}" value="Submit Me"/>
</h:form>
不幸的是,我永远不会取回提交的值。如果我使用我在这里实现的状态保存,我会从自动完成中取回 PersonSelected,但我永远不会取回选定的电话号码或个人姓名(它们返回为空)。如果我不使用状态保存,那么一切都会返回 null。