跳至主要内容

ProTable 控制項介紹

ProTable 是頁面設計器中的通用遠端表格控制項,適合直接呼叫 HTTP API,並在頁面中設定欄位、查詢工具列、分頁與資料列操作。

閱讀本手冊後,你可以完成以下設定:

  • 使用 Custom URL 串接 GET 或 POST 分頁 API
  • 透過 Query Context 將 Input 等控制項的目前值傳給 API
  • 使用 Search 按鈕從第一頁開始一次新的查詢
  • 翻頁時繼續帶入目前查詢條件
  • 設定回應資料路徑、欄位、資料列唯一鍵與顯示樣式
  • 依照本手冊的 Java 範例實作可供 ProTable 使用的後端 API

本手冊使用 Spark 內建的商品查詢範例。API 提供 23 筆模擬商品資料,並支援透過 keyword 對商品編碼與名稱進行不區分大小寫的模糊查詢。

1. ProTable 的適用情境​

1.1 優先使用 ProTable 的情況​

以下情境適合使用 ProTable:

  • 頁面需要直接呼叫自訂後端 API
  • 資料來自第三方系統或彙總服務,而不是單一 Business Object
  • API 已經有自己的分頁、篩選、排序或權限邏輯
  • 按鈕呼叫 API 後,需要透過 setTableData 將結果陣列寫入表格

1.2 ProTable、DataList 與 TaskList 的差異​

控制項主要資料適用情境
ProTableCustom URL 或 Model Data Source通用 HTTP API、彙總資料、自訂分頁契約
DataListBusiness ObjectDQL、模型欄位、Object Action 與模型資料權限
TaskListSpark Tasks任務 Filter、任務狀態、辦理與任務上下文

ProTable 也支援 Model Data Source,其模型繫結方式與 DataList 類似。Business Object、DQL 與模型篩選的設定,請閱讀 DataList 控制項介紹。本手冊重點介紹 ProTable 的 Custom URL 用法。

2. 本手冊範例​

範例 API:

POST https://spark.hiforce.ai/spark/api/sample/protable/products

範例頁面設定如下:

  • 資料來源:Custom URL
  • 請求方式:POST
  • 預設分頁:每頁 10 筆
  • Left Toolbar:一個 Key Word Input 與一個 Search Button
  • Query Context:將 Input 的值對應到請求欄位 keyword
  • Search 互動:呼叫目前 ProTable 的 reloadTable
  • 欄位:code、name、category、price、stock

登入 Spark 並擁有該應用程式的權限後,可以開啟本手冊對應的 Runtime 頁面進行驗證。

3. Custom URL 分頁契約​

3.1 POST 請求​

ProTable 載入第一頁時,會將分頁參數與 Query Context 參數合併為 JSON 請求主體:

{
"current": 1,
"pageSize": 10,
"keyword": "Graphite"
}
欄位型別說明
currentnumber目前頁碼,從 1 開始
pageSizenumber每頁資料筆數
keywordstring本範例透過 Query Context 加入的業務查詢參數

current 與 pageSize 由 ProTable 自動維護,不需要在 Query Context 中重複定義。

3.2 GET 請求​

選擇 GET 時,同一組參數會轉換為 URL Query String:

/spark/api/sample/protable/products?current=1&pageSize=10&keyword=Graphite

GET 請求會略過 undefined、null 與空字串。陣列或物件參數會序列化為 JSON 字串,後端需要使用 JSON Parser 解析。

3.3 預設回應結構​

本範例傳回 Spark 標準 ApiResult,分頁資料位於 data:

{
"success": true,
"code": 200,
"message": "商品資料載入成功。",
"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"
}
]
}
}

因此資料來源路徑設定為:

Rows Path: data.records
Total Path: data.total
Current Page Path: data.current
Page Size Path: data.pageSize

路徑使用點號讀取巢狀屬性。如果 API 的資料列位於 result.items,Rows Path 應填寫 result.items。

分頁 API 必須傳回篩選後的資料總筆數,而不是目前頁面陣列的長度,否則 ProTable 無法計算正確的總頁數。

3.4 頁面請求上下文 Header​

