CRUD

TsgcHTMLComponent_CRUD: 1 つのテーブルの一覧、検索、ページング、作成・編集フォーム、削除確認、書き込み処理を、TDataSet または独自の SQL に対して、Delphi、C++ Builder、.NET で 1 つのコンポーネントとして提供します。

TsgcHTMLComponent_CRUD

業務アプリケーションがテーブルごとに毎回書き直しているページを、1 つのコンポーネントにしました。キーフィールドとルートプレフィックスを指定し、TDataSet をバインドするか、独自の SQL 上に 6 つのメソッドで構成したアダプターを渡します。ShowList がマークアップを返し、ページからポストされた内容は ProcessAction が処理します。

コンポーネントクラス

TsgcHTMLComponent_CRUD。同じユニット内の TsgcHTMLCRUDAdapter と TsgcHTMLCRUDAdapter_DataSet が付属します

レンダリング内容

検索とページャー付きの一覧、作成・編集フォーム、削除確認

ファミリー

データ & テーブル

言語

Delphi, C++ Builder, .NET

データセットをバインドするか、アダプターを書く

KeyField と RoutePrefix を設定し、アクティブな DataSet をバインドして ShowList(search, page) を呼び出します。独自の SQL を使う場合は、代わりに SetAdapter でコンポーネントにアダプターを渡します。ページからポストされた内容は 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

主なプロパティとメソッド

最もよく使うメンバーです。

アダプター

TsgcHTMLCRUDAdapter は書き込み処理を担い、6 つの仮想メソッドを持ちます: Locate、Read、List、Insert、Update、Delete。TsgcHTMLCRUDAdapter_DataSet はバインドされたデータセットを操作します。SetAdapter は独自のアダプターをコンポーネントに渡し、その所有権を引き継ぎます。List は 1 ページ分をタブ区切りの行として返し、キーは最後のセルに入れ、セット全体の行数を戻り値として返します。

.NET の場合

.NET には TDataSet がないため、.NET 版には 3 つのアダプターが付属します: TsgcHTMLCRUDAdapter_DataTable は DataTable を対象とし、TsgcHTMLCRUDAdapter_List<T> は任意の IList<T> を対象とし、TsgcHTMLCRUDAdapter_DbConnection は DbConnection の背後にあるテーブルを対象として、すべての値をパラメーターとして送信します。Columns は宣言してください。テーブルから読み取られることはありません。

ページ

ShowList(search, page)、ShowNew、ShowEdit(key)、Save(key, values, html)、DeleteRecord(key, html) は、それぞれホストが差し替えるマークアップを返します。ProcessAction は、ポストされた crudList、crudNew、crudEdit、crudSave、crudDelete をこれらに振り分け、それ以外のアクションには False を返すので、1 つのページに複数のコンポーネントを置けます。

ページのオプション

Actions は caCreate、caEdit、caDelete、caView、caExport のセットで、caView だけのページはレポートになります。EditMode は cemModal または cemPage で、ConfirmDelete は削除の前に確認を求めます。PageSize の既定値は 25 で、500 を超えることはありません。SearchFields は検索ボックスが対象とするフィールドをセミコロン区切りで列挙します。

Columns

TsgcHTMLCRUDColumn は FieldName、Caption、Visible、ReadOnly、Required、InList、InForm、Width、MaxLength を持ちます。1 つも宣言しない場合、コンポーネントはアクティブなデータセットから読み取ります。キャプションは DisplayLabel から、必須フラグと長さも同様で、キーは表示されますが編集はされません。LoadColumnsFromDataSet を呼ぶと、必要なときにこの処理を実行できます。

検証

必須の列が空の場合と、MaxLength より長い値は、最初に拒否されます。次に OnValidate が呼ばれます。問題ごとに aErrors に 1 行追加すると何も書き込まれず、メッセージ付きでフォームが戻ります。OnApplyUpdates はアダプターの前に実行され、aHandled を設定すると書き込みを独自に行えます。OnApplied は書き込みが成功した後に発生するので、ログの記録や、一覧を見ている他の画面の更新に使えます。

