CRUD

TsgcHTMLComponent_CRUD: a lista, a busca, a paginação, o formulário de criação e edição, a confirmação de exclusão e o caminho de escrita de uma tabela em um único componente, sobre um TDataSet ou o seu próprio SQL, em Delphi, C++ Builder e .NET.

TsgcHTMLComponent_CRUD

A página que toda aplicação de negócio reescreve para cada tabela, como um único componente. Informe um campo chave e um prefixo de rota, e então vincule um TDataSet ou entregue a ele um adaptador de seis métodos sobre o seu próprio SQL. ShowList devolve o markup, e o que a página envia de volta passa por ProcessAction.

Classe do componente

TsgcHTMLComponent_CRUD, com TsgcHTMLCRUDAdapter e TsgcHTMLCRUDAdapter_DataSet na mesma unit

Renderiza

A lista com busca e paginação, o formulário de criação e edição e a confirmação de exclusão

Família

Dados & Tabelas

Linguagens

Delphi, C++ Builder, .NET

Vincule um dataset ou escreva o adaptador

Defina KeyField e RoutePrefix, vincule um DataSet ativo e chame ShowList(search, page). Para o seu próprio SQL, dê ao componente um adaptador com SetAdapter. Encaminhe o que a página envia de volta por meio de ProcessAction.

uses
  Classes, sgcHTML_Component_CRUD;

var
  oCRUD: TsgcHTMLComponent_CRUD;
  vHTML: string;
begin
  oCRUD := TsgcHTMLComponent_CRUD.Create(nil);
  try
    oCRUD.CRUDID := 'customers';
    oCRUD.Title := 'Customers';
    oCRUD.RoutePrefix := '/customers';
    oCRUD.KeyField := 'ID';
    oCRUD.PageSize := 15;
    oCRUD.Actions := [caCreate, caEdit, caDelete, caView, caExport];
    oCRUD.VersionField := 'UPDATED_AT';   // optimistic locking
    oCRUD.OnValidate := CustomersValidate;

    // an active dataset: the columns are read off it
    oCRUD.DataSet := qryCustomers;

    vHTML := oCRUD.ShowList('', 1);   // search text, page number
  finally
    oCRUD.Free;
  end;
end;

// The rules that are about your data. Add a line per problem: nothing is
// written and the form comes back with the messages.
procedure TServer.CustomersValidate(Sender: TObject;
  aOperation: TsgcHTMLCRUDOperation; const aKey: string;
  aValues, aErrors: TStrings);
begin
  if Length(Trim(aValues.Values['NAME'])) < 3 then
    aErrors.Add('The name needs at least three characters.');
end;

// A message from the browser carries action, crud and key with the form fields
if oCRUD.ProcessAction(vAction, vKey, oFields, vHTML) then
  aResponse := vHTML;   // the markup to swap into the page

// Or over your own SQL: an adapter of six methods
type
  TCustomersAdapter = class(TsgcHTMLCRUDAdapter)
  public
    function Locate(const aKey: string): Boolean; override;
    function Read(const aKey: string; aValues: TStrings): Boolean; override;
    function List(const aSearch: string; aPage, aPageSize: Integer;
      aRows: TStrings): Integer; override;
    function Insert(aValues: TStrings; var aKey: string;
      aErrors: TStrings): Boolean; override;
    function Update(const aKey: string; aValues, aErrors: TStrings): Boolean;
      override;
    function Delete(const aKey: string; aErrors: TStrings): Boolean; override;
  end;

oCRUD.SetAdapter(TCustomersAdapter.Create(oCRUD));   // the component owns it
// includes: sgcHTML_Component_CRUD.hpp

TsgcHTMLComponent_CRUD *oCRUD = new TsgcHTMLComponent_CRUD(NULL);
try
{
  oCRUD->CRUDID = "customers";
  oCRUD->Title = "Customers";
  oCRUD->RoutePrefix = "/customers";
  oCRUD->KeyField = "ID";
  oCRUD->PageSize = 15;
  oCRUD->Actions = TsgcHTMLCRUDActions() << caCreate << caEdit << caDelete
    << caView << caExport;
  oCRUD->VersionField = "UPDATED_AT";   // optimistic locking
  oCRUD->OnValidate = CustomersValidate;

  // an active dataset: the columns are read off it
  oCRUD->DataSet = qryCustomers;

  String html = oCRUD->ShowList("", 1);   // search text, page number
}
__finally
{
  delete oCRUD;
}

