Ced
Ced

Reputation: 17417

selectonemenu, when editable=false, cannot submit the form

My form is working if editable is set to true but not when it's false. When false the submit action method is not even called. I need it to be false (not editable so I can store the correct value in db).

This is the menu I added.

<tr><td>
<h:outputLabel for="country">#{registering.Country}: </h:outputLabel></td>
    <td><p:selectOneMenu editable="false" id="country" required="true" requiredMessage="Required" value="#{subscribeUser.user.countryBean}" converter="#{countriesConverter}" effect="fold" >
        <f:selectItem itemValue="#{null}" itemLabel="#{registering.SelectCountry}" />
        <f:selectItems value="#{countriesConverter.countries}" var="country" itemLabel="#{country.shortName}" itemValue="#{country}" />
    </p:selectOneMenu></td><td></td><td>
<span class="error"><h:message id="countryMessage" for="country"/></span></td>
</tr> 

Notice that the select one menu has editable="false" it doesn't work in that case. But if I put editable="true" it works.

here is the full form, last 2 fields are problematic:

<h:form>

   <table class="registration">             

        <!-- username -->
        <tr><td>
        <h:outputLabel for="username">#{registering.Username}: </h:outputLabel></td><td>
        <p:inputText id="username" value="#{subscribeUser.user.username}" 
            validator="#{usernameValidator.validate}" maxlength="#{values.small}">
            <f:passThroughAttribute name="required" value="true"/>
            <f:ajax event="blur" render="usernameCheck usernameMessage submit"></f:ajax>
        </p:inputText></td><td>
        <h:panelGroup id="usernameCheck">
            <h:graphicImage library="images/icons" name="failed_indicator.png" rendered="#{usernameValidator.isIndicatorVisible.usernameFailed}"></h:graphicImage>
            <h:graphicImage library="images/icons" name="success_indicator.png" rendered="#{usernameValidator.isIndicatorVisible.usernameSuccess}"></h:graphicImage>
        </h:panelGroup></td><td>
        <span class="error"><h:message id="usernameMessage" for="username"/></span></td>
        </tr>

        <!-- password -->           

        <tr><td>
        <h:outputLabel for="password">#{registering.Password}: </h:outputLabel></td><td>
        <p:password id="password" value="#{subscribeUser.userCredential.password}" feedback="true"

            promptLabel="#{registering.PleaseEnterPass}" weakLabel="#{registering.Weak}"
            goodLabel="#{registering.Good}" strongLabel="#{registering.Strong}"
            requiredMessage="#{registering.reqPassword}"
            validator="#{passwordValidator.validate}">
            <f:passThroughAttribute name="required" value="true"/>
            <f:attribute name="confirm" value="#{confirmPassword}" />
            <f:passThroughAttribute name="required" value="true"/>
            <f:ajax event="blur" execute="password confirmPassword" render="passwordMessage passwordCheck confpasswordCheck submit"></f:ajax>                       
        </p:password></td><td>
        <h:panelGroup id="passwordCheck">
            <h:graphicImage library="images/icons" name="failed_indicator.png" rendered="#{passwordValidator.isIndicatorVisible.passwordFailed}"></h:graphicImage>
            <h:graphicImage library="images/icons" name="success_indicator.png" rendered="#{passwordValidator.isIndicatorVisible.passwordSuccess}"></h:graphicImage>
        </h:panelGroup>
        </td><td>
        <span class="error"><h:message id="passwordMessage" for="password"/></span></td>
        </tr>           

        <!-- Confirm password -->

        <tr><td>
        <h:outputLabel for="confirmPassword" value="#{registering.ConfirmPass}: "/></td><td>
        <p:password id="confirmPassword" required="true"
            requiredMessage="#{registering.PleaseConfirmPassword}" 
            binding="#{confirmPassword}">
            <f:passThroughAttribute name="required" value="true"/>
            <f:ajax event="blur" execute="password confirmPassword" render="passwordMessage passwordCheck confpasswordCheck submit"></f:ajax>

            </p:password> </td><td>
            <h:panelGroup id="confpasswordCheck">
                <h:graphicImage library="images/icons" name="failed_indicator.png" rendered="#{passwordValidator.isIndicatorVisible.passwordFailed}"></h:graphicImage>
                <h:graphicImage library="images/icons" name="success_indicator.png" rendered="#{passwordValidator.isIndicatorVisible.passwordSuccess}"></h:graphicImage>
            </h:panelGroup>

            </td><td>
        <span class="error"><h:message id="passwordConfMessage" for="confirmPassword" /></span></td>
        </tr>

        <!-- Email -->

        <tr><td>
        <h:outputLabel for="email">#{registering.Email}: </h:outputLabel></td><td>
        <p:inputText id="email" required="true" value="#{subscribeUser.user.email}" 
            validator="#{emailValidator.validate}"
            requiredMessage="#{registering.reqEmail}">
            <f:passThroughAttribute name="required" value="true"/>
            <f:passThroughAttribute name="type" value="email"/>
            <f:passThroughAttribute name="maxlength" value="100"/>
            <f:ajax event="blur" render="emailCheck emailMessage submit"></f:ajax>

        </p:inputText></td><td>
         <h:panelGroup id="emailCheck">
            <h:graphicImage library="images/icons" name="failed_indicator.png" rendered="#{emailValidator.isIndicatorVisible.emailFailed}"></h:graphicImage>
            <h:graphicImage library="images/icons" name="success_indicator.png" rendered="#{emailValidator.isIndicatorVisible.emailSuccess}"></h:graphicImage>
        </h:panelGroup> </td><td>
        <span class="error"><h:message id="emailMessage" for="email"/></span></td>
        </tr>

        <!-- Country -->

            <tr><td>
        <h:outputLabel for="country">#{registering.Country}: </h:outputLabel></td>
        <td colspan="3"><p:selectOneMenu required="true" editable="true" id="country" value="#{subscribeUser.user.countryBean}" converter="#{countriesConverter}" effect="fold">
                <f:selectItems value="#{countriesConverter.countries}" var="country" itemLabel="#{country.shortName}" itemValue="#{country}" />
            </p:selectOneMenu></td>
        </tr>   
        <!-- Timezone -->   

         <tr><td>
        <h:outputLabel for="timezone">#{registering.Timezone}: </h:outputLabel></td>
        <td colspan="3">
            <p:selectOneMenu id="timezone" editable="true" required="true" value="#{subscribeUser.user.timezoneOffset}" converter="#{timezonesConverter}" effect="fold" >
                <f:selectItems value="#{timezonesConverter.timezonesList}" var="timezone" itemLabel="#{timezone.name}" itemValue="#{timezone}"  />

            </p:selectOneMenu></td>
        </tr>  



    </table>
    <div class="areYouARobot" ><div class="captchaLabel">#{registering.AreYouARobot}</div>
    <!-- <div class="g-recaptcha" data-sitekey="6LdFTgcTAAAAAHQeLLEKPE-of2si0GrwuZXoEAHb"></div> --></div>
    <div class="submit">
    <p:commandButton value="#{registering.Submit}" id="submit" action="#{subscribeUser.inscrireUser}"  icon="ui-icon-disk" 
            disabled="#{!(usernameValidator.ok  and passwordValidator.ok and emailValidator.ok)}" >

    </p:commandButton></div>

