Files
Unify/Rendering.md
2025-12-25 11:16:59 +01:00

213 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.