1

When I use f:selectItems the itemLabel doesnt show the property descricao, but show the toString(). I've made some researches, but the problem continues. <f:selectItems> only shows toString() of the model as item label

What am I doing wrong? Any ideas?

I have a class Tipo as follow:

   public class Tipo implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -763536865855419703L;
    // descrição do tipo
    private String descricao;
    // código do tipo
    private Long tipoId;


    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException cnse) {
            return null;
        }
    }

    public Tipo(Long id) {
        this.tipoId = id;
    }

    public Tipo() {

    }

    public String getDescricao() {
        return descricao;
    }

    public Long getTipoId() {
        return tipoId;
    }

    public void setDescricao(String umaDesc) {
        this.descricao = umaDesc;
    }

    public void setTipoId(Long id) {
        this.tipoId = id;
    }




    public String toString() {
        return " ID=" + this.getTipoId() + ", Descricao=" + this.getDescricao();
    }

    @Override
    public boolean equals(Object other){
        return (other != null && getClass() == other.getClass() && tipoId != null) 
                ? tipoId.equals(((Tipo) other).tipoId) : (other == this);
    }

    @Override
    public int hashCode() {
        return (tipoId != null) 
                ? (getClass().hashCode() + tipoId.hashCode()) : super.hashCode();
    }


}

And a TipoDAOImpl:

    public class TipoDAOImpl extends NamedParameterJdbcDaoSupport implements TipoDAO, Serializable {

    private static final long serialVersionUID = 8698127647660788120L;
    private SimpleJdbcInsert sji;
    @Value("#{queries.sql03}")
    private String sql03;
    @Value("#{queries.sql04}")
    private String sql04;

@Override
    public List<Tipo> getTodosTipos() throws DAOException {
        try {
            RowMapper<Tipo> mapper = getRowMapper();
            return getJdbcTemplate().query(this.sql03, mapper);
        } catch (EmptyResultDataAccessException ex) {
            throw new DAOException("Não há registros na tabela de tipos.");
        } catch (DataAccessException e) {
            throw new DAOException(e.getMessage());
        }

    }



    private RowMapper<Tipo> getRowMapper() {
        RowMapper<Tipo> mapper = new RowMapper<Tipo>() {
            public Tipo mapRow(ResultSet rs, int rowNum) throws SQLException {
                Tipo t = new Tipo();
                t.setTipoId(rs.getLong("tipo_id"));
                t.setDescricao(rs.getString("descricao"));

                return t;
            }
        };
        return mapper;

    }

    protected SimpleJdbcInsert getSji() {
        return sji;
    }

    protected void setSji(SimpleJdbcInsert sji) {
        this.sji = sji;
    }


}

ManagedBean:

 @ManagedBean
@SessionScoped
public class TipoMB extends ManagedBeanBasico implements Serializable{


    private static final long serialVersionUID = 2482494734070978599L;
    @ManagedProperty(name = "tipoFacade", value = "#{tipoFacade}")
    private TipoFacade tipoFacade;
    private List<Tipo> listTipos;
    private Tipo tipo;

         public List<Tipo> getTodosTipos(){
                    try {
                        listTipos = tipoFacade.getTodosTipos();
                    } catch (DAOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return listTipos;
                }

Converter:

    @FacesConverter(value="tipoConverter")
public class TipoConverter implements Converter {

    @EJB private Tipo tipo;
    @EJB private TipoFacade tipoFacade;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value)
            throws ConverterException {
        try {
            return tipoFacade.getTipoPorId(Long.parseLong(value));
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (DAOException e) {
            e.printStackTrace();
        }
        return value;

    }

    @Override
    public String getAsString(FacesContext context, UIComponent component,
            Object value) throws ConverterException {
        if (value == null) {
            return "";
        } 
        if (!(value instanceof Tipo)) {
            throw new ConverterException("Não é um tipo válido " + value );
        }
        return ((Tipo) value).getTipoId().toString();

    }

form.xhtml:

<h:outputText value="TIPO:"/>
            <p:selectOneMenu value="#{publicacaoMB.publicacao.tipo}" converter="tipoConverter">
                <f:selectItems value="#{tipoMB.listTipos}" var="tipo"
                            itemLabel="#{tipo.descricao}" itemValue="#{tipo.tipoId}"/>
            </p:selectOneMenu>
Community
  • 1
  • 1

2 Answers2

3

I think this is a Primefaces bug. When the itemLabel expression resolves to null (#{myObject.name} => null), Primefaces shows the toString value of the object. That's wrong because the toString method may not have been overridden and that will result in presenting the internals of the application (class name, etc.) to the end user. It happened to me during a presentation and it was quite embarrassing. The value was null because of bad data in the database.

I guess the PF implementor assumed that if itemLabel was null, it's because it was not set and that the intend was indeed to use toString on the whole object. But itemLabel may have been set but resolved to null, in which case Primefaces should show a null value, being it "null", empty string, "!!", etc.

The selectItems tag could have a "null value" property to tell PF what to show in case itemLabel resolves to null.

Agustí Sánchez
  • 10,455
  • 2
  • 34
  • 25
2

Look like you got the converter logic wrong

  1. In getAsObject you are not returning 1 object but a whole array. You are supposed to return just one, by mathching one of the tipo attributes. You could use the tipoId as long as it's unique per tipo instance. The hashCode might also do the trick.
  2. In getAsString, you should return the same attribute you're using in getAsObject to identify objects. The descricao doesn't seem right.
  3. In the selectOneMenu component, use the object itself, not it's attribute (itemValue="#{tipo}")
yannicuLar
  • 3,083
  • 3
  • 32
  • 50
  • The converter logic is indeed not making sense, but that still doesn't explain the described problem with item label as it doesn't involve the converter. – BalusC Apr 28 '15 at 06:47
  • Thank you @yannicuLar, i changed getAsObject and getAsString, but still appears the toString(). But when I use: `code 'selectItensTipos.add(new SelectItem(String.valueOf(t.getTipoId()), t.getDescricao()));' code` It works, any ideas? – Natália Batuta Apr 28 '15 at 18:56
  • can you provide more code from Tipo Class? I'd like to check the class Declaration (annotations, implements, extends etc) and the tipoId and descricao getters and setters – yannicuLar Apr 28 '15 at 23:27
  • @yannicuLar the entire class Tipo. – Natália Batuta May 04 '15 at 17:02
  • @NatáliaBatuta Sorry, I don't see anything wrong. You could search for known PF bugs, and try updating version. Or debug with a breakpoint in Tipo.getDescricao() – yannicuLar May 13 '15 at 10:05