First commit

This commit is contained in:
2025-12-25 11:16:59 +01:00
commit 0c5ca09a63
720 changed files with 329234 additions and 0 deletions

212
Rendering.md Normal file
View File

@@ -0,0 +1,212 @@
# Rendering Data: Render Collections, Lists & Pagination
In Unify, data-driven interfaces, such as tables, lists, or collections, are rendered through a powerful concept: **Render Collections**.
A **Render Collection** is a UI component that displays rows of data, with a complete lifecycle from querying the backend to rendering the results, all while maintaining a declarative, JavaScript-centric approach.
## 1. Define a Render Collection
A render collection consists of two key components:
* **The Row Renderer**: A component that defines how each row is visually rendered.
* **The Collection**: A component that defines the data source and binds to a backend Table (like `news`, `users`, etc.).
### Example: Defining a Render Collection
```js
export default class newsListTable extends gridView {
header = new newsListTableHeader();
body = new newsListTableBody(
newsListItem, // Row Renderer
new collection(news) // Backend Data Binding
);
}
```
In this example:
* **`newsListItem`** is the row component used to render individual items.
* **`new collection(news)`** binds to the `news` Table on the backend, providing the data to be displayed.
### How it works:
* The **Collection** defines the data.
* The **Row Renderer** defines how to display each individual row.
---
## 2. Syncing Data — Client-Server Integration
Unify makes sure the **data remains synced** across the client and the backend. You can fetch data on-demand and refresh it easily with the `.sync()` method:
```js
await this.body.sync();
```
When the data is synced, the following happens:
1. The query is sent to the server (via `node async filterSearch()`).
2. The server executes the query and returns the rows.
3. The rows are parsed and displayed in the UI via the **Row Renderer**.
If the collection is already **pre-bound** to a table with an `id`, calling `.sync()` will also refresh that specific objects details.
---
## 3. Pagination — Developer Controlled
Pagination in Unify is **developer-controlled**. Unlike typical frameworks that handle pagination automatically, you have full control over how the **limit**, **offset**, and **searching** are applied.
### Pagination Example:
```js
async update(updatePagination = true) {
if (updatePagination) {
this.page = 0;
}
// Filtering and pagination logic
this.numberOfPages = await this.filterSearch(
this.searchType,
this.searchTerm,
this.sort,
this.direction,
this.limit,
this.page
);
await this.sync();
if (updatePagination) {
this.parents("newsItemPage").tableControl.pagination.create();
}
}
```
In this case:
1. **`filterSearch()`** constructs the filter for the query and is called to fetch the rows.
2. After filtering, **pagination** is managed manually.
3. **`sync()`** then updates the UI with the results.
This gives you control over **pagination logic** and how the list is managed.
---
## 4. Filtering and Searching — Declarative Querying
In Unify, **filtering and searching** are handled using **logical expressions** that are parsed on the server side. You construct the queries using Unify's SQL helpers:
```js
import OR from '/unify/sql/OR.js';
import LIKE from '/unify/sql/LIKE.js';
import GREATER from '/unify/sql/GREATER_OR_EQUAL.js';
import SMALLER from '/unify/sql/SMALLER_OR_EQUAL.js';
node async filterSearch(searchType, searchTerm, order, direction, limit, page) {
const filter = this.getFilter();
switch (searchType) {
case 0:
filter.search = OR(
OR(LIKE(filter.title, searchTerm), LIKE(filter.comments.body, searchTerm)),
LIKE(filter.body, searchTerm)
);
break;
case 1:
filter.search = GREATER(filter.price, searchTerm);
break;
case 2:
filter.search = SMALLER(filter.price, searchTerm);
break;
}
if (!searchTerm) filter.search = false;
filter.order = filter[order];
filter.direction = direction === "desc" ? "desc" : "asc";
filter.limit = parseInt(limit);
filter.from = parseInt(page * limit);
await this.get(); // Fetch results
const numberOfPages = Math.ceil(this.rows.length / limit);
return numberOfPages;
}
```
### Key Points:
* **Logical operators** like `OR`, `AND`, and `LIKE` are used to build declarative filters.
* Filters are executed directly **on the server**. The objects are **parsed as-is**, without the need for string-based queries or manual SQL building.
* **Pagination and sorting** logic is fully under the developers control.
The **query-building** helpers (`LIKE`, `GREATER`, `SMALLER`) are parsed directly by the server. They allow you to declaratively specify complex queries in JavaScript, all while Unify handles the logic and results.
---
## 5. Permissions — Controlling Access to Data
Just like Unify Table objects, **Render Collections** have a permission system that controls who can **read**, **write**, or **delete** data. For example:
```js
permission() {
this.allow(groups.visitor, "READ");
this.allow(groups.member, "READ");
this.allow(groups.admin, "READ");
}
```
Permissions can control:
* Which rows are visible to different user roles
* Which fields inside those rows are visible or editable
Its important to note that **filtering** does not automatically enforce permissions. You must explicitly **validate** access rights to each table, column, or row.
For example, before executing an action like `deleteRow()`, you must check the users permissions:
```js
node async deleteRow(client) {
const user = client.user;
if (!user.hasPermission("DELETE_NEWS")) {
throw new Error("Permission denied");
}
this.delete();
}
```
---
## 6. Lifecycle Summary
| Action | Happens Where | Implemented by |
| --------------------------- | ------------------------ | -------------- |
| Configure UI of list | Client | Developer |
| Construct query logic | Shared (Client + Server) | Developer |
| Execute SQL on server | Server | Unify |
| Deserialize objects | Server → Client | Unify |
| Render rows in the UI | Client | Unify |
| Handle permissions for rows | Server | Developer |
Unify abstracts away complex data handling. The **UI** remains declarative and **directly synced** with the **backend**, ensuring **zero glue code** between database and frontend.
---
## Conclusion
Unifys **Render Collections** provide a simple, declarative way to build **dynamic, paginated, filtered lists**:
1. The **backend logic** remains completely abstracted (no REST APIs, no ORM configuration).
2. The **client UI** renders the data based on **filter and query objects**.
3. The **permissions and security** of the data are automatically respected with clear separation between **frontend** and **backend** logic.
The system gives you **total control** over pagination, sorting, searching, and permissions, while Unify handles the **syncing**, **UI updates**, and **backend query execution** for you.