CRUD
TsgcHTMLComponent_CRUD: 1 つのテーブルの一覧、検索、ページング、作成・編集フォーム、削除確認、書き込み処理を、TDataSet または独自の SQL に対して、Delphi、C++ Builder、.NET で 1 つのコンポーネントとして提供します。
TsgcHTMLComponent_CRUD: 1 つのテーブルの一覧、検索、ページング、作成・編集フォーム、削除確認、書き込み処理を、TDataSet または独自の SQL に対して、Delphi、C++ Builder、.NET で 1 つのコンポーネントとして提供します。
業務アプリケーションがテーブルごとに毎回書き直しているページを、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 には 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 は検索ボックスが対象とするフィールドをセミコロン区切りで列挙します。
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 つのマスターレコードに絞り込みます。
caExport を指定すると、ツールバーに RoutePath('export.csv') への Export リンクが表示されます。このルートには GetExportCSV(search) で応答します。エクスポートされるのは一覧が表示している検索結果で、最大 500 行までであり、テーブル全体ではありません。すべての値が引用符で囲まれるため、カンマや改行でファイルが壊れることはありません。
キーはすべてブラウザーから届きます。ShowEdit、Save、DeleteRecord は、編集や削除を行う前にアダプターに Locate を依頼するので、URL の数値を書き換えても別のレコードには到達しません。Actions も強制されます。caDelete のないページは、偽造されたフォームが削除をポストしても拒否します。セルとフィールドはエスケープされるため、マークアップを含むレコードもテキストとして表示されます。
検索ボックス、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 で、顧客エリアがこのコンポーネントで構築されており、置き換えられた手書きのページと並べて確認できます。