ProTable 使用 Custom URL 發出實際 HTTP 請求時,會自動攜帶兩個標準 Header。後端可以依此選擇測試資料、限制設計器存取,或記錄請求來源:

Header列舉值說明
X-Spark-Page-EnvironmentDEV、TEST、PROD目前頁面環境;Page Designer 中的資料預覽使用 DEV
X-Spark-Page-Render-ModeDESIGN_PREVIEW、RUNTIMEPage Designer、從設計器開啟的 Preview 與實際資料預覽使用 DESIGN_PREVIEW;一般執行頁面使用 RUNTIME

環境與渲染模式是兩個獨立維度。例如,開發環境執行頁面會傳送 DEV + RUNTIME,在 Page Designer 中載入實際資料則會傳送 DEV + DESIGN_PREVIEW。這些值是穩定列舉,後端不應依賴頁面 URL 或 Referer 猜測呼叫情境。

這是所有 Page Designer 與 Page Runtime 後端請求的統一規範,不限於 ProTable 或 Custom URL。Model Data Source、Data Form、Task 控制項、上傳、資料列操作與 Send Request 等管道也會攜帶相同 Header。GET 與 POST 的行為相同;如果 Custom URL 指向跨來源服務,服務端 CORS 設定必須在 Access-Control-Allow-Headers 中允許這兩個 Header。

4. 新增並設定 ProTable​

4.1 新增控制項​

  1. 開啟目標頁面並進入 Page Designer。
  2. 在左側控制項面板展開 Data 分組。
  3. 將 ProTable 拖入可接收的容器。
  4. 選取 ProTable,設定穩定且容易辨識的名稱,例如 productTable。

穩定的名稱便於在 Button 的 Component API 中選擇正確的目標控制項。

4.2 設定 Custom URL​

在 ProTable 的 Data Source 中選擇 Custom URL,依照本範例填寫:

屬性值
URLhttps://spark.hiforce.ai/spark/api/sample/protable/products
Request MethodPOST
Rows Pathdata.records
Total Pathdata.total
Current Page Pathdata.current
Page Size Pathdata.pageSize

URL 支援三種格式:

https://api.example.com/products
connection://erp/products
/products # 同時選擇 Connection Key
  • 同源 Spark API 可以使用完整 URL 或站台相對路徑。
  • 外部服務建議先在應用程式設定中建立 External Connection,再使用 Connection Key。
  • Secret、Token 與密碼應維護在連線設定中,不要寫入 URL 或 Query Context。
  • API 仍受登入狀態、資料權限與後端驗證約束;直接在瀏覽器開啟受保護的 API 時可能傳回 401。

設定 Response Fields​

Model Data Source 可以直接讀取 Business Object 欄位中繼資料,Custom URL 則需要在 Data Source 中維護 Response Fields。每一項包含:

屬性說明
Field Path相對於單筆資料的欄位路徑,例如 name 或 owner.name
LabelColumns 等欄位選擇器中顯示的名稱
Data TypeTEXT、NUMBER、BOOLEAN、DATE、DATETIME、URL、OBJECT 或 ARRAY

可以按下加號手動新增,也可以按下 Test & Import 請求最多 20 筆資料並合併欄位。匯入遵循以下規則:

  • 只有使用者明確按下按鈕時才會請求後端;開啟對話框不會自動請求。
  • 使用目前的 URL、Method、Query Context 與 Rows Path。
  • 巢狀物件轉換為點號路徑;陣列只登記陣列欄位本身。
  • 保留現有欄位的 Label、Data Type 與順序,只附加新路徑。
  • 不儲存範例值;空回應或失敗不會覆寫現有設定。

Test & Import 使用 DEV + DESIGN_PREVIEW 頁面請求上下文。Data Source 對話框中的修改只有按下 Save 後才會進入頁面設定,Cancel 會捨棄目前草稿。

4.3 設定 Columns​

在 Columns 中依序加入:

LabelData Field
Codecode
Namename
Categorycategory
Priceprice
Stockstock

Data Field 支援點路徑。例如資料列中存在 owner.name,欄位可以填寫:

