Arquivo MAIS EDITADO da sequência DELETE. Ganha a coluna da lixeirinha na tabela, a inner class DeleteFromList que trata o ciclo de dois cliques (confirmar + executar), e uma constante nova CONFIRM_LIST.
package br.xt.app.departamento.produto;
import br.jasap.core.Effect;
import br.jasap.effect.Response;
import br.jasap.gui.JasapPage;
import br.jasap.gui.ListColumn;
import br.jasap.gui.ListLine;
import br.jasap.gui.ListView;
import br.jasap.gui.Table;
import br.jasap.gui.Toast;
import br.jasap.util.JasapFunctions;
import br.jasap.util.Js;
import br.jasap.util.exceptions.SQLConstraintException;
import br.xt.acore.view.IconButton;
import br.xt.acore.view.XtPage;
public class DepartamentoProdutoList extends DepartamentoProdutoAction {
@Override
public Effect execute() throws Exception {
render();
return new Response();
}
public void render() throws Exception {
XtPage page = new XtPage(getManager());
if (isAjaxCall()) {
update(JasapPage.DIV_WINDOW, page.content(window().toHtml()));
} else {
page.getTable()
.setBorder(4)
.rowC("100%")
.setContent(page.content(window().toHtml()))
.setStyle(ui().stretchBorder());
page.setWinTitle("Produtos");
getOutput().write(this, page);
}
}
public Table window() throws Exception {
Table w = new Table(getManager()).setSize("100%", "100%");
w.rowC("99%", JasapPage.DIV_WSPACE, lView());
w.rowC("1%", null, ui().line());
return w;
}
private ListView lv = null;
public ListView lView() throws Exception {
if (lv == null) {
lv = ui().lView();
lv.setOrderBy(DepartamentoProdutoBean.NOME_PRODUTO);
lv.setPageSize(9999);
lv.setFiltro(getFiltro());
getFactory().departamento().proModel().daoList(lv.getData());
ListColumn col_nome = lv.newColumn("Nome").setWidth(220).setPadding(";padding:10 8 10 8;");
ListColumn col_vl = lv.newColumn("Valor").setWidth(100).setPadding(";padding:10 8 10 8;").alignCenter();
ListColumn col_qtd = lv.newColumn("Qtd").setWidth(80).setPadding(";padding:10 8 10 8;").alignCenter();
ListColumn col_obs = lv.newColumn("Observação").setPadding(";padding:10 8 10 8;");
ListColumn col_del = lv.newColumn("").setWidth(50).setPadding(";padding:6 4 6 4;").alignCenter();
while (lv.hasNext()) {
DepartamentoProdutoBean bean = (DepartamentoProdutoBean) lv.next();
ListLine line = lv.createLine();
col_nome.setContent(bean.getNome_produto());
col_vl.setContent(bean.getVl_produto());
col_qtd.setContent(bean.getQtd_produto());
col_obs.setContent(bean.getObs_produto());
col_del.setHtmlData(new IconButton("trash")
.setColor("#d9534f")
.setTitle("Excluir")
.setOnclick(link(DeleteFromList.class).putInteger(DepartamentoProdutoBean.ID_PRODUTO, bean.getId_produto()).ajax())
.toHtml());
lv.addLine(line);
}
}
return lv;
}
public DepartamentoProdutoWBean getFiltro() throws Exception {
DepartamentoProdutoWBean filtro = (DepartamentoProdutoWBean) getSession().getObject(FILTRO);
if (filtro == null) {
filtro = proWBean();
getSession().addObj(FILTRO, filtro);
}
return filtro;
}
public static class DeleteFromList extends DepartamentoProdutoList {
@Override
public Effect execute() throws Exception {
try {
Integer id = getInput().getInteger(DepartamentoProdutoBean.ID_PRODUTO);
if (JasapFunctions.equals(getInput().getInteger(CONFIRM_LIST), 1)) {
DepartamentoProdutoBean bean = new DepartamentoProdutoBean();
bean.setId_produto(id);
getFactory().departamento().proModel().daoDelete(bean);
update(lView().getDIV_BODY(), lView().getBody());
update(lView().getDIV_NAVIGATE(), lView().getNavForm());
} else {
eval(Js.swalConfirm("Confirma a exclusão desse registro?",
link(DeleteFromList.class)
.putInteger(DepartamentoProdutoBean.ID_PRODUTO, id)
.putInteger(CONFIRM_LIST, 1).ajax(), ""));
}
return new Response();
} catch (SQLConstraintException e) {
eval(new Toast("Falha ao excluir: esse registro está sendo usado em outra tabela.").fail());
return new Response();
}
}
}
public static final String LIST = ROOT.concat("__LIST/");
public static final String FILTRO = LIST.concat("__FILTRO");
public static final String CONFIRM_LIST = LIST.concat("__CONFIRM_LIST");
}
Mudanças nesta sequência: 5 imports novos, 1 coluna nova (col_del), IconButton dentro do while, 1 inner class inteira (DeleteFromList), 1 constante (CONFIRM_LIST).
IconButton — componente de ícone do projeto
IconButton é um componente fluent de br.xt.acore.view que gera um botão de ícone pronto com event.stopPropagation() embutido. Ele encapsula o HTML do botão que antes era montado na mão:
new IconButton("trash") // ícone de lixeira (glyphicon)
.setColor("#d9534f") // vermelho Bootstrap (danger)
.setTitle("Excluir") // tooltip ao passar o mouse
.setOnclick(link(...).ajax()) // ação Ajax ao clicar
.toHtml() // gera o HTML final
O stopPropagation já vem embutido — quando a linha da tabela tiver setOnclick (na sequência UPDATE), clicar no ícone NÃO abre o formulário de edição.
Usa setHtmlData() (não setContent()) porque o resultado é HTML bruto que o browser precisa interpretar como botão real.
DeleteFromList
A inner class DeleteFromList trata DOIS cenários no mesmo método:
| Clique | Flag CONFIRM_LIST | O que o agente faz |
|---|---|---|
| 1º (na lixeirinha) | Ausente / não é 1 | Entra no else. Escreve ordem Js.swalConfirm pro browser abrir popup de confirmação. O botão SIM do popup chama a PRÓPRIA DeleteFromList de novo — com CONFIRM_LIST=1. |
| 2º (SIM no popup) | 1 | Entra no if. Cria ficha em branco com só o ID, chama daoDelete, e atualiza parcialmente DIV_BODY + DIV_NAVIGATE da ListView. |
Importante: cada clique é um request NOVO, com agente e cartão novos. O encadeamento ("o segundo clique precisa vir depois do primeiro") vive no CÓDIGO (a flag no input), não na memória do agente — cada agente chega zero-memória.
SQLConstraintException separadamente?
Tentar excluir um produto que tem FK apontando pra ele em outra tabela faz o banco REJEITAR o delete, lançando SQLConstraintException. Esse erro tem uma CAUSA CLARA que dá pra explicar pro usuário.
Sem o catch, a exceção subiria pro ErrorFilter global que mostra "Ocorreu um erro" genérico. Com o catch específico, mostramos Toast com mensagem útil: "Falha ao excluir: esse registro está sendo usado em outra tabela."
Outros erros (conexão, query inválida) NÃO são capturados aqui — sobem pro ErrorFilter, que é a rede de proteção padrão.
Depois do daoDelete, em vez de recarregar a página inteira, atualizamos só o corpo da lista:
update(lView().getDIV_BODY(), lView().getBody());
update(lView().getDIV_NAVIGATE(), lView().getNavForm());
Ao chamar lView() depois do delete, ele é reexecutado — busca a lista nova no banco (sem a linha excluída) e remonta a tabela. O framework manda os dois updates Ajax pro browser, que cola os novos conteúdos por cima dos antigos. Usuário vê a lista sem a linha, tudo sem reload.