// The rules that are about your data. Add a line per problem: nothing is
// written and the form comes back with the messages.
void __fastcall TServer::CustomersValidate(TObject *Sender,
  TsgcHTMLCRUDOperation aOperation, const String aKey,
  TStrings *aValues, TStrings *aErrors)
{
  if (aValues->Values["NAME"].Trim().Length() < 3)
    aErrors->Add("The name needs at least three characters.");
}

// A message from the browser carries action, crud and key with the form fields
String html;
if (oCRUD->ProcessAction(action, key, fields, html))
  response = html;   // the markup to swap into the page

// For your own SQL, derive from TsgcHTMLCRUDAdapter, override its six methods
// (Locate, Read, List, Insert, Update, Delete) and call oCRUD->SetAdapter().
using System.Collections.Generic;
using esegece.sgcWebSockets;

var crud = new TsgcHTMLComponent_CRUD();
crud.CRUDID = "customers";
crud.Title = "Customers";
crud.RoutePrefix = "/customers";
crud.KeyField = "ID";
crud.PageSize = 15;
crud.Actions = TsgcHTMLCRUDActions.caCreate | TsgcHTMLCRUDActions.caEdit
    | TsgcHTMLCRUDActions.caDelete | TsgcHTMLCRUDActions.caView
    | TsgcHTMLCRUDActions.caExport;
crud.VersionField = "UPDATED_AT";   // optimistic locking

// The key travels with the page: it is in neither the list nor the form
var id = crud.Columns.Add();
id.FieldName = "ID";
id.InList = false;
id.InForm = false;

var name = crud.Columns.Add();
name.FieldName = "NAME";
name.Caption = "Name";
name.Required = true;
name.MaxLength = 120;

// There is no TDataSet: read a DataTable, an IList<T> or a table behind a DbConnection
crud.SetAdapter(new TsgcHTMLCRUDAdapter_DataTable(crud, customersTable));

// The rules that are about your data
crud.OnValidate += (object sender, TsgcHTMLCRUDOperation op, string recordKey,
    List<string> values, List<string> errors) =>
{
    if (TsgcHTMLComponent_CRUD.ValueOf(values, "NAME").Trim().Length < 3)
        errors.Add("The name needs at least three characters.");
};

string html = crud.ShowList("", 1);   // search text, page number

// A message from the browser carries action, crud and key with the form fields
if (crud.ProcessAction(action, key, fields, out string reply))
    response = reply;   // the markup to swap into the page

Principais propriedades & métodos

Os membros que você usa com mais frequência.

Adaptador

TsgcHTMLCRUDAdapter é o caminho de escrita, com seis métodos virtuais: Locate, Read, List, Insert, Update e Delete. TsgcHTMLCRUDAdapter_DataSet controla o dataset vinculado, e SetAdapter entrega ao componente um adaptador seu e passa a ser o dono dele. List devolve uma página como linhas separadas por tabulação, com a chave na última célula, e retorna quantas linhas o conjunto inteiro tem.

No .NET

Não existe TDataSet, então a versão .NET traz três adaptadores: TsgcHTMLCRUDAdapter_DataTable sobre um DataTable, TsgcHTMLCRUDAdapter_List<T> sobre qualquer IList<T> e TsgcHTMLCRUDAdapter_DbConnection sobre uma tabela atrás de um DbConnection, com todo valor enviado como parâmetro. Declare as Columns: elas não são lidas da tabela.

A página

ShowList(search, page), ShowNew, ShowEdit(key), Save(key, values, html) e DeleteRecord(key, html) devolvem, cada um, o markup que o host insere na página. ProcessAction encaminha para eles as ações crudList, crudNew, crudEdit, crudSave e crudDelete recebidas no post, e devolve False para qualquer outra ação, de modo que uma página possa conter vários componentes.

Opções da página

Actions é um conjunto de caCreate, caEdit, caDelete, caView e caExport, e uma página só com caView é um relatório. EditMode é cemModal ou cemPage, ConfirmDelete pergunta antes de uma exclusão, PageSize tem 25 como padrão e nunca passa de 500, e SearchFields lista os campos em que a caixa de busca procura, separados por ponto e vírgula.

Colunas

Um TsgcHTMLCRUDColumn tem FieldName, Caption, Visible, ReadOnly, Required, InList, InForm, Width e MaxLength. Sem nenhuma declarada, o componente as lê do dataset ativo: o título a partir de DisplayLabel, o indicador de obrigatório e o tamanho, e a chave é exibida, mas nunca editada. LoadColumnsFromDataSet faz isso sob demanda.