</h:form>

Here is the country converter:

@ManagedBean
public class CountriesConverter implements Converter {
    private List<Country> countries;
    private List<String> countriesStr;
    private List<Integer> countriesId;
    private Country country;

    @EJB
    private CountriesService cs;

    @PostConstruct
    public void init() {
        this.countries = cs.getAllCountries();
        this.countriesStr = new ArrayList<String>();
        this.countriesId = new ArrayList<Integer>();
        for (Country c : countries) {
            this.countriesStr.add(c.getShortName());
        }
        for (Country c : countries) {
            this.countriesId.add(c.getIdCountry());
        }
    }

    @Override
    public Country getAsObject(FacesContext ctx, UIComponent component,
            String submittedValue) {
        if (submittedValue == null || submittedValue.isEmpty()) {
            return null;
        }

        try {
            return cs.findByName(submittedValue);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public String getAsString(FacesContext fc, UIComponent uic,
            Object modelValue) {

        Country datCountry = (Country) modelValue;
        if (datCountry == null) {
            return "";
        }

        else if (datCountry instanceof Country) {
            return datCountry.getShortName();
        } else {
            return null;
        }
    }
    //get&sets
}

The country entity:

@Entity
@Table(name="countries")
@NamedQuery(name="Country.findAll", query="SELECT c FROM Country c")
public class Country implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    private int idCountry;

    @Column(name="calling_code")
    private String callingCode;

    private String cctld;

    private String iso2;

    private String iso3;

    @Column(name="long_name")
    private String longName;

    private String numcode;

    @Column(name="short_name")
    private String shortName;

    @Column(name="un_member")
    private String unMember;

    //bi-directional many-to-one association to User
    @OneToMany(mappedBy="countryBean")
    private List<User> users;

    public Country() {
    }
    //getter & setters
}

My timezone converter:

@ManagedBean
public class TimezonesConverter implements Converter {

    private List<Timezone> timezonesList;
    private Map<String, Timezone> myMap;
    private static final String[] timezonesStr = {
            "(GMT -12:00) Eniwetok, Kwajalein",
            "etcetera" };

