7.2 KiB
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
export default class newsListTable extends gridView {
header = new newsListTableHeader();
body = new newsListTableBody(
newsListItem, // Row Renderer
new collection(news) // Backend Data Binding
);
}
In this example:
newsListItemis the row component used to render individual items.new collection(news)binds to thenewsTable 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:
await this.body.sync();
When the data is synced, the following happens:
- The query is sent to the server (via
node async filterSearch()). - The server executes the query and returns the rows.
- 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:
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:
filterSearch()constructs the filter for the query and is called to fetch the rows.- After filtering, pagination is managed manually.
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:
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, andLIKEare 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:
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:
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:
- The backend logic remains completely abstracted (no REST APIs, no ORM configuration).
- The client UI renders the data based on filter and query objects.
- 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.