SelectOneMenu e converter RESOLVIDO

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…

Está no seu dia de sorte!
Existem outras boas soluções neste caso:

http://www.rponte.com.br/2008/07/26/entity-converters-pra-da-e-vender/

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 :slight_smile:

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 :slight_smile: