ProTable Component
ProTable is a general-purpose remote table component in Page Designer. Use it to call an HTTP API directly and configure columns, query toolbars, pagination, and row actions on the page.
After reading this guide, you will be able to:
- Connect a Custom URL to a paginated GET or POST endpoint
- Pass current values from Input and other components through Query Context
- Start a new search from the first page with a Search button
- Keep the current search criteria when moving between pages
- Configure response paths, columns, row keys, and display styles
- Implement a ProTable-compatible backend endpoint with the Java example in this guide
This guide uses Spark's built-in product query sample. The endpoint contains 23 sample products and supports a case-insensitive partial match on product code and name through keyword.
1. When to use ProTable
1.1 Recommended use cases
Use ProTable when:
- The page needs to call a custom backend endpoint directly
- Data comes from a third-party or aggregation service rather than one Business Object
- The endpoint owns its pagination, filtering, sorting, or authorization logic
- A button calls an endpoint and then writes the returned array to the table with
setTableData
1.2 ProTable, DataList, and TaskList
| Component | Primary data | Best suited for |
|---|---|---|
| ProTable | Custom URL or Model Data Source | General HTTP APIs, aggregated data, and custom pagination contracts |
| DataList | Business Object | DQL, model fields, Object Actions, and model data permissions |
| TaskList | Spark Tasks | Task filters, task statuses, task processing, and task context |
ProTable also supports Model Data Source, whose model binding is similar to DataList. For Business Objects, DQL, and model filters, read the DataList Component guide. This guide focuses on ProTable's Custom URL data source.
2. Sample used in this guide
Sample endpoint:
POST https://spark.hiforce.ai/spark/api/sample/protable/products
The sample page is configured with:
- Data source: Custom URL
- Request method: POST
- Default page size: 10 records
- Left Toolbar: one
Key WordInput and oneSearchButton - Query Context: maps the Input value to the
keywordrequest field - Search interaction: calls
reloadTableon the current ProTable - Columns:
code,name,category,price, andstock
After signing in to Spark with access to the application, open the runtime page used by this guide to verify the example.
3. Custom URL pagination contract
3.1 POST request
When ProTable loads the first page, it merges pagination fields and Query Context parameters into a JSON request body:
{
"current": 1,
"pageSize": 10,
"keyword": "Graphite"
}
| Field | Type | Description |
|---|---|---|
current | number | Current page number, starting at 1 |
pageSize | number | Number of records per page |
keyword | string | Business query parameter added through Query Context in this example |
ProTable manages current and pageSize automatically. Do not define them again in Query Context.
3.2 GET request
With GET selected, the same parameters are converted to a URL query string:
/spark/api/sample/protable/products?current=1&pageSize=10&keyword=Graphite
GET requests omit undefined, null, and empty-string values. Array and object parameters are serialized as JSON strings, which the backend must parse as JSON.
3.3 Default response structure
The sample returns Spark's standard ApiResult, with paginated data under data:
{
"success": true,
"code": 200,
"message": "Product data loaded successfully.",
"data": {
"total": 1,
"current": 1,
"pageSize": 10,
"requestContext": {
"environment": "DEV",
"renderMode": "RUNTIME"
},
"records": [
{
"id": 4,
"code": "DSP-2002",
"name": "Graphite Monitor 32",
"category": "DISPLAY",
"status": "ACTIVE",
"price": 699.00,
"stock": 21,
"updatedAt": "2026-07-25T16:40:00Z"
}
]
}
}
Configure the data source paths as follows:
Rows Path: data.records
Total Path: data.total
Current Page Path: data.current
Page Size Path: data.pageSize
Paths use dot notation for nested properties. If your endpoint returns records under result.items, set Rows Path to result.items.
A paginated endpoint must return the total number of filtered records, not the length of the current page array. Otherwise, ProTable cannot calculate the correct number of pages.
3.4 Page request context headers
When ProTable sends a real HTTP request through Custom URL, it automatically includes two standard headers. The backend can use them to choose test data, restrict designer access, or record where a request originated:
| Header | Values | Description |
|---|---|---|
X-Spark-Page-Environment | DEV, TEST, PROD | Current page environment; data previews in Page Designer use DEV |
X-Spark-Page-Render-Mode | DESIGN_PREVIEW, RUNTIME | Page Designer, Preview opened from the designer, and real-record previews use DESIGN_PREVIEW; normal runtime pages use RUNTIME |
Environment and render mode are independent dimensions. For example, a runtime page in the development environment sends DEV + RUNTIME, while loading real records inside Page Designer sends DEV + DESIGN_PREVIEW. These values are stable enums; the backend must not infer the scenario from the page URL or Referer.
This is the shared contract for all Page Designer and Page Runtime backend requests, not only ProTable or Custom URL. Model Data Source, Data Form, Task components, uploads, row actions, and Send Request use the same headers. GET and POST behave identically. If a Custom URL targets a cross-origin service, its CORS configuration must allow both headers in Access-Control-Allow-Headers.
4. Add and configure ProTable
4.1 Add the component
- Open the target page in Page Designer.
- Expand Data in the component palette.
- Drag ProTable into a compatible container.
- Select ProTable and give it a stable, recognizable name such as
productTable.
A stable name makes it easier to select the correct target in a Button's Component API interaction.
4.2 Configure Custom URL
Select Custom URL under ProTable's Data Source and enter:
| Property | Value |
|---|---|
| URL | https://spark.hiforce.ai/spark/api/sample/protable/products |
| Request Method | POST |
| Rows Path | data.records |
| Total Path | data.total |
| Current Page Path | data.current |
| Page Size Path | data.pageSize |
The URL supports three forms:
https://api.example.com/products
connection://erp/products
/products # Select a Connection Key as well
- For same-origin Spark APIs, use a full URL or a site-relative path.
- For external services, configure an External Connection in application settings and use its Connection Key.
- Store secrets, tokens, and passwords in the connection configuration, never in the URL or Query Context.
- The endpoint remains subject to authentication, data permissions, and backend authorization. Opening a protected endpoint directly in the browser may return
401.
Configure Response Fields
Model Data Source can read Business Object field metadata directly. With Custom URL, maintain Response Fields in Data Source. Each item contains:
| Property | Description |
|---|---|
| Field Path | Path relative to one record, such as name or owner.name |
| Label | Name shown in Columns and other field selectors |
| Data Type | TEXT, NUMBER, BOOLEAN, DATE, DATETIME, URL, OBJECT, or ARRAY |
Add fields manually with the plus button, or click Test & Import to request up to 20 records and merge the discovered fields. Import follows these rules:
- The backend is called only after an explicit click; opening the dialog never sends a request.
- The request uses the current URL, Method, Query Context, and Rows Path.
- Nested objects become dot paths; arrays register only the array field itself.
- Existing Labels, Data Types, and ordering are preserved; only new paths are appended.
- Sample values are not saved, and an empty or failed response never overwrites existing fields.
Test & Import uses the DEV + DESIGN_PREVIEW page request context. Data Source changes enter the page configuration only after Save; Cancel discards the current draft.
4.3 Configure Columns
Add these entries to Columns in order:
| Label | Data Field |
|---|---|
| Code | code |
| Name | name |
| Category | category |
| Price | price |
| Stock | stock |
Data Field supports dot paths. For example, if a record contains owner.name, enter:
owner.name
Set column widths according to their content. A code column can be narrow, while a name column needs more room. Do not let long text expand the entire table.
4.4 Configure pagination and the row key
Recommended settings:
Pagination / Page Size: 10
Row Unique Key: id
The Row Unique Key field must be stable and unique. Do not use names, statuses, or other values that may repeat or change.
At runtime, users can switch between 10, 20, and 50 records per page. When the page number or size changes, ProTable requests the endpoint again while keeping the current Query Context criteria.
5. Add a query control to Left Toolbar
Select ProTable and add an Input to Left Toolbar:
Label: Key Word
Name: productKeywordInput
Allow Clear: true
The Input's internal ID on the sample page is field_bfd1e2b2. Do not copy this ID into your page. Select the Input you just added from the FX editor instead.
When Allow Clear is enabled, the clear icon appears while the Input contains text. Clearing the Input only changes its value; click Search again to reload the table without the keyword.
A common toolbar layout is:
- Left Toolbar: Input, Date Picker, Select, Radio, and Search controls
- Toolbar: Create, Import, Export, and other business actions
To keep the criteria and Search on the same horizontal line, place Search in Left Toolbar as shown in this example.
6. Bind keyword with Query Context
Query Context maps runtime page values to HTTP request parameters. This example needs the following mapping:
Current Input value → productKeyword variable → keyword request field
6.1 Add a variable
- Edit ProTable's Data Source.
- Click FX under Query Context.
- Click Add Variable.
- Enter
productKeywordas Variable Name. - Select Component Value as Source.
- Select the
Key WordInput you just added as Component.
6.2 Add a request parameter
Add this entry in the Query Context parameter area:
Parameter: keyword
Value: [[productKeyword]]
Before every request, the runtime reads the Input's current value again. If the user enters nothing, keyword is empty. The sample backend ignores a blank keyword and returns all records.
6.3 Common variable sources
| Source | Use case |
|---|---|
| Component Value | Current value of Input, Select, Radio, Date Picker, Uploader, or another component |
| URL Parameter | Query parameter from the browser URL |
| Page Context | Page context or a value written by an earlier interaction |
| Current Row | A field from the current record in a row action |
| Static Value | A fixed string, number, or boolean |
Target Value can reference one variable or combine several values:
[[productKeyword]]
prefix-[[productKeyword]]
When Target Value is exactly [[variableName]], the runtime preserves the original value type whenever possible. Arrays, objects, numbers, and booleans do not need to be converted to strings first.
7. Configure the Search button
Add a Button to Left Toolbar:
Text: Search
Button Type: Primary
Then configure its click interaction:
- Select the Search Button and open Interaction editor.
- Add a Component API action.
- Select the current ProTable, such as
productTable, as Target Component. - Select
reloadTableas Function. - Save the interaction.
reloadTable starts a new query. It:
- Resets the current page to
1. - Resolves current component values in Query Context again.
- Requests the endpoint with the new
current,pageSize, andkeyword.
This behavior prevents a common issue. Suppose a user is on page 2 and enters a keyword with only one matching record. Sending current: 2 would correctly return an empty array. Because Search calls reloadTable, the request sends current: 1 and displays the match.
Normal pagination is not a new search. When the user moves to another page, ProTable changes only current and keeps the current keyword:
Click Search: reset to page 1 + use the latest criteria
Click pagination: change the page + keep the current criteria
8. Java backend example
The complete implementation in the Spark repository is located at:
spark-sample/src/main/java/hiforce/spark/sample/protable/
spark-console includes spark-sample, so the endpoint is published with Spark Console. The following sections show the core code required by a ProTable Custom URL.
8.1 Request DTO
package hiforce.spark.sample.protable.model;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
@Getter
@Setter
public class ProTableProductQueryRequest {
private Integer current = 1;
private Integer pageSize = 10;
private String keyword;
private String category;
private String status;
private BigDecimal minPrice;
private BigDecimal maxPrice;
}
In addition to keyword, the sample reserves category, status, and price range fields. The full repository implementation also includes generic conditions and a sortBy map.
8.2 Row DTO and page result
package hiforce.spark.sample.protable.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.List;
@Getter
@AllArgsConstructor
public class ProTableProductDTO {
private final Long id;
private final String code;
private final String name;
private final String category;
private final String status;
private final BigDecimal price;
private final Integer stock;
private final Instant updatedAt;
}
@Getter
@AllArgsConstructor
public class ProTablePageResult<T> {
private final long total;
private final int current;
private final int pageSize;
private final List<T> records;
}
In an actual project, place the two public classes in separate files with matching names. They are combined here only to show the request and response contract together.
8.3 Controller
package hiforce.spark.sample.protable.web;
import hiforce.spark.base.model.result.ApiResult;
import hiforce.spark.base.web.SparkApiResultHelper;
import hiforce.spark.sample.protable.model.ProTablePageResult;
import hiforce.spark.sample.protable.model.ProTableProductDTO;
import hiforce.spark.sample.protable.model.ProTableProductQueryRequest;
import hiforce.spark.sample.protable.service.ProTableProductService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Locale;
@RestController
@RequestMapping("/spark/api/sample/protable/products")
public class ProTableProductController {
private final ProTableProductService productService;
public ProTableProductController(ProTableProductService productService) {
this.productService = productService;
}
@PostMapping
public ApiResult<ProTablePageResult<ProTableProductDTO>> query(
@RequestBody(required = false) ProTableProductQueryRequest request,
@RequestHeader(value = "Accept-Language", required = false) String language) {
Locale locale = SparkApiResultHelper.resolveLocale(language);
return SparkApiResultHelper.success(
locale,
productService.query(request),
"SPARK-SAMPLE-PROTABLE-0001");
}
}
The Controller returns DTOs and a page result wrapped in ApiResult. The user-facing success message is localized through the Lattice Message Code SPARK-SAMPLE-PROTABLE-0001 instead of being hard-coded in the Controller.
8.4 Query, partial match, and pagination
package hiforce.spark.sample.protable.service;
import hiforce.spark.sample.protable.model.ProTablePageResult;
import hiforce.spark.sample.protable.model.ProTableProductDTO;
import hiforce.spark.sample.protable.model.ProTableProductQueryRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@Service
public class ProTableProductService {
private static final int MAX_PAGE_SIZE = 100;
private static final List<ProTableProductDTO> PRODUCTS = List.of(
product(1, "SPK-1001", "SparkBook Air", "COMPUTING", "ACTIVE", "999.00", 32,
"2026-07-27T14:20:00Z"),
product(2, "SPK-1002", "SparkBook Pro", "COMPUTING", "ACTIVE", "1599.00", 18,
"2026-07-28T09:10:00Z"),
product(3, "DSP-2001", "Aurora Monitor 27", "DISPLAY", "ACTIVE", "499.00", 45,
"2026-07-26T08:30:00Z"),
product(4, "DSP-2002", "Graphite Monitor 32", "DISPLAY", "ACTIVE", "699.00", 21,
"2026-07-25T16:40:00Z"),
product(5, "ACC-3001", "Pulse Mechanical Keyboard", "ACCESSORY", "ACTIVE", "129.00", 86,
"2026-07-24T11:00:00Z"),
product(6, "ACC-3002", "Flow Wireless Mouse", "ACCESSORY", "ACTIVE", "79.00", 120,
"2026-07-23T13:15:00Z"),
product(7, "ACC-3003", "Runtime Dock", "ACCESSORY", "ACTIVE", "219.00", 38,
"2026-07-22T09:45:00Z"),
product(8, "STO-4001", "Core SSD 1TB", "STORAGE", "ACTIVE", "149.00", 64,
"2026-07-21T15:20:00Z"),
product(9, "STO-4002", "Core SSD 2TB", "STORAGE", "ACTIVE", "269.00", 41,
"2026-07-20T10:05:00Z"),
product(10, "NET-5001", "Lattice Router", "NETWORK", "ACTIVE", "189.00", 27,
"2026-07-19T17:25:00Z"),
product(11, "NET-5002", "Mesh Access Point", "NETWORK", "ACTIVE", "139.00", 54,
"2026-07-18T12:35:00Z"),
product(12, "CMP-6001", "Build Station", "COMPUTING", "DRAFT", "2399.00", 7,
"2026-07-17T08:50:00Z"),
product(13, "DSP-2003", "Silk Portable Display", "DISPLAY", "ACTIVE", "329.00", 33,
"2026-07-16T14:10:00Z"),
product(14, "ACC-3004", "Spark AI Headset", "ACCESSORY", "ACTIVE", "199.00", 49,
"2026-07-15T10:45:00Z"),
product(15, "ACC-3005", "Console Camera", "ACCESSORY", "ACTIVE", "159.00", 72,
"2026-07-14T09:35:00Z"),
product(16, "STO-4003", "Archive Drive 8TB", "STORAGE", "ACTIVE", "289.00", 25,
"2026-07-13T18:20:00Z"),
product(17, "NET-5003", "Secure Gateway", "NETWORK", "DRAFT", "799.00", 5,
"2026-07-12T07:30:00Z"),
product(18, "CMP-6002", "Runtime Server", "COMPUTING", "ACTIVE", "3499.00", 4,
"2026-07-11T16:00:00Z"),
product(19, "ACC-3006", "USB-C Travel Hub", "ACCESSORY", "DISCONTINUED", "59.00", 0,
"2026-07-10T11:40:00Z"),
product(20, "DSP-2004", "Aurora Display Mini", "DISPLAY", "DRAFT", "279.00", 12,
"2026-07-09T13:05:00Z"),
product(21, "PRN-7001", "Flow Label Printer", "OFFICE", "ACTIVE", "249.00", 28,
"2026-07-08T08:25:00Z"),
product(22, "AUD-8001", "Aurora Conference Speaker", "AUDIO", "ACTIVE", "349.00", 16,
"2026-07-07T15:35:00Z"),
product(23, "PWR-9001", "Core Power Station", "POWER", "DRAFT", "899.00", 9,
"2026-07-06T10:15:00Z"));
public ProTablePageResult<ProTableProductDTO> query(ProTableProductQueryRequest request) {
ProTableProductQueryRequest query = request == null
? new ProTableProductQueryRequest()
: request;
int current = Math.max(query.getCurrent() == null ? 1 : query.getCurrent(), 1);
int pageSize = Math.min(
Math.max(query.getPageSize() == null ? 10 : query.getPageSize(), 1),
MAX_PAGE_SIZE);
String keyword = StringUtils.trimToNull(query.getKeyword());
List<ProTableProductDTO> filtered = PRODUCTS.stream()
.filter(product -> keyword == null
|| StringUtils.containsIgnoreCase(product.getCode(), keyword)
|| StringUtils.containsIgnoreCase(product.getName(), keyword))
.sorted(Comparator.comparing(ProTableProductDTO::getCode))
.toList();
long requestedOffset = (long) (current - 1) * pageSize;
int fromIndex = (int) Math.min(requestedOffset, filtered.size());
int toIndex = Math.min(fromIndex + pageSize, filtered.size());
return new ProTablePageResult<>(
filtered.size(),
current,
pageSize,
new ArrayList<>(filtered.subList(fromIndex, toIndex)));
}
private static ProTableProductDTO product(long id, String code, String name,
String category, String status, String price,
int stock, String updatedAt) {
return new ProTableProductDTO(
id, code, name, category, status,
new BigDecimal(price), stock, Instant.parse(updatedAt));
}
}
The sample has three important behaviors:
StringUtils.trimToNullconverts a blank keyword tonull, which means no keyword filter.containsIgnoreCasematches both product code and name, soGraphitematchesGraphite Monitor 32.- The service filters and calculates
totalbefore slicing the current page withcurrentandpageSize.
To keep the keyword flow focused, the Service above omits the full repository implementation's category, status, price, generic condition, and multi-field sorting logic. Its 23 products and pagination behavior match the actual sample.
8.5 GET endpoint extension
To support GET as well, assemble the query parameters into the same Request DTO and reuse the POST query method:
@GetMapping
public ApiResult<ProTablePageResult<ProTableProductDTO>> queryByGet(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String keyword,
@RequestHeader(value = "Accept-Language", required = false) String language) {
ProTableProductQueryRequest request = new ProTableProductQueryRequest();
request.setCurrent(current);
request.setPageSize(pageSize);
request.setKeyword(keyword);
return query(request, language);
}
The actual sample endpoint also supports:
| Parameter | Description |
|---|---|
category | Case-insensitive exact category match |
status | Case-insensitive exact status match |
minPrice / maxPrice | Price range |
conditions | Generic condition array |
sortBy | Ordered map from field names to asc or desc |
With GET, conditions and sortBy are URL-encoded JSON strings. With POST, they are sent directly as a JSON array or object.
9. Advanced configuration
9.1 Multiple criteria
Add a Category Select, Status Radio, or Price Input to Left Toolbar and map each value in Query Context:
{
"keyword": "[[productKeyword]]",
"category": "[[productCategory]]",
"status": "[[productStatus]]",
"minPrice": "[[minimumPrice]]",
"maxPrice": "[[maximumPrice]]"
}
The backend defines whether an empty value means "do not filter." For optional strings, use trimToNull; for optional numbers, use null rather than a special string.
9.2 Row Actions
Add View, Edit, Delete, or other operations under Row Actions. A row action receives the current record context, so FX can read fields such as id and code from that row.
After a successful modification, call reloadTable on the current ProTable to retrieve fresh server data. Add confirmation for destructive actions, and enforce authorization and business validation on the backend.
9.3 setTableData
When a button has already called another endpoint and obtained a complete record array, it can call:
setTableData(data)
This replaces the table's local data with the supplied array and resets the page to 1. setTableData does not persist data and does not extract data.records from an outer response object automatically. The supplied value must be the record array itself.
For standard remote pagination, prefer reloadTable so that the backend continues to own the criteria, total, and paging behavior.
9.4 Display properties
| Property | Description |
|---|---|
| Enable Row Number | Displays a continuous row number across pages in the first column |
| Table Size | Sets large, middle, or small density |
| Show Header | Controls whether the column header row is visible |
| Bordered | Controls whether table borders are visible |
| Row Unique Key | Unique identifier field for each row; defaults to id |
10. Verify the sample
After saving the page, verify these steps in Preview and Runtime:
- Leave the keyword empty. The first load should show page 1 with 23 total records.
- Move to page 2. The table should show the next page of data.
- On page 2, enter
Graphiteand click Search. - The request should use
current: 1, and the result should contain onlyDSP-2002 / Graphite Monitor 32. - Clear the Input and click Search. All 23 records should be available again, starting on page 1.
- Enter a keyword that matches multiple pages, click Search, and then paginate. Subsequent requests should keep the keyword.
In the browser developer tools, inspect the POST Request Payload:
{
"current": 1,
"pageSize": 10,
"keyword": "Graphite"
}
Confirm that data.total, data.current, data.pageSize, and data.records in the response match the request.
11. Troubleshooting
11.1 Graphite is not found after searching from page 2
The Search button may be configured as a regular request instead of calling ProTable's reloadTable. Verify:
Interaction: Component API
Target Component: current ProTable
Function: reloadTable
reloadTable resets the page to 1. Normal pagination still keeps the current criteria.
11.2 Data does not change after entering a keyword
Check the following in order:
- Query Context contains a
keywordparameter. - The
productKeywordvariable reads Component Value from the correct Input. - Target Value is
[[productKeyword]]. - The backend request field is also named
keyword. - Search calls
reloadTableon the current ProTable.
11.3 The Input has no clear icon
Make sure Allow Clear is enabled and the runtime Input contains text. The clear icon is not shown for an empty Input. Click Search after clearing to reload the table.
11.4 The endpoint returns data but the table is empty
Check Rows Path. The record array in this guide is located at:
data.records
If you configure an incorrect custom path, ProTable does not guess another custom path and displays an empty array.
11.5 The total or page number is incorrect
Verify that:
- Total Path points to the total number of filtered records
- Current Page Path points to the current page returned by the server
- Page Size Path points to the page size actually used by the server
- Backend page numbering starts at 1
11.6 The endpoint returns 401, 403, or a CORS error
401: The session is invalid, or the endpoint requires authentication.403: The current user does not have endpoint or data permission.- CORS: The external service does not allow requests from the current Spark origin.
Prefer an External Connection or a controlled backend proxy for external systems. Never expose long-lived credentials in page configuration.
11.7 GET conditions cannot be parsed
GET serializes objects and arrays as JSON strings. URL-decode them and use a JSON parser on the backend instead of splitting complex objects by commas. POST has no extra conversion, so prefer POST for complex criteria.
11.8 Custom URL Data Field has no options
Open Data Source, configure Response Fields, and save. Add them manually or use Test & Import. Response Fields belong to the data source contract; loading one table preview record does not temporarily change the field catalog.
12. Configuration checklist
- ProTable has a stable and unique component name
- Data Source is set to Custom URL
- Response Fields have been added manually or imported with Test & Import and saved
- URL, Request Method, and authentication are correct
- Rows, Total, Current Page, and Page Size paths match the response
- Column Data Fields match the record fields
- Row Unique Key is stable and unique
- Query Context parameter names match backend DTO fields
- FX variables read from the correct Component Value
- Search calls
reloadTableon the current ProTable - A new search returns to page 1
- Normal pagination keeps the current criteria
- Empty, matching, and non-matching keywords have been tested
- Toolbar, table, and pagination have been verified in both Preview and Runtime
After completing this configuration, ProTable can serve as a stable list surface for a custom HTTP API and coordinate query controls, server-side pagination, and page interactions correctly.