我正在尝试创建一个作为输入组件的复合组件,类似于本教程中列出的组件:Composite Input Components in JSF

该组件使用来自primefaces的p:autocomplete和ap:selectonemenu,使用可选的p:inputText来获取一些额外的文本,然后它将返回一个对象,该对象将Web信息的其他部分整齐地包装起来 . 使用 . 不幸的是,我似乎永远无法获得 Value . 这是代码:

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