owner.name

欄寬應依照內容設定。編碼欄可以較窄,名稱欄應預留更多空間。不要讓過長文字撐開整個表格。

4.4 設定分頁與資料列唯一鍵​

建議設定:

Pagination / Page Size: 10
Row Unique Key: id

Row Unique Key 對應的欄位必須穩定且唯一。不要使用名稱、狀態等可能重複或變動的欄位。

Runtime 可以切換每頁 10、20、50 筆。頁碼或每頁筆數變更後,ProTable 會重新請求 API,並保留目前的 Query Context 查詢條件。

5. 在 Left Toolbar 中新增查詢控制項​

選取 ProTable,在 Left Toolbar 中新增 Input:

Label: Key Word
Name: productKeywordInput
Allow Clear: true

本範例頁面的 Input 內部 ID 為 field_bfd1e2b2。在自己的頁面中不需要複製這個 ID,只要在 FX 中選擇剛新增的 Input 即可。

開啟 Allow Clear 後,Input 有內容時會顯示清除圖示。按下清除圖示只會清空控制項值;需要再次按下 Search,才會使用空條件重新請求表格。

常見的工具列配置:

  • Left Toolbar:Input、Date Picker、Select、Radio 與 Search
  • Toolbar:Create、Import、Export 等業務操作

如果希望查詢條件與 Search 維持在同一水平線,可以像本範例一樣將 Search 也放入 Left Toolbar。

6. 使用 Query Context 繫結 keyword​

Query Context 用於將頁面 Runtime 值對應到 HTTP 請求參數。本範例需要以下對應關係:

Input 目前值 → productKeyword 變數 → keyword 請求欄位

6.1 新增變數​

  1. 編輯 ProTable 的 Data Source。
  2. 在 Query Context 中按下 FX。
  3. 按下 Add Variable。
  4. Variable Name 填寫 productKeyword。
  5. Source 選擇 Component Value。
  6. Component 選擇剛新增的 Key Word Input。

6.2 新增請求參數​

在 Query Context 的參數區域加入:

Parameter: keyword
Value: [[productKeyword]]

Runtime 會在每次請求前重新讀取 Input 的目前值。使用者沒有輸入內容時,keyword 為空;本範例後端會略過空白關鍵字並傳回所有資料。

6.3 常用變數來源​

Source適用情境
Component ValueInput、Select、Radio、Date Picker、Uploader 等控制項的目前值
URL Parameter瀏覽器 URL 中的查詢參數
Page Context頁面上下文或前一個互動寫入的值
Current Row資料列操作中的目前資料欄位
Static Value固定字串、數字或布林值

Target Value 可以只引用一個變數,也可以組合多個值:

[[productKeyword]]
prefix-[[productKeyword]]

當 Target Value 完全等於 [[variableName]] 時,Runtime 會盡量保留原始型別。陣列、物件、數字或布林值不需要先轉換成字串。

7. 設定 Search 按鈕​

在 Left Toolbar 中新增 Button:

Text: Search
Button Type: Primary

接著設定按下互動:

  1. 選取 Search Button,開啟 Interaction editor。
  2. 新增 Component API 動作。
  3. Target Component 選擇目前 ProTable,例如 productTable。
  4. Function 選擇 reloadTable。
  5. 儲存互動設定。

reloadTable 代表一次新的查詢,它會:

  1. 將目前頁碼重設為 1。
  2. 重新解析 Query Context 中的控制項值。
  3. 使用新的 current、pageSize 與 keyword 請求 API。

這個行為可以避免常見問題:使用者原本位於第 2 頁,輸入只有 1 筆結果的關鍵字後,如果仍傳送 current: 2,API 會正確傳回空陣列。Search 使用 reloadTable 後會傳送 current: 1,因此可以顯示結果。

一般翻頁不是新的查詢。使用者切換頁面時,ProTable 只會修改 current,並繼續帶入目前的 keyword:

按下 Search:重設為第 1 頁 + 使用最新查詢條件
按下分頁:切換頁碼 + 保留目前查詢條件

8. Java 後端範例​

Spark 儲存庫中的完整實作位於:

spark-sample/src/main/java/hiforce/spark/sample/protable/

spark-console 已經引用 spark-sample,因此 API 會隨 Spark Console 一起發佈。以下列出 ProTable Custom URL 所需的核心程式碼。

8.1 請求 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;
}

除了本範例使用的 keyword,還預留了分類、狀態與價格範圍。儲存庫的完整實作也包含通用 conditions 與 sortBy。

8.2 資料列 DTO 與分頁結果​

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;
}

實際專案中,兩個 public 類別應分別放在同名 Java 檔案。此處合併呈現是為了集中說明請求與回應契約。

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");
}
}

Controller 會傳回 DTO 與分頁結果,並統一包裝成 ApiResult。使用者可見的成功訊息透過 Lattice Message Code SPARK-SAMPLE-PROTABLE-0001 國際化,不會直接寫死在 Controller。

8.4 模糊查詢與分頁​

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));
}
}

範例有三個重要行為:

  1. StringUtils.trimToNull 將空白關鍵字轉換為 null,表示不加入關鍵字篩選。
  2. containsIgnoreCase 同時比對商品編碼與名稱,因此輸入 Graphite 可以找到 Graphite Monitor 32。
  3. 先篩選並計算 total,再依照 current 與 pageSize 取得目前頁面。

為了聚焦 keyword 查詢流程,上面的 Service 省略了完整實作中的分類、狀態、價格、通用條件與多欄位排序;23 筆商品與分頁行為和實際範例一致。

8.5 擴充 GET API​

如果也需要支援 GET,可以將查詢參數組裝為同一個 Request DTO,然後重複使用 POST 查詢方法:

@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);
}

實際範例 API 也支援以下選用參數:

參數說明
category不區分大小寫的分類完全比對
status不區分大小寫的狀態完全比對
minPrice / maxPrice價格範圍
conditions通用條件陣列
sortBy從欄位名稱到 asc 或 desc 的有序 Map

使用 GET 傳遞 conditions 或 sortBy 時,它們是 URL 編碼後的 JSON 字串;POST 則直接使用 JSON 陣列或物件。

9. 進階設定​

9.1 多個查詢條件​

可以在 Left Toolbar 中繼續加入 Category Select、Status Radio 或 Price Input,並在 Query Context 中分別對應:

{
"keyword": "[[productKeyword]]",
"category": "[[productCategory]]",
"status": "[[productStatus]]",
"minPrice": "[[minimumPrice]]",
"maxPrice": "[[maximumPrice]]"
}

空值是否代表「不篩選」由後端 API 定義。選用字串建議使用 trimToNull,選用數字使用 null,不要使用特殊字串模擬空值。

9.2 Row Actions​

在 Row Actions 中可以新增 View、Edit、Delete 等操作。資料列操作會提供目前記錄的上下文,可以透過 FX 讀取目前資料列的 id、code 等欄位。

修改成功後,呼叫目前 ProTable 的 reloadTable 重新查詢伺服器資料。危險操作應增加確認步驟,並由後端執行權限與業務驗證。

9.3 setTableData​

當按鈕已經呼叫另一個 API,並取得完整資料列陣列時,可以呼叫:

setTableData(data)

它會使用傳入陣列取代目前本機表格資料,並將頁碼重設為 1。setTableData 不會自動儲存資料,也不會從外層回應自動擷取 data.records;傳入值必須是資料列陣列本身。

標準遠端分頁查詢應優先使用 reloadTable,讓查詢條件、總筆數與分頁繼續由後端統一處理。

9.4 顯示屬性​

屬性說明
Enable Row Number在第一欄顯示跨頁連續的資料列編號
Table Size設定 large、middle 或 small 密度
Show Header是否顯示欄位標題列
Bordered是否顯示表格框線
Row Unique Key每一列的唯一識別欄位,預設為 id

10. 驗證本範例​

