JSF 2.0: use Enum values for selectOneMenu [duplicate]

拈花ヽ惹草 提交于 2019-11-28 16:33:13
Ingo Fischer

Ok, here is the final way: - Register the standard enum converter in faces-config.xml (optional):

<converter>
  <converter-for-class>java.lang.Enum</converter-for-class>
  <converter-class>javax.faces.convert.EnumConverter</converter-class>
</converter>

Add a function for example to a managed bean which converts the Enum values to an array of SelectItems:

@ManagedBean
public class GenderBean {
  public SelectItem[] getGenderValues() {
    SelectItem[] items = new SelectItem[Gender.values().length];
    int i = 0;
    for(Gender g: Gender.values()) {
      items[i++] = new SelectItem(g, g.getLabel());
    }
    return items;
  }
}

Then bind this function to the selectOneMenu in JSF:

<h:selectOneMenu id="gender" value="#{person.gender}">
  <!-- use property name not method name -->
  <f:selectItems value="#{genderBean.genderValues}" />
</h:selectOneMenu>

That's it! Not the first explanation for this problem on the net. But i think it's the easiest & shortest one ;)

Ingo Fischer

After looking at my own Seam example for a minute I created a method in a managed bean like this :

@ManagedBean
public class MyManagedBean {
  public Gender[] getGenderValues() {
    return Gender.values;
  }
}   

And in my markup I put

<h:selectOneMenu id="gender" value="#{person.gender}">
  <f:selectItems value="#{myManagedBean.genderValues}" var="g" 
    itemValue="#{g}" itemLabel="#{g.label}"/>
</h:selectOneMenu>

Now I'll have to see if the enum is saved correctly in my entity when the form is sent. I'll see if I can do this myself - anyway, I would appreciate tips or best practices on this!

Roger Keays

Here's a simpler method which uses a simple getter and setter to marshal strings to enums.

http://www.ninthavenue.com.au/blog/using-enums-in-el

I run into this issue some time ago and I solved it as you did, but then I realized at some point that I with that solution I could not use i18n because the strings were hardcoded in the enum class. So I modified my enumConverter to use messages for rendering.

Also sometimes you may want to render the enum as some unique identifier and not as user readable text (for internal usage inside some components).

This is my converter:

import java.util.ResourceBundle;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */




import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;

import com.eyeprevent.configuration.ConfigurationReader;


/**
 * converts an enum in a way that makes the conversion reversible (sometimes)
 * <ul>
 * <li>input: uses its classname and ordinal, reversible<li>
 * <li>else: uses its name, non reversible<li>
 * </ul>
 */
public class EnumConverter implements Converter
{
    @SuppressWarnings("unchecked")
    public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException
    {
        if (value == null || value.length() < 1)
        {
            return null;
        }

        int pos = value.indexOf('@');
        if (pos < 0)
        {
            throw new IllegalArgumentException(value + " do not point to an enum");
        }

        String className = value.substring(0, pos);
        Class clazz;
        int ordinal = Integer.parseInt(value.substring(pos+1), 10);

        try
        {
            clazz = Class.forName( className, true, Thread.currentThread().getContextClassLoader() );
            // if the clazz is not an enum it might be an enum which is inherited. In this case try to find the superclass.
            while (clazz != null && !clazz.isEnum())
            {
                clazz = clazz.getSuperclass();
            }
            if (clazz == null)
            {
                throw new IllegalArgumentException("class " + className + " couldn't be treated as enum");
            }

            Enum[] enums = (Enum[]) clazz.getEnumConstants();
            if (enums.length >= ordinal)
            {
                return enums[ordinal];
            }
        }
        catch (ClassNotFoundException e1)
        {
            throw new RuntimeException(e1);
        }

        throw new IllegalArgumentException("ordinal " + ordinal + " not found in enum " + clazz);
    }

    public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException
    {
        if (value == null)
        {
            return "";
        }

        Enum<?> e = (Enum<?>) value;

        if (component instanceof UIInput || UIInput.COMPONENT_FAMILY.equals(component.getFamily()))
        {
            return e.getClass().getName() + "@" + Integer.toString(e.ordinal(), 10);
        }
        ResourceBundle messages =ConfigurationReader.getMessages(context.getViewRoot().getLocale());
        return messages.getString(e.name());

    }
}

I use this simple approach, it is quite optimistic, you can customize it for your own purpose. I put the following code in a reusable bean, that can be called from your application at any time, so you can use any of your enums declared in your package.

public List<String> fromEnum(String cname) {
        List<String> names = new ArrayList<>();
        try {
            Class c = Class.forName(cname);
            Object[] r = c.getEnumConstants();
            if (r != null) {
                for (Object o : r) {
                    names.add(o.toString());
                }
            }
        } catch (ClassNotFoundException ex) {
            FaceUtil.ShowError(ex);
        }
        return names;
    }
public static void ShowError(Exception ex) {
        FacesMessage msg=new FacesMessage(FacesMessage.SEVERITY_ERROR,ex.getMessage(),"Error Message");
        FacesContext.getCurrentInstance().addMessage(null, msg);
        }

Now use it in the xhtml file as follows:

<p:selectOneMenu value="#{jobapp.aplicant.marital}">
<f:selectItems value="#{rtutil.fromEnum('com.company.package.enMarital')}" var="m" itemLabel="#{m}" itemValue="#{m}"/>
</p:selectOneMenu>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!