O usuário precisa cadastrar vários produtos em sequência. Sem essa feature, a cada insert ele clica Salvar, o modal fecha, ele clica Novo Registro de novo, abre modal de novo, preenche, salva, repete. A feature Insert Chk resolve isso: uma caixinha "Continuar inserindo" no formulário. Marcada, o modal não fecha após salvar — limpa os campos e fica aberto pro próximo registro, com a lista por trás já atualizada.
4 peças trabalhando juntas: campo transiente no Bean, checkbox no Form, lógica condicional no Insert, e ShowInsert recuperando o estado pra manter a caixinha marcada.
package br.xt.app.departamento.produto;
import br.jasap.dao.DBInfo;
import java.io.Serializable;
public class DepartamentoProdutoBean implements Serializable {
public static String TABLE = "departamento.produto";
private Integer id_produto;
private String nome_produto;
private Double vl_produto;
private Integer qtd_produto;
private String obs_produto;
private String qs_produto;
private Integer insert_chk;
@DBInfo(serial=true, pk=true)
public Integer getId_produto() { return id_produto; }
public void setId_produto(Integer id_produto) { this.id_produto = id_produto; }
public String getNome_produto() { return nome_produto; }
public void setNome_produto(String nome_produto) { this.nome_produto = nome_produto; }
public Double getVl_produto() { return vl_produto; }
public void setVl_produto(Double vl_produto) { this.vl_produto = vl_produto; }
public Integer getQtd_produto() { return qtd_produto; }
public void setQtd_produto(Integer qtd_produto) { this.qtd_produto = qtd_produto; }
public String getObs_produto() { return obs_produto; }
public void setObs_produto(String obs_produto) { this.obs_produto = obs_produto; }
public String getQs_produto() { return qs_produto; }
public void setQs_produto(String qs_produto) { this.qs_produto = qs_produto; }
public Integer getInsert_chk() { return insert_chk; }
public void setInsert_chk(Integer insert_chk) { this.insert_chk = insert_chk; }
public static String ID_PRODUTO = "id_produto";
public static String NOME_PRODUTO = "nome_produto";
public static String VL_PRODUTO = "vl_produto";
public static String QTD_PRODUTO = "qtd_produto";
public static String OBS_PRODUTO = "obs_produto";
public static String QS_PRODUTO = "qs_produto";
public static String INSERT_CHK = "insert_chk";
}
Mudanças nesta sequência: 1 campo Integer insert_chk, 1 par getter/setter e 1 constante INSERT_CHK. Não tem @DBInfo — não é coluna do banco, é campo só de UI. Nenhum import novo.
package br.xt.app.departamento.produto;
import br.jasap.core.Effect;
import br.jasap.effect.Response;
import br.jasap.gui.Bar;
import br.jasap.gui.Button;
import br.jasap.gui.JasapPage;
import br.jasap.gui.Table;
import br.jasap.gui.form.Check;
import br.jasap.gui.form.Form;
import br.jasap.gui.form.Text;
import br.jasap.gui.form.Textarea;
import br.jasap.util.DomainValue;
import br.jasap.util.JasapFunctions;
import br.jasap.util.JasapList;
import br.jasap.util.Js;
import br.jasap.util.filters.TransactionFilter;
import br.xt.acore.view.XtPage;
public class DepartamentoProdutoForm extends DepartamentoProdutoAction {
// ... execute() (sem alteração)
public static class ShowInsert extends DepartamentoProdutoForm {
@Override
public Effect execute() throws Exception {
setInsert(FORM);
getSession().remove(ROOT.concat(DepartamentoProdutoBean.ID_PRODUTO));
proBean().setInsert_chk(getInput().getInteger(DepartamentoProdutoBean.INSERT_CHK));
render();
return new Response();
}
}
public static class Insert extends DepartamentoProdutoAction {
public Insert() { super.getFilters().add(new TransactionFilter()); }
@Override
public Effect execute() throws Exception {
Integer chk = proBean().getInsert_chk();
proBean().setInsert_chk(null);
getFactory().departamento().proModel().insert(proBean());
if (JasapFunctions.equals(chk, 1)) {
eval(link(ShowInsert.class).putInteger(DepartamentoProdutoBean.INSERT_CHK, 1).noWait().ajax());
evalParent(link(DepartamentoProdutoList.Sort.class).noWait().ajax(false));
} else {
evalParent(Js.CLOSE_SUB_WINDOWS);
}
return new Response();
}
}
// ... ShowUpdate, Update, Cancelar (sem alteração)
// ... render(), window(), br() (sem alteração)
public String form() throws Exception {
Form frm = ui().form();
frm.addHidden(DepartamentoProdutoBean.ID_PRODUTO, proBean().getId_produto());
frm.line().add(nome_produto(), "150");
frm.line().add(vl_produto(), "150");
frm.line().add(qtd_produto(), "150");
frm.line().add(obs_produto(), "150");
frm.line().add(insert_chk(), "150");
return frm.getTable().toHtml();
}
// ... nome_produto(), vl_produto(), qtd_produto(), obs_produto() (sem alteração)
protected Check insert_chk = null;
public Check insert_chk() throws Exception {
if (insert_chk == null) {
JasapList aux = new JasapList();
aux.getList().add(new DomainValue(1, "Continuar inserindo"));
aux.setSelected(proBean().getInsert_chk());
insert_chk = new Check(getManager(), DepartamentoProdutoBean.INSERT_CHK)
.setLabel(" ")
.setLoad(isInsert(FORM))
.setValue(aux);
}
return insert_chk;
}
public static String FORM = ROOT.concat("__FORM");
}
Mudanças nesta sequência: 4 imports novos (Check, DomainValue, JasapFunctions, JasapList), 1 linha no ShowInsert, bloco do Insert expandido (4 linhas novas + if/else), 1 linha no form(), e o método insert_chk() completo.
"Continuar inserindo" exige coordenação entre o Bean (guardar a escolha), o Form (mostrar o checkbox), a action de Insert (decidir fechar ou não), e a action de ShowInsert (manter a caixinha marcada ao reabrir).
| Peça | Onde | Papel |
|---|---|---|
Campo transiente insert_chk | Bean | Guarda o estado da caixinha (0 ou 1). Sem @DBInfo — não vai pro banco |
Checkbox insert_chk() | Form | Renderiza a caixinha, só no modo insert |
| Lógica condicional | Insert | Marcado → reabre form + atualiza lista; desmarcado → fecha modal |
| Restauração do estado | ShowInsert | Lê INSERT_CHK do input e pré-marca a caixinha |
O fluxo completo (caixinha marcada):
ShowInsert abre modal com form vazio, caixinha desmarcadaInsert — salva no banco, vê chk == 1Insert dispara 2 ações em paralelo (noWait):
ShowInsert com INSERT_CHK=1 — form vazio, caixinha ainda marcadaSort — lista atrás do modal atualiza, mostra o produto recém-criadoSe desmarcar: Insert chama evalParent(CLOSE_SUB_WINDOWS) e o modal fecha normalmente — o setOnCloseURL(url(Sort.class)) da lista dispara o refresh automático.
private Integer insert_chk;
public Integer getInsert_chk() { return insert_chk; }
public void setInsert_chk(Integer insert_chk) { this.insert_chk = insert_chk; }
public static String INSERT_CHK = "insert_chk";
Padrão idêntico aos outros campos — exceto por não ter @DBInfo no getter.
Por que Integer e não Boolean? Convenção do framework Jasap: Check usa Integer (0/1) porque o componente JasapList com DomainValue só trabalha com Integer. Facilita a serialização entre HTML, input e Bean.
Por que NÃO tem @DBInfo? @DBInfo marca um getter como coluna do banco. Sem ele, o framework deixa o campo em paz — não vai pro INSERT, não vai pro UPDATE. É puramente um carregador de estado entre Form e Insert, usado só em memória.
O tipo Integer (em vez de int) é importante: aceita null. Quando a caixinha está desmarcada, getInsert_chk() retorna null, não zero. O JasapFunctions.equals(chk, 1) do Insert dá false pra null e pra zero — ambos levam ao fluxo "fechar modal".
Check + JasapList + DomainValue)
protected Check insert_chk = null;
public Check insert_chk() throws Exception {
if (insert_chk == null) {
JasapList aux = new JasapList();
aux.getList().add(new DomainValue(1, "Continuar inserindo"));
aux.setSelected(proBean().getInsert_chk());
insert_chk = new Check(getManager(), DepartamentoProdutoBean.INSERT_CHK)
.setLabel(" ")
.setLoad(isInsert(FORM))
.setValue(aux);
}
return insert_chk;
}
O Check do Jasap é uma caixa de seleção múltipla — renderiza uma ou mais checkboxes. Aqui usamos com uma única opção, que funciona igual a um checkbox simples.
new JasapList() — container das opções da caixa.new DomainValue(1, "Continuar inserindo") — cria a única opção disponível. Valor interno 1 (o que vai pro Bean se marcado), texto "Continuar inserindo" (o que o usuário lê).aux.setSelected(proBean().getInsert_chk()) — pré-marca a caixa se o Bean já tem insert_chk = 1. É isso que mantém a caixinha marcada quando o ShowInsert reabre o form depois de um insert.new Check(getManager(), INSERT_CHK) — o nome do input no HTML será insert_chk. Esse é o nome que getInput().getInteger(INSERT_CHK) lê..setLabel(" ") — label vazio (espaço não quebrável). O texto "Continuar inserindo" já vem da DomainValue, então não precisa de label. O mantém o alinhamento vertical do form — se fosse "" literal, a linha ficaria desalinhada..setLoad(isInsert(FORM)) — só renderiza a caixinha no modo insert. No update não tem sentido "continuar inserindo", então some da tela..setValue(aux) — passa o JasapList com a opção e o estado pré-marcado.public static class Insert extends DepartamentoProdutoAction {
public Insert() { super.getFilters().add(new TransactionFilter()); }
@Override
public Effect execute() throws Exception {
Integer chk = proBean().getInsert_chk();
proBean().setInsert_chk(null);
getFactory().departamento().proModel().insert(proBean());
if (JasapFunctions.equals(chk, 1)) {
eval(link(ShowInsert.class).putInteger(DepartamentoProdutoBean.INSERT_CHK, 1).noWait().ajax());
evalParent(link(DepartamentoProdutoList.Sort.class).noWait().ajax(false));
} else {
evalParent(Js.CLOSE_SUB_WINDOWS);
}
return new Response();
}
}
4 passos:
chk antes do insertInteger chk = proBean().getInsert_chk();
Lê o valor da caixinha do Bean. Tem que ser antes do insert — o passo 2 vai zerar o campo.
proBean().setInsert_chk(null);
Zera o insert_chk do Bean. Explicado em detalhe na próxima seção — resumo: é proteção contra o framework tentar persistir o campo.
getFactory().departamento().proModel().insert(proBean());
Chamada idêntica à versão anterior do Insert. O TransactionFilter no construtor garante rollback se qualquer coisa explodir.
if (JasapFunctions.equals(chk, 1)) {
eval(link(ShowInsert.class).putInteger(DepartamentoProdutoBean.INSERT_CHK, 1).noWait().ajax());
evalParent(link(DepartamentoProdutoList.Sort.class).noWait().ajax(false));
} else {
evalParent(Js.CLOSE_SUB_WINDOWS);
}
Dois caminhos — detalhados na seção noWait + Sort abaixo.
public static class ShowInsert extends DepartamentoProdutoForm {
@Override
public Effect execute() throws Exception {
setInsert(FORM);
getSession().remove(ROOT.concat(DepartamentoProdutoBean.ID_PRODUTO));
proBean().setInsert_chk(getInput().getInteger(DepartamentoProdutoBean.INSERT_CHK));
render();
return new Response();
}
}
Só uma linha foi adicionada — mas é o que fecha o ciclo:
proBean().setInsert_chk(getInput().getInteger(DepartamentoProdutoBean.INSERT_CHK));
Lê o parâmetro INSERT_CHK do input e grava no Bean. Dois cenários:
INSERT_CHK. getInteger() retorna null. setInsert_chk(null) — Bean fica zerado, caixinha desmarcada.Insert chamou link(ShowInsert.class).putInteger(INSERT_CHK, 1). Dessa vez o input tem INSERT_CHK=1. setInsert_chk(1) — Bean marcado, caixinha continua marcada.Como o insert_chk() do Form faz aux.setSelected(proBean().getInsert_chk()), essa uma linha do ShowInsert é tudo que precisa pra caixinha aparecer marcada.
noWait + Sort — coordenação entre iframe filho e pai
O bloco condicional do Insert tem 2 chamadas em paralelo quando chk == 1:
eval(link(ShowInsert.class).putInteger(DepartamentoProdutoBean.INSERT_CHK, 1).noWait().ajax());
evalParent(link(DepartamentoProdutoList.Sort.class).noWait().ajax(false));
eval(link(ShowInsert.class).putInteger(INSERT_CHK, 1).noWait().ajax());
eval — dispara JS no iframe filho (o do próprio form).link(ShowInsert.class) — cria link pra action ShowInsert..putInteger(INSERT_CHK, 1) — passa o parâmetro INSERT_CHK=1 no input. A Peça 4 lê isso..noWait() — dispara sem bloquear a thread atual. Sem noWait, o Insert esperaria a resposta do ShowInsert antes de continuar, o que pode travar o fluxo porque as duas ações estão no mesmo ciclo de request..ajax() — envia como AJAX (não abre nova janela, atualiza o iframe atual).Resultado: o form atual é substituído por um form novo e vazio, com a caixinha pré-marcada. Usuário vê a tela "piscando" pra um form em branco.
evalParent(link(DepartamentoProdutoList.Sort.class).noWait().ajax(false));
evalParent — dispara JS no iframe pai (a janela da lista). É por isso que o Sort roda na janela certa, mesmo a linha estando no código do Insert.link(Sort.class) — a inner class Sort da List atualiza header + body + navegação sem recarregar a página toda. Já explicada no episódio de Sort..noWait() — dispara e segue..ajax(false) — o parâmetro false aqui desliga o indicador de loading. Sem ele, o spinner apareceria na janela da lista cada vez que o usuário salva com chk marcado — incômodo visual desnecessário.Resultado: lista atrás do modal re-renderiza com o produto recém-criado, sem spinner.
} else {
evalParent(Js.CLOSE_SUB_WINDOWS);
}
Fluxo simples: CLOSE_SUB_WINDOWS é JS que fecha todos os modais abertos. Como a lista foi aberta com setOnCloseURL(url(Sort.class)) no link(ShowInsert).modal(...), o fechamento já dispara o Sort automaticamente. Não precisa de evalParent(Sort) separado.
insert_chk ANTES do insert
Integer chk = proBean().getInsert_chk();
proBean().setInsert_chk(null); // OBRIGATÓRIO
getFactory().departamento().proModel().insert(proBean());
Parece redundante: o campo não tem @DBInfo, então o framework deveria ignorar. Por que limpar?
Porque o SqlInsert do Jasap varre o Bean via reflection. Ele olha todos os getters, monta o INSERT incluindo cada campo que tem um valor não-null. @DBInfo é pra customizar o comportamento (marca PK, marca serial, renomeia coluna), não pra habilitar o campo.
Com a caixinha marcada, proBean().getInsert_chk() == 1. Se deixarmos assim, o SQL gerado vira:
INSERT INTO departamento.produto
(nome_produto, vl_produto, qtd_produto, obs_produto, qs_produto, insert_chk)
VALUES (...)
Erro: column "insert_chk" of relation "produto" does not exist. O banco não tem essa coluna — ela é só estado de UI.
Ao setar null, o SqlInsert vê o campo como "não tem valor" e omite do SQL. O INSERT roda limpo, só com as colunas reais.
Regra geral: todo campo transiente no Bean (sem @DBInfo, mas usado pra carregar estado de UI) tem que ser zerado antes de qualquer insert ou update.
setLoad)
insert_chk = new Check(getManager(), DepartamentoProdutoBean.INSERT_CHK)
.setLabel(" ")
.setLoad(isInsert(FORM))
.setValue(aux);
setLoad é a chave do componente: se false, o Check não é renderizado — o toHtml() devolve string vazia.
isInsert(FORM) retorna true quando o form está no modo insert (foi aberto por ShowInsert) e false no modo update. A caixinha aparece no primeiro caso, some no segundo.
Por que ocultar no update? "Continuar inserindo" só tem sentido quando você está criando registros. No update você está editando um registro específico — ao salvar, o fluxo natural é voltar pra lista, não abrir outro form. Mostrar a caixinha ali geraria confusão sem benefício.
Se preferir esconder via CSS (render + display:none), a caixinha ainda ocuparia espaço no DOM e apareceria em inspeção. setLoad(false) não renderiza nada — é mais limpo.
O Insert Chk isolado faz uma coisa: manter o modal aberto após salvar, limpando os campos. Algumas features parecem fazer parte mas são separadas:
| Feature | Faz parte? | Porquê |
|---|---|---|
Inner class Sort na List | Dependência | Já existia antes — o Insert Chk só usa o Sort pra atualizar a lista. Sem Sort não rolaria, por isso o Sort é pré-requisito. |
setOnCloseURL(url(Sort.class)) no botão "Novo Registro" | Dependência | Garante que o fluxo desmarcado também atualize a lista. Também já existia. |
| Botão Delete no form | Não | Feature separada — o Delete é uma inner class própria, não tem relação com o ciclo de Insert. |
| Reset de página ao inserir | Não | Só faz sentido com paginação ativa — episódio de paginação. |
| Tabs de status | Não | Filtro por status — feature separada. |
| Validação de obrigatoriedade | Não | Já vem do .setRequired(true) nos campos — o submit().validate() barra o envio se qualquer required estiver vazio, independente do chk. |
O Insert Chk que implementamos é o esqueleto: 4 peças, um if/else. As features acima estendem ou combinam com o fluxo de Insert, mas não dependem do chk e o chk não depende delas.
| Arquivo | Tipo | Edição |
|---|---|---|
| DepartamentoProdutoBean | editar | Campo insert_chk (sem @DBInfo), getter/setter, constante INSERT_CHK |
| DepartamentoProdutoForm | editar | 4 imports (Check, DomainValue, JasapFunctions, JasapList), linha no ShowInsert, bloco do Insert expandido, linha no form(), método insert_chk() |
| DepartamentoManager | — | Nada muda — Insert e ShowInsert já estavam registrados |
2 arquivos editados, nenhuma mudança no banco, nenhum arquivo Java novo. Resultado: caixinha "Continuar inserindo" funcionando — marcada mantém o modal aberto pro próximo registro com lista por trás atualizada; desmarcada fecha o modal normalmente.