儲存頁面後,在 Preview 與 Runtime 中依序驗證:

  1. 不輸入關鍵字,首次載入應顯示第 1 頁,共 23 筆資料。
  2. 切換到第 2 頁,表格應顯示下一頁資料。
  3. 在第 2 頁輸入 Graphite 並按下 Search。
  4. 請求應使用 current: 1,結果只包含 DSP-2002 / Graphite Monitor 32。
  5. 清除 Input 後按下 Search,應重新顯示全部 23 筆資料,並回到第 1 頁。
  6. 輸入可比對多頁資料的關鍵字,按下 Search 後再翻頁,後續請求應繼續帶入該關鍵字。

可以在瀏覽器開發者工具的 Network 中檢查 POST Request Payload:

{
"current": 1,
"pageSize": 10,
"keyword": "Graphite"
}

確認回應中的 data.total、data.current、data.pageSize 與 data.records 符合請求內容。

11. 常見問題​

11.1 在第 2 頁搜尋 Graphite 後找不到資料​

Search 按鈕可能設定為一般請求,而沒有呼叫 ProTable 的 reloadTable。請檢查:

Interaction: Component API
Target Component: 目前 ProTable
Function: reloadTable

reloadTable 會將頁碼重設為 1;一般翻頁仍會保留查詢條件。

11.2 輸入關鍵字後資料沒有變化​

依序檢查:

  1. Query Context 是否存在 keyword 參數。
  2. productKeyword 變數是否從正確 Input 讀取 Component Value。
  3. Target Value 是否為 [[productKeyword]]。
  4. 後端 DTO 欄位是否也命名為 keyword。
  5. Search 是否呼叫目前 ProTable 的 reloadTable。

11.3 Input 沒有顯示清除圖示​

確認 Input 的 Allow Clear 已開啟,而且 Runtime Input 已有文字。空白 Input 不會顯示清除圖示。清除後需要按下 Search 才會重新載入表格。

11.4 API 有資料但表格為空​

檢查 Rows Path。本範例的資料列陣列位於:

data.records

如果設定了錯誤的自訂路徑,ProTable 不會猜測另一個路徑,而會顯示空陣列。

11.5 總筆數或頁碼不正確​

請確認:

  • Total Path 指向篩選後的總筆數
  • Current Page Path 指向伺服器傳回的目前頁碼
  • Page Size Path 指向伺服器實際使用的每頁筆數
  • 後端頁碼從 1 開始

11.6 API 傳回 401、403 或 CORS 錯誤​

  • 401:登入狀態無效,或 API 需要驗證。
  • 403:目前使用者沒有 API 或資料權限。
  • CORS:外部服務未允許目前的 Spark Origin。

外部系統優先透過 External Connection 或受控後端 Proxy 串接,不要在頁面設定中暴露長期憑證。

11.7 無法解析 GET conditions​

GET 會將物件與陣列序列化為 JSON 字串。後端應先進行 URL Decode,再使用 JSON Parser 解析,不要使用逗號手動拆解複雜物件。複雜條件建議使用 POST。

11.8 Custom URL 的 Data Field 沒有候選項目​

開啟 Data Source、設定並儲存 Response Fields。可以手動新增,也可以使用 Test & Import。Response Fields 屬於資料來源契約;載入一筆表格預覽資料不會暫時改變欄位目錄。

12. 設定檢查清單​

  • ProTable 已設定穩定且唯一的控制項名稱
  • Data Source 已選擇 Custom URL
  • Response Fields 已手動設定,或透過 Test & Import 匯入並儲存
  • URL、Request Method 與驗證方式正確
  • Rows、Total、Current Page、Page Size 路徑符合回應結構
  • Columns 的 Data Field 符合資料列欄位
  • Row Unique Key 穩定且唯一
  • Query Context 參數名稱符合後端 DTO 欄位
  • FX 變數讀取正確的 Component Value
  • Search 按鈕呼叫目前 ProTable 的 reloadTable
  • 新查詢會回到第 1 頁
  • 一般翻頁會保留目前查詢條件
  • 已驗證空白、有結果與無結果的關鍵字
  • 已在 Preview 與 Runtime 驗證工具列、表格與分頁

完成以上設定後,ProTable 就可以作為自訂 HTTP API 的穩定清單入口,並正確協同查詢控制項、伺服器分頁與頁面互動。