Skip to content

Bandboxbind Aninhado com Dependência

Vinícius Ferreira edited this page Mar 2, 2018 · 1 revision

Bandboxbind Aninhado com Dependência

No exemplo a seguir criaremos três bandboxbind aninhados com dependência.
Num caso de uso de cadastro de endereço para funcionários, teremos uma entidade nomeada FuncionarioEnderecoVO que irá servir de envolcro para os dados relacionados ao logradouro da pessoa.
A listagem de cidades vai depender do estado selecionado e, que consequentemente, depende do país selecionado.

Entidades

Criando os atributos nas entidades envolvidas.

FuncionarioEnderecoVO.class

@Entity
public class FuncionarioEnderecoVO extends TutorialBaseVO {
	
	private String rua;
	private String numero;
	private String bairro;
	private CidadeVO cidade;
	private FuncionarioVO funcionario;
	
	@OneToOne(fetch=FetchType.EAGER)
	public FuncionarioVO getFuncionario() {
		return funcionario;
	}

	public void setFuncionario(FuncionarioVO funcionario) {
		this.funcionario = funcionario;
	}

	//Demais atributos, getters e setters omitidos para brevidade do exemplo
}

FuncionarioVO.class

@Entity
public class FuncionarioVO extends TutorialBaseVO {

	private String nome;
	private FuncionarioEnderecoVO endereco;
	
	@OneToOne(mappedBy="funcionario", cascade=CascadeType.ALL, fetch=FetchType.EAGER, orphanRemoval=true)
	public FuncionarioEnderecoVO getEndereco() {
		return endereco;
	}

	public void setEndereco(FuncionarioEnderecoVO endereco) {
		this.endereco = endereco;
	}
	
	//Demais atributos, getters e setters omitidos para brevidade do exemplo
}

PaisVO.class

@Entity
@Lookup
public class PaisVO extends TutorialBaseVO {

	private static final long serialVersionUID = -6691964077120534991L;

	private String nome;
	private String abreviacao;
	private Set<EstadoVO> estados = new LinkedHashSet<EstadoVO>(); 

	@OneToMany(mappedBy="pais", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
	public Set<EstadoVO> getEstados() {
		return estados;
	}

	public void setEstados(Set<EstadoVO> estados) {
		this.estados = estados;
	}

	//Demais atributos, getters e setters omitidos para brevidade do exemplo
}

EstadoVO.class

@Entity
@Lookup
public class EstadoVO extends TutorialBaseVO {

	private String nome;
	private String abreviacao;
	private PaisVO pais;
	private Set<CidadeVO> cidades = new LinkedHashSet<CidadeVO>();

	@OneToMany(mappedBy="estado", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
	@OrderBy("nome")
	public Set<CidadeVO> getCidades() {
		return cidades;
	}

	public void setCidades(Set<CidadeVO> cidades) {
		this.cidades = cidades;
	}

	@ManyToOne(fetch=FetchType.EAGER)
	public PaisVO getPais() {
		return pais;
	}

	public void setPais(PaisVO pais) {
		this.pais = pais;
	}
	
	//Demais atributos, getters e setters omitidos para brevidade do exemplo
}

CidadeVO.class

@Entity
@Lookup
public class CidadeVO extends TutorialBaseVO {

	private static final long serialVersionUID = 5137715580432889802L;

	private String nome;
	private String abreviacao;
	private EstadoVO estado;

	@ManyToOne(fetch = FetchType.EAGER)
	public EstadoVO getEstado() {
		return estado;
	}

	public void setEstado(EstadoVO estado) {
		this.estado = estado;
	}

