Demo App One Plus One Software Logo
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; ClassManager looks it up by that name.

How does it flow?

div.table + JS tablemaker.js api/tablemaker/GetTable CarsTableGetTableAsync → HTML
  • 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: SaveFormSaveFormAsync → the entity's SaveAsync. 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.
  • ReturnDetails tells 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

  1. An entity in the BLL: class Invoice : Record with [Column].
  2. A table: class InvoicesTable : TableMakerEx<Invoice> — column headers + Filterable.
  3. A page: Pages/Invoices.html containing <div id="InvoicesTable" class="table">.
  4. A method: public ReturnDetails Invoices() => AdminPage("Invoices");
  5. A button in the menu: <input type="button" id="Invoices" value="Invoices">

Five steps, zero SQL, zero JavaScript — and that is the whole story.