Presentation 03
The UI layer
UI + App/ButtonClicked
The engine that turns an entity into a complete admin screen — and one server-side method per page.
TableMakerEx<T> — a generic admin screen
// 1. The table class (DemoApp/Tables)
public class CarsTable : TableMakerEx<Car>
{
public CarsTable()
{
this["Color"].HeaderText = "Color";
this["Color"].Filterable = true;
}
}
// 2. One single line in the page's HTML
<div id="CarsTable" class="table"></div>
- The columns are derived automatically from the entity's
[Column]attributes. - You get all of this for free: the grid, sorting by clicking a header, paging, an add/edit/delete form, export and import.
- The id of the div = the name of the class;
ClassManagerlooks it up by that name.
How does it flow?
- The JS sends the class name plus the sorting, filtering and page; the server returns finished HTML, or just the rows that need updating.
- Saving a form:
SaveForm→SaveFormAsync→ the entity'sSaveAsync. All of it asynchronous.
A generic filter bar
this["IsAutomatic"].Filterable = true; // checkbox → yes/no/all
this["Grade"].Filterable = true;
this["Grade"].DataSource = new[] {1,2,3,4,5,6,7,8}; // → drop-down list
this["Color"].Filterable = true; // text → exact match
- The free-text search box searches every text column (LIKE, with parameters).
- Every change to a filter refreshes the grid without reloading the page.
- It is all implemented in one generic
BuildFilter— there is no per-screen filtering code.
Special fields — UIField
ImageField— drag-and-drop image upload, stored as a JPG named after the record id.ColorField— a color picker with a swatch shown in the grid.ButtonUiField / LinkUiField / IconUiField— an action on every row.ListField<P>— multi-select of related entities (checkboxes).- Declarative validation:
IsMandatory,RegExp, custom messages — enforced on the client side.
The App + ButtonClicked pattern
// every page = a method that returns ReturnDetails
public ReturnDetails Cars() => AdminPage("Cars");
// a button in the HTML — no onclick:
<button id="Cars">Cars</button>
// the id of the button = the name of the method that runs on the server
- One single endpoint:
api/app/ButtonClicked. ReturnDetailstells the client what to do: html to inject, popUp, script, filling a list…- The application state travels back to the client encrypted and compressed (
ProtectAppState) — any tampering with it is spotted immediately. - Full browser history: every page has a URL of its own, deep links included.
A new screen — the checklist
- An entity in the BLL:
class Invoice : Recordwith[Column]. - A table:
class InvoicesTable : TableMakerEx<Invoice>— column headers +Filterable. - A page:
Pages/Invoices.htmlcontaining<div id="InvoicesTable" class="table">. - A method:
public ReturnDetails Invoices() => AdminPage("Invoices"); - A button in the menu:
<input type="button" id="Invoices" value="Invoices">
Five steps, zero SQL, zero JavaScript — and that is the whole story.