Olá pessoa, à dois dias estou lutando com o JSF nos seguinte ponto:
tenha este selectonemenu
<h:selectOneMenu id="cidades" value="#{usuarioBean.cidade}" converter="CidadeConverter">
<f:selectItems value="#{usuarioBean.cidades}" var="c"
itemValue="#{c.id}" itemLabel="#{c.nome}" />
</h:selectOneMenu>
e tenho o converter:
FacesConverter(value="CidadeConverter")
public class CidadeConverter implements Converter {
@Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String value) {
return new CidadeDAO().buscaCidadePorId(new Integer(value));
}
@Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object value) {
return value.toString();
}
}
Quando executo tenho este erro: cadastro:cidades: Erro de validação: o valor não é válido.
Ah, li em algum lugar algo sobre usar hash e equals e não precisar de converter, mas não faço ideias de como eles funcionam…
Desde já obrigado…
Não é necessário criar conversores para cada objeto que vc for trafegar.
você pode usar uma solução do JBoss Seam 3 que te fornece a tag <s:objectConverter/> que faz isso pra vc automaticamente.
Infelizmente não posso usar o Seam, tenho que conseguir resolver assim mesmo…
Pessoal, e se eu só captar num inteiro o id da cidade selecionada? com o estado consigo fazer isso, e usuando o ajax atualizo as cidades de acordo com o estado, como quando tentei salvar o id da cidade dá erro.
no itemValue, passa o próprio objeto.
e pode tirar também o atributo “converter”.
<h:selectOneMenu id=“cidades” value="#{usuarioBean.cidade}">
<f:selectItems value="#{usuarioBean.cidades}" var=“c”
itemValue="#{c}" itemLabel="#{c.nome}" />
</h:selectOneMenu>
e no Conveter:
@FacesConverter(forClass=Cidade.class)
public class CidadeConverter implements Converter {
@Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String value) {
Integer id = Integer.valueOf(value);
return new CidadeDAO().buscaCidadePorId(id);
}
@Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object value) {
Cidade cidade = (Cidade) value;
return String.valueOf(cidade.getId());
}
}
Fiz como me sugeriu, a tenho a impressão de que estou quase resolvendo, li em algum lugar que eu deveria implementar Serializable, seria isso?
Ainda diz que o valor não é valido!
Bom, vou mostrar todo o meu codigo, pra ver se juntos descobrimos um porque
Este é o managed bean:
@ManagedBean
@RequestScoped
public class UsuarioBean {
private Usuario usuario = new Usuario();
private String confirmarSenha;
private int idEstadoSelecionado;
private int idCidadeSelecionada;
private List<SelectItem> estados;
private List<Estado> estadosObj;
private Cidade[] cidades;
private Cidade cidade;
private Endereco endereco = new Endereco();
public List<SelectItem> getEstados() {
if (estados == null) {
estados = new ArrayList<SelectItem>();
estadosObj = new EstadoDAO().buscaTodosEstados();
for (Estado e : estadosObj) {
estados.add(new SelectItem(e.getId(), e.getNome()));
}
}
return estados;
}
public void setEstados(List<SelectItem> estados) {
this.estados = estados;
}
public Cidade[] getCidades() {
List<Cidade> aux = new CidadeDAO()
.buscarCidadesPorEstado(idEstadoSelecionado);
cidades = (Cidade[]) aux.toArray(new Cidade[aux.size()]);
return cidades;
}
public String novo() {
return "usuario";
}
public String salvar() {
FacesContext context = FacesContext.getCurrentInstance();
if (!confirmarSenha.equals(usuario.getSenha())) {
FacesMessage facesMessage = new FacesMessage(
"A senha não foi confirmada corretamente");
context.addMessage(null, facesMessage);
return null;
}
endereco.setCidade(cidade);
usuario.setEndereco(endereco);
new UsuarioRN().salvarUsuario(usuario);
return "sucesso";
}
//gets e set omitidos em sua maioria
}
Agora o trecho de importancia do xhtml:
<h:outputLabel value="Estado" for="estado" />
<h:selectOneMenu id="estados"
value="#{usuarioBean.idEstadoSelecionado}">
<f:selectItem itemValue="" itemLabel="Selecione" />
<f:selectItems value="#{usuarioBean.estados}" />
<f:ajax execute="@this" render="cidades" />
</h:selectOneMenu>
<h:outputLabel value="Cidade" for="cidade" />
<h:selectOneMenu id="cidades" value="#{usuarioBean.cidade}">
<f:selectItems value="#{usuarioBean.cidades}" var="c"
itemValue="#{c}" itemLabel="#{c.nome}" />
</h:selectOneMenu>
agora o meu CidadeConverter:
@FacesConverter(forClass=Cidade.class)
public class CidadeConverter implements Converter {
@Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String value) {
Integer id = Integer.valueOf(value);
return new CidadeDAO().buscaCidadePorId(id);
}
@Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object value) {
Cidade cidade = (Cidade) value;
return String.valueOf(cidade.getId());
}
}
Observem que no caso do estado eu consigo enviar o valor ao int idEstadoSelecionado, por meio de ajax e também recarregar o vetor de cidades com apenas às equivalentes àquele estado.
Eu queria a principio fazer o mesmo com o idCidadeSelecionada, assim eu poderia montar o objeto por meio do CidadeDAO(). Mas não consegui, então seguindo dicas fiz como mostra o codigo apenas com a cidade , para depois fazer com o estado. Enfim, é isso.
mesmo fazendo o CidadeConverter desse jeito ainda tá dando erro?
continua dando o “valor inválido” pra cidade ?
Sim =/
Eu não consigo encontrar erro algum…
Eu ficaria feliz em fazer sem converter, passando o id pra uma variavel inteira. Mas isso também não consegui
cara, o ideal seria fazer com Conversor, afinal a função dele é justamente essa, converter objeto.
Quando funcionar com o conversor, tbm irá funcionar para ID e vice-versa.
Então, temos que descobrir o que está acontecendo pra ele perder os valores no submit.
Para vermos se tem algo relacionado ao escopo, tira o @RequestScoped e coloca @ViewScoped.
e implementa Serializable nas suas entidades (Usuario, Endereco, Cidade) etc.
se isso funcionar podemos tirar algumas conclusões.
Funcionou apenas mudando o Escopo!
Dessa experiencia tirei que preciso estudar os escopos e também o cliclo de vida do JSF
Agora vou organizar o bean, de maneira que o estado funcione do mesmo jeito que a cidade 
Só mais uma pergunta antes de marcar resolvido, acha melhor eu manter os estados e cidades em memoria, ou buscar no banco cada vez que necessario? neste casa cada vez que o SelectItem muda?
Muito obrigado pela ajuda!
o @ViewScoped guarda o estado dos atributos durante todas as submissões para mesma tela.
quando você trabalha com @RequestScoped e faz requisições Ajax, você acaba perdendo o estado dos atributos, pq o ManagedBean é instanciado novamente e o máximo que vc vai ter são os atributos que foram submetidos nessa requisição Ajax específica. Como vc usou execute="@this" provavelmente só setou nessa nova instância os atributos do estado e não da cidade.
Depois vc tenta colocar execute="@form", é capaz que funcione mesmo com @RequestScoped pq neste caso você levará para esse novo request todos os atributos setados.
Sobre manter as coleções de Estado e Cidade, acho que não é necessário ficar solicitando ao DAO sempre, pq normalmente isso é uma coisa que não muda muito.
a não ser que o cadastro de estado/cidade seja uma coisa que é alterada no sistema com muita frequência.
existem algumas abordagens interessantes, como por exemplo criar um EstadoBean e CidadeBean com @ApplicationScoped
além de fazer o select no banco só uma vez, você pode usar os métodos #{estadoBean.getEstados} e #{cidadeBean.getCidades} diversas vezes, e em diversas telas diferentes, sem precisar ficar criando um getCidades() pra cada ManagedBean que vc for criar.
Certo, muito obrigado! Hoje vou reorganizar e criar um bean pra cidade e outro pro estado.
Mais cedo estava mesmo pensando que era por culpa do ajax que o requestscope num dava certo, contudo, se eu enviar todo o form, vou contra a ideia de velocidade que ganho renderizandp apenas um componente, creio eu.
Enfim, vou fechar o topico, muito obrigado, mais um vez o GUJ me salvou 