Validação

Uma coluna obrigatória vazia e um valor mais longo que MaxLength são recusados primeiro. Depois OnValidate é consultado: adicione uma linha por problema a aErrors e nada será gravado; o formulário volta com as mensagens. OnApplyUpdates executa antes do adaptador, e definir aHandled torna a gravação responsabilidade sua. OnApplied dispara depois que uma gravação foi concluída, para registrá-la em log ou atualizar quem mais estiver olhando a lista.

Bloqueio otimista

Informe um campo de versão em VersionField. Um salvamento compara o que o navegador envia com o que está armazenado e recusa a gravação quando outra pessoa alterou o registro nesse intervalo, de modo que a segunda pessoa a pressionar Save não sobrescreve a primeira.

Mestre e detalhe

Details guarda as listas filhas de um registro. Cada item nomeia outro TsgcHTMLComponent_CRUD em CRUD, o campo do filho que guarda a chave do mestre em MasterField e uma Caption. Elas são renderizadas abaixo do formulário enquanto um registro está sendo editado. SetMaster(field, value) restringe uma lista a um registro mestre.

Exportação CSV

Com caExport, a barra de ferramentas mostra um link Export para RoutePath('export.csv'). Responda a essa rota com GetExportCSV(search): ele exporta a busca que a lista está mostrando, até 500 linhas, e não a tabela inteira. Todo valor vai entre aspas, de modo que uma vírgula ou uma quebra de linha não consegue quebrar o arquivo.

A chave é localizada primeiro

Toda chave chega do navegador. ShowEdit, Save e DeleteRecord pedem ao adaptador que a localize com Locate antes de qualquer edição ou exclusão, então mudar um número em uma URL nunca alcança outro registro. Actions também é imposto: uma página sem caDelete recusa uma exclusão mesmo quando um formulário forjado a envia. Células e campos são escapados, então um registro que contém markup é exibido como texto.

Socket e HTTP

A caixa de busca, New, Edit, Delete e o formulário são formulários marcados com data-sgc-ws-send, cada um com os campos ocultos action, crud e key, e o host os entrega a ProcessAction. Uma mensagem pelo socket não carrega caminho e nunca chega ao roteador, então autorize-a você mesmo. No Delphi, TsgcHTMX_Engine_Server.MessageSession devolve a sessão por trás dos cookies do handshake, ou nil. Para HTTP, RoutePath monta os caminhos que um host declara no roteador do engine: /customers, /customers/new, /customers/{id}/edit, /customers/save e /customers/{id}/delete.

Formas de começar

Crie-o em código, como acima, ou use o assistente da IDE do Delphi: Tools › New sgcHTML CRUD page lista os datasets do formulário aberto, lê os campos do que você escolher e adiciona ao formulário um componente configurado, com as duas linhas de ligação na área de transferência. Exige o Delphi 10.4 ou posterior. Fora da IDE, a ferramenta de linha de comando sgcHTMLGen grava o código-fonte da página em Object Pascal ou em C#, a partir da tabela, do prefixo de rota e dos campos, cada um escrito como name:flag, por exemplo ID:key ou NAME:req:60.

Disponibilidade

Parte do sgcHTML, que é vendido de forma independente do sgcWebSockets. A unit compila onde SGC_HTML está definido, o que o sgcVer.inc faz para o pacote HTML em todas as plataformas, exceto Android e iOS. A demonstração é Demos\60.HTML\01.RunTime\02.AdminCRUD, onde a área de clientes é construída sobre este componente, ao lado da página escrita à mão que ele substituiu.

Continue explorando

Ajuda onlineGuia de uso para páginas CRUD, o adaptador e o caminho de escrita.
Todos os Componentes sgcHTMLExplore a matriz completa de recursos com mais de 80 componentes.
Baixar Versão de Avaliação GratuitaA avaliação de 30 dias inclui os projetos de demonstração 60.HTML.
PreçosLicenças Single, Team e Site com código-fonte completo.
Melhor custo-benefício: All-AccessTodos os produtos da eSeGeCe, com Suporte Premium incluído, a partir de €1,059/ano.
Ver preços do All-Access

Pronto para Começar?

Baixe a versão de avaliação gratuita e comece a construir UIs web em Delphi, C++ Builder e .NET.