	//Demais atributos, getters e setters omitidos para brevidade do exemplo
}

Controller

No controller iremos criar os objetos e métodos que nos auxiliarão na utilização dos componentes.

Importante!

No nosso modelo só conseguimos acessar o estado e país através da cidade que é persistida na nossa entidade de endereço, ou seja, em FuncionarioEnderecoVO, quando precisamos ter acesso aos dados do endereço, temos que seguir a seguinte hierarquia CidadeVO > EstadoVO > PaisVO.
Isso vira um problema na hora de aninhar os componentes uma vez que queremos percorrer o caminho inverso, ou seja, primeiro queremos selecionar o país, que então irá filtrar os estados e que, consequentemente, irá filtrar as cidades cadastradas. Numa situação em que estamos cadastrando um novo registro, por exemplo, não conseguiríamos utilizar o objeto respectivo a CidadeVO da entidade já que ele seria nulo.
Para contornar esta situação iremos criar no controller um objeto do tipo EstadoVO, que irá nos auxiliar a preencher os componentes respectivos aos países e estados.

Conforme o código abaixo, crie um objeto do tipo EstadoVO no controller e seus respectivos getter e setter.

FuncionarioCtr.class

private EstadoVO estado = new EstadoVO();

//Demais atributos e métodos ocultados para brevidade do exemplo

public EstadoVO getEstado() {
	if (estado == null)
		estado = new EstadoVO();
		
	if (estado.getPais() == null) {
		estado.setPais(new PaisVO());
	}
	
	return estado;
}

public void setEstado(EstadoVO estado) {
	this.estado = estado;
}

Em seguida, crie os métodos responsáveis por filtrar os resultados no nosso banboxbind na classe controller, conforme o código abaixo.

FuncionarioCtr.class

public List<EstadoVO> filtrarEstado(Bandboxbind box) {

	List<EstadoVO> retorno = new ArrayList<EstadoVO>();

	Bandboxbind componenteDependido = (Bandboxbind) ProHelperView
			.recuperaBandboxParaAninhar(getWindowAtual(),
					box.getDependeDoComponente());
	PaisVO pais = (PaisVO) componenteDependido.getObject();

	if (pais != null)
		retorno.addAll(pais.getEstados());

	return retorno;
}


public List<CidadeVO> filtrarCidade(Bandboxbind box) {

	List<CidadeVO> retorno = new ArrayList<CidadeVO>();

	Bandboxbind componenteDependido = (Bandboxbind) ProHelperView
			.recuperaBandboxParaAninhar(getWindowAtual(),
					box.getDependeDoComponente());
	EstadoVO estado = (EstadoVO) componenteDependido.getObject();

	if (estado != null)
		retorno.addAll(estado.getCidades());

	return retorno;
}
	
//Demais métodos omitidos para brevidade

Agora sobrescrevemos o método antesAtualizaObjetoAtual para que, quando selecionarmos algum objeto já persistido para visualização, os componentes auxiliares correspondentes ao país e estado sejam preenchidos com os dados recuperados do banco.

Importante!

Este passo é necessário uma vez que os componentes referentes ao PaisVO e EstadoVO são ligados ao objeto auxiliar criado no controller e não ao objeto de CidadeVO da nossa entidade, conforme explicado acima. Sendo assim, devemos setar diretamente no objeto auxiliar o estado referente ao endereço recuperado do banco.

FuncionarioCtr.class

@Override
protected void antesAtualizaObjetoAtual(Object objetoAtual) {
	super.antesAtualizaObjetoAtual(objetoAtual);
	
	EstadoVO estado = getObjetoAtual().getEndereco().getCidade().getEstado();
	setEstado(estado);
}

Sobrescrevemos o método configuraValoresPadroesParaObjeto para criar a relação entre as entidades FuncionarioVO e FuncionarioEnderecoVO. Como existe a relação bidirecional um-para-um

@Override
protected void configuraValoresPadroesParaObjeto() {
	super.configuraValoresPadroesParaObjeto();
	FuncionarioEnderecoVO endereco = getObjetoAtual().getEndereco();

	if (endereco == null) {
		endereco = new FuncionarioEnderecoVO();
		endereco.setFuncionario(getObjetoAtual());
		getObjetoAtual().setEndereco(endereco);
	} else {
		endereco.setFuncionario(getObjetoAtual());
		getObjetoAtual().setEndereco(endereco);
	}
} 

View

Criamos os componentes com seus respectivos identificadores, setamos as dependências necessárias com o dependeDoComponente e no metodoFiltro definimos os métodos do controller que irão filtrar a listagem de resultados no respectivo componente.

Funcionario.zul

<label value="Pais" />
<bandboxbind
	nomeDoObjeto="classecontrole.estado.pais"
	labelValorList="Nome:nome; UF:abreviacao"
	atributoQueSeraVisualizado="nome" identificador="pais">
</bandboxbind>

<label value="Estado" />
<bandboxbind
	nomeDoObjeto="classecontrole.estado"
	labelValorList="Nome:nome; UF:abreviacao"
	atributoQueSeraVisualizado="nome" identificador="estado"
	dependeDoComponente="pais" metodoFiltro="filtrarEstado">
</bandboxbind>

<label value="Cidade" />
<bandboxbind nomeDoObjeto="classecontrole.objetoAtual.endereco.cidade"
	labelValorList="Nome:nome; UF:abreviacao"
	atributoQueSeraVisualizado="nome" dependeDoComponente="estado"
	metodoFiltro="filtrarCidade">
</bandboxbind>
	
<!-- Demais componentes ocultados para brevidade -->