-
Notifications
You must be signed in to change notification settings - Fork 0
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.
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
}
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 hierarquiaCidadeVO > 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 aCidadeVO
da entidade já que ele seria nulo.
Para contornar esta situação iremos criar no controller um objeto do tipoEstadoVO
, 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
eEstadoVO
são ligados ao objeto auxiliar criado no controller e não ao objeto deCidadeVO
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);
}
}
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 -->