I'm using JSF 2.0, PrimeFaces and OmniFaces.
I have 2 dialogs with <h:selectManyCheckbox>
. The first dialog creates a new Course
:
The Disciplina
s are presented as:
<h:selectManyCheckbox id="disciplinas"
value="#{cursoMBean.listaDisciplinasDoCurso}"
converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{cursoMBean.listaTodasDisciplinas}"
var="disciplina" itemValue="#{disciplina}"
itemLabel="#{disciplina.nome}" />
</h:selectManyCheckbox>
This works fine. When I select some disciplines and submit the form, then the new Course
with the selected Discipline
s is properly inserted in the DB.
However, when I try to retrieve an existing Course
from the DB, the saved Discipline
s are not preselected.
The code is the same:
<h:selectManyCheckbox id="disciplinas"
value="#{cursoMBean.listaDisciplinasDoCurso}"
converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{cursoMBean.listaTodasDisciplinas}"
var="disciplina" itemValue="#{disciplina}"
itemLabel="#{disciplina.nome}" />
</h:selectManyCheckbox>
Here's the backing bean:
private ArrayList<Disciplina> listaTodasDisciplinas;
private ArrayList<Disciplina> listaDisciplinasDoCurso;
public CursoMBean() {
if (listaTodasDisciplinas == null) {
listaTodasDisciplinas = controleDisciplina.consulta();
}
if (listaDisciplinasDoCurso == null) {
listaDisciplinasDoCurso = new ArrayList<Disciplina>();
}
}
// When user selects one Course to edit, this method is called:
public void setSelecionado(Curso selecionado) {
this.selecionado = selecionado;
if (selecionado != null) {
listaTodasDisciplinas = controleDisciplina.consulta();
listaDisciplinasDoCurso = controleCurso.listaDisciplinasAssociadas(selecionado);
}
}
Here's the Disciplina
entity:
public class Disciplina {
private int id;
private String nome;
public Disciplina() {
}
public Disciplina(int id, String nome) {
this.id = id;
this.nome = nome;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
if (!(nome.isEmpty() || nome == " " || nome == " ")){
this.nome = nome;
}
}
}
How is this caused and how can I solve it?
By default, the SelectItemsConverter
relies on toString()
of the entity to match the selected items. Your entity however doesn't have a toString()
implemented and is thus relying on default fqn@hashcode
result which is not the same when two physically different Disciplina
instances are created even though they have the same value.
You've basically 2 options, also hinted in SelectItemsConverter
showcase and javadoc:
Implement a toString
method that uniquely identifies the entity and which makes sense as an identifier. For example,
@Override
public String toString() {
return String.format("%s[id=%d]", getClass().getSimpleName(), getId());
}
(note that this toString()
is designed such that you can easily keep it in an abstract base class of all your entities, so that you don't need to copypaste the same over all your entities)
Or, if implementing such a toString()
is not an option for some reason (e.g. relying on generated classes which can't be modified afterwards (neither the generator template)), then extend the converter as follows:
@FacesConverter("disciplinaSelectItemsConverter")
public class DisciplinaSelectItemsConverter extends SelectItemsConverter {
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
Integer id = (value instanceof Disciplina) ? ((Disciplina) value).getId() : null;
return (id != null) ? String.valueOf(id) : null;
}
}
(note: you should really be using Integer
instead of int
as ID, the int
cannot be null
which is the correct way to represent a brand new and unpersisted entity)
And use it as follows
<h:selectManyCheckbox ... converter="disciplinaSelectItemsConverter">