    public TimezonesConverter() {
        timezonesList = new ArrayList<Timezone>();
        myMap = new TreeMap<String, Timezone>();
        int offset = -720;
        for (String s : timezonesStr) {
            Timezone timezone = new Timezone(s, offset);
            timezonesList.add(timezone);
            myMap.put(s, timezone);
            offset += 60;
        }
    }

    @Override
    public Object getAsObject(FacesContext arg0, UIComponent arg1,
            String submittedValue) {
        if (submittedValue == null || submittedValue.isEmpty()) {
            return null;
        }
        try {
            return myMap.get(submittedValue);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public String getAsString(FacesContext arg0, UIComponent arg1,
            Object modelValue) {
        // casting error here.
        Timezone timezone = (Timezone) modelValue;
        if (timezone == null) {
            return "";
        } else if (timezone instanceof Timezone) {
            return timezone.getName();
        } else {
            return null;
        }
    }
//getters
}

Timezone class:

public class Timezone {
    private String name;
    private int offset;

    public Timezone(String name, int offset) {
        this.name = name;
        this.value = offset;
    }
//getters and setters
}

I commented the timezone thing but when uncommented it gives me this error, which makes no sens to me since the var I put in the timezone menu is a timezone object:

java.lang.ClassCastException: java.lang.Integer cannot be cast to main.java.utils.Timezone
    at main.java.converters.TimezonesConverter.getAsString(TimezonesConverter.java:82)

I did a workaround for this but I'm unhappy with it:

@Override
public String getAsString(FacesContext arg0, UIComponent arg1,
        Object modelValue) {
    int offset = (int) modelValue;
    if (modelValue == null) {
        return "";
    } else {
        for (Timezone tz : timezonesList) {
            if (tz.getOffset() == offset) {
                return tz.getName();
            }
        }
    }
    return null;
}

and I switched the value in the form to :

<f:selectItems value="#{timezonesConverter.timezonesList}" var="timezone" itemLabel="#{timezone.name}" itemValue="#{timezone.offset}"  />

So instead of the value being a Timezone object (that x reason was considered as an Integer and caused the cast exception), it is the int variable inside the Timezone object that is taken, and that works.

the user entity:

@Entity
@Table(name="users")
@NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    private String username;

    private String email;

    @Column(name="is_enabled")
    private int isEnabled;

    @Temporal(TemporalType.DATE)
    @Column(name="member_since")
    private Date memberSince;

    @Column(name="profile_pic")
    private String profilePic;

    private int reputation;

    private String role;

    private String status;

    private int timezoneOffset;

    @Column(name="warning_lvl")
    private int warningLvl;

    private String xxx;

    //bi-directional many-to-one association to Friend
    @OneToMany(mappedBy="user1")
    private List<Friend> friends1;

    //bi-directional many-to-one association to Friend
    @OneToMany(mappedBy="user2")
    private List<Friend> friends2;

    //bi-directional many-to-one association to Message
    @OneToMany(mappedBy="user1")
    private List<Message> messages1;

    //bi-directional many-to-one association to Message
    @OneToMany(mappedBy="user2")
    private List<Message> messages2;

    //bi-directional many-to-one association to Thethread
    @OneToMany(mappedBy="user1")
    private List<Thethread> thethreads1;

    //bi-directional many-to-one association to Thethread
    @OneToMany(mappedBy="user2")
    private List<Thethread> thethreads2;

    //bi-directional many-to-one association to Usercfg
    @OneToMany(mappedBy="user")
    private List<Usercfg> usercfgs;

    //bi-directional many-to-one association to Userinfo
    @OneToMany(mappedBy="user")
    private List<Userinfo> userinfos;

    //bi-directional many-to-one association to Country
    @ManyToOne
    @JoinColumn(name="country")
    private Country countryBean;

    public User() {
    }
    // getters & setters
}

Upvotes: 0

Views: 1418

Answers (1)

Pavel Sedek
Pavel Sedek

Reputation: 529

The reason why method is not called is, that conversion of values fails.

Country field

CountriesConverter method getAsObject should have return type Object, I expect it's not called at all now.

Country class must implement the equals and hashCode methods.

Timezone field

The timezoneOffset property of the User class must be instance of the Timezone class.

OR

Keep the timezoneOffset int, remove the converter and set the int value using the itemValue attribute. See the example:

<p:selectOneMenu id="timezone" editable="true" required="true" 
        value="#{subscribeUser.user.timezoneOffset}" effect="fold" >
    <f:selectItems value="#{timezonesConverter.timezonesList}" var="timezone" 
        itemLabel="#{timezone.name}" itemValue="#{timezone.offset}"  />
</p:selectOneMenu>

Upvotes: 1

Related Questions