楽観的ロック

VersionField にバージョンフィールドを指定します。保存時にブラウザーから送られた値と保存済みの値を比較し、その間に他のユーザーがレコードを変更していた場合は書き込みを拒否するので、2 人目に Save を押したユーザーが 1 人目の変更を上書きすることはありません。

マスターと詳細

Details はレコードの子リストを保持します。各項目は、別の TsgcHTMLComponent_CRUD を CRUD に、子側でマスターキーを保持するフィールドを MasterField に、キャプションを Caption に指定します。これらはレコードの編集中にフォームの下に表示されます。SetMaster(field, value) は一覧を 1 つのマスターレコードに絞り込みます。

CSV エクスポート

caExport を指定すると、ツールバーに RoutePath('export.csv') への Export リンクが表示されます。このルートには GetExportCSV(search) で応答します。エクスポートされるのは一覧が表示している検索結果で、最大 500 行までであり、テーブル全体ではありません。すべての値が引用符で囲まれるため、カンマや改行でファイルが壊れることはありません。

最初にキーを特定します

キーはすべてブラウザーから届きます。ShowEdit、Save、DeleteRecord は、編集や削除を行う前にアダプターに Locate を依頼するので、URL の数値を書き換えても別のレコードには到達しません。Actions も強制されます。caDelete のないページは、偽造されたフォームが削除をポストしても拒否します。セルとフィールドはエスケープされるため、マークアップを含むレコードもテキストとして表示されます。

ソケットと HTTP

検索ボックス、New、Edit、Delete、およびフォームは、data-sgc-ws-send が付いたフォームで、それぞれに隠しフィールドの action、crud、key があり、ホストがそれらを ProcessAction に渡します。ソケット経由のメッセージにはパスがなく、ルーターには届かないため、認可は自分で行ってください。Delphi では、TsgcHTMX_Engine_Server.MessageSession がハンドシェイクの Cookie に対応するセッションを返し、なければ nil を返します。HTTP では、RoutePath がホストがエンジンのルーターに宣言するパスを組み立てます: /customers、/customers/new、/customers/{id}/edit、/customers/save、/customers/{id}/delete。

始め方

上記のようにコードで作成するか、Delphi IDE のウィザードを使います。Tools › New sgcHTML CRUD page は、開いているフォーム上のデータセットを一覧表示し、選択したデータセットのフィールドを読み取って、設定済みのコンポーネントをフォームに追加します。接続に必要な 2 行のコードはクリップボードに入ります。Delphi 10.4 以降が必要です。IDE の外では、sgcHTMLGen コマンドラインツールが、テーブル、ルートプレフィックス、フィールドから、Object Pascal または C# でページのソースを書き出します。フィールドは name:flag の形式で指定し、たとえば ID:key や NAME:req:60 のように書きます。

提供状況

sgcHTML の一部で、sgcWebSockets とは別に販売されています。ユニットは SGC_HTML が定義されている環境でコンパイルされ、sgcVer.inc は Android と iOS を除くすべてのプラットフォームで HTML パック用にこれを定義します。デモは Demos\60.HTML\01.RunTime\02.AdminCRUD で、顧客エリアがこのコンポーネントで構築されており、置き換えられた手書きのページと並べて確認できます。

さらに詳しく

オンラインヘルプCRUD ページ、アダプター、書き込み処理の使用ガイドです。
すべての sgcHTML コンポーネント80 以上のコンポーネントの全機能マトリックスを閲覧できます。
無料体験版のダウンロード30 日間の体験版には 60.HTML デモプロジェクトが付属します。
価格完全なソースコード付きの Single、Team、Site ライセンス。
最もお得な選択: All-AccesseSeGeCe の全製品にプレミアムサポートが付いて、年間 €1,059 からご利用いただけます。
All-Access の価格を見る

始める準備はできましたか?

無料体験版をダウンロードして、Delphi、C++ Builder、.NET で Web UI の構築を始めましょう。