CRUD
TsgcHTMLComponent_CRUD: 한 테이블의 목록, 검색, 페이징, 생성 및 편집 폼, 삭제 확인, 쓰기 경로를 하나의 컴포넌트로 제공하며, Delphi, C++ Builder 및 .NET에서 TDataSet 또는 사용자 자신의 SQL을 대상으로 동작합니다.
TsgcHTMLComponent_CRUD: 한 테이블의 목록, 검색, 페이징, 생성 및 편집 폼, 삭제 확인, 쓰기 경로를 하나의 컴포넌트로 제공하며, Delphi, C++ Builder 및 .NET에서 TDataSet 또는 사용자 자신의 SQL을 대상으로 동작합니다.
모든 업무용 애플리케이션이 테이블마다 다시 작성하는 페이지를 하나의 컴포넌트로 만들었습니다. 키 필드와 라우트 접두사를 지정한 다음 TDataSet을 바인딩하거나, 사용자 자신의 SQL을 대상으로 하는 여섯 개의 메서드로 이루어진 어댑터를 전달합니다. 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는 쓰기 경로이며, Locate, Read, List, Insert, Update, Delete의 여섯 가지 가상 메서드를 갖습니다. TsgcHTMLCRUDAdapter_DataSet은 바인딩된 데이터셋을 구동하고, SetAdapter는 컴포넌트에 사용자 자신의 어댑터를 지정하며 그 소유권을 가져옵니다. List는 한 페이지를 탭으로 구분된 줄로, 키는 마지막 셀에 담아 응답하고, 전체 집합의 행이 몇 개인지 반환합니다.
TDataSet이 없으므로 이 포트는 어댑터 세 개를 제공합니다. 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를 반환하므로 한 페이지에 여러 컴포넌트를 둘 수 있습니다.
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를 갖습니다. 아무것도 선언하지 않으면 컴포넌트가 활성 데이터셋에서 읽어 옵니다. 캡션은 DisplayLabel에서, 필수 여부와 길이도 함께 가져오며, 키는 표시되지만 편집되지는 않습니다. LoadColumnsFromDataSet은 필요할 때 이 작업을 수행합니다.
비어 있는 필수 열과 MaxLength보다 긴 값은 가장 먼저 거부됩니다. 그다음 OnValidate가 호출됩니다. 문제마다 aErrors에 한 줄씩 추가하면 아무것도 기록되지 않고 폼이 메시지와 함께 되돌아옵니다. OnApplyUpdates는 어댑터보다 먼저 실행되며, aHandled를 설정하면 쓰기를 직접 처리하게 됩니다. OnApplied는 쓰기가 성공한 뒤에 발생하므로, 이를 기록하거나 목록을 보고 있는 다른 사용자의 화면을 새로 고치는 데 사용할 수 있습니다.
VersionField에 버전 필드의 이름을 지정하십시오. 저장할 때 브라우저가 보낸 값과 저장된 값을 비교하며, 그 사이에 다른 사람이 레코드를 변경했다면 쓰기를 거부합니다. 따라서 두 번째로 Save를 누른 사람이 첫 번째 사람의 변경을 덮어쓰지 않습니다.
Details는 레코드의 하위 목록을 담습니다. 각 항목은 다른 TsgcHTMLComponent_CRUD를 CRUD에 지정하고, 마스터 키를 담고 있는 하위 항목의 필드를 MasterField에, 그리고 Caption을 지정합니다. 이 목록들은 레코드를 편집하는 동안 폼 아래에 렌더링됩니다. SetMaster(field, value)는 목록을 하나의 마스터 레코드로 좁힙니다.
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이 핸드셰이크 쿠키에 연결된 세션을 반환하며, 없으면 nil을 반환합니다. HTTP에서는 RoutePath가 호스트가 엔진 라우터에 선언하는 경로를 만듭니다. /customers, /customers/new, /customers/{id}/edit, /customers/save, /customers/{id}/delete입니다.
위와 같이 코드로 생성하거나 Delphi IDE의 마법사를 사용하십시오. 메뉴 Tools › New sgcHTML CRUD page는 열려 있는 폼의 데이터셋을 나열하고, 선택한 데이터셋의 필드를 읽어 구성된 컴포넌트를 폼에 추가하며, 연결에 필요한 두 줄을 클립보드에 넣어 줍니다. Delphi 10.4 이상이 필요합니다. IDE 밖에서는 sgcHTMLGen 명령줄 도구가 테이블, 라우트 접두사, 그리고 name:flag 형식으로 적은 필드로부터 페이지의 소스를 Object Pascal 또는 C#으로 작성합니다. 예를 들면 ID:key 또는 NAME:req:60입니다.
sgcWebSockets와 별도로 판매되는 sgcHTML의 일부입니다. 이 유닛은 SGC_HTML이 정의된 곳에서 컴파일되며, sgcVer.inc는 Android와 iOS를 제외한 모든 플랫폼에서 HTML 팩에 대해 이를 정의합니다. 데모는 Demos\60.HTML\01.RunTime\02.AdminCRUD이며, 여기서는 고객 영역이 이 컴포넌트로 구성되어 있고 그것이 대체한 수작업 페이지가 그 옆에 있습니다.