First commit
This commit is contained in:
212
Rendering.md
Normal file
212
Rendering.md
Normal 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 object’s 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 developer’s 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
|
||||
|
||||
It’s 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 user’s 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
|
||||
|
||||
Unify’s **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.
|
||||
|
||||
Reference in New Issue
Block a user