延伸文章 建立一個在 Business Central 裡獨立運行的應用程式(CRUD) 中的運用,在 Name 欄位 → 開窗查詢 User 資料(例如從系統 User 表或自訂表)→ 選取後自動把使用者的名稱與描述帶回當前記錄。
整體概念
在 AL Page 裡可以使用:
TableRelation→ 建立 Lookup 關聯OnValidate()或OnLookup()→ 自訂選取邏輯與資料回填
可以用「系統內建的 User 表」(User table, ID = 2000000120) 作為範例。
BasicTablePage.al
page 60100 "Basic Table Page"
{
PageType = List;
ApplicationArea = All;
SourceTable = "Basic Table";
UsageCategory = Lists; // 讓它可以從搜尋直接找到
layout
{
area(content)
{
repeater(Group)
{
field(ID; Rec."ID")
{
ApplicationArea = All;
Editable = false;
}
field(Name; Rec."Name")
{
ApplicationArea = All;
}
field(Description; Rec."Description")
{
ApplicationArea = All;
}
field("User ID"; Rec."User ID")
{
ApplicationArea = All;
Caption = 'User';
TableRelation = User."User Security ID"; // 建立查詢來源
trigger OnLookup(var Text: Text): Boolean
var
UserRec: Record User;
begin
// 開啟使用者清單供選擇
if PAGE.RunModal(PAGE::"Users", UserRec) = ACTION::LookupOK then begin
Rec."User ID" := UserRec."User Security ID";
Rec."Name" := UserRec."User Name";
Rec."Description" := UserRec."Full Name";
end;
exit(true); // 告訴系統這個 lookup 已被處理
end;
}
}
}
}
}功能說明
- 使用者點擊
User ID欄位 → 系統開啟內建的 Users 頁面。 - 選取使用者 → 回傳資料到變數
UserRec。 - 將
UserRec的欄位值(User Name,Full Name)帶回當前記錄:
Rec."Name" := UserRec."User Name";
Rec."Description" := UserRec."Full Name";延伸應用
若你想用自訂的 User 表(例如 Employee 或 Customer),只要:
- 將
UserRec改成你的資料表記錄變數; - 把
PAGE.RunModal(PAGE::"Users", UserRec)改成對應的頁面名稱; - 調整回填欄位即可。
總結
這樣部署後:
- 在
Hello World Page點選User ID→ 開窗顯示使用者清單 - 選擇一位使用者 → 自動帶入「使用者名稱」與「全名」到
Name、Description欄位
使用既有欄位查詢
新增一個欄位,似乎是多此一舉,改成「使用者點擊 Name 欄位 → 開啟系統內建的 Users 頁面 → 選取使用者 → 自動回填 Name 與 Description 欄位」而且 不增加 User ID 欄位。
解法重點
在 AL 中可以用:
trigger OnLookup()自訂查詢視窗PAGE.RunModal()開啟使用者頁面- 直接對
Rec記錄物件寫入值
BasicTablePage.al 增加查詢視窗
page 60100 "Basic Table Page"
{
PageType = List;
ApplicationArea = All;
SourceTable = "Basic Table";
UsageCategory = Lists; // 讓它可以從搜尋直接找到
layout
{
area(content)
{
repeater(Group)
{
field(ID; Rec."ID")
{
ApplicationArea = All;
Editable = false;
}
field(Name; Rec."Name")
{
ApplicationArea = All;
Lookup = true;
trigger OnLookup(var Text: Text): Boolean
var
UserRec: Record User;
ConfirmReplace: Boolean;
begin
if PAGE.RunModal(PAGE::Users, UserRec) = ACTION::LookupOK then begin
Rec."Name" := UserRec."User Name";
if Rec."Description" <> '' then begin
ConfirmReplace := Dialog.Confirm(
'此筆資料已有描述 (%1),是否要覆寫為 "%2"?',
false,
Rec."Description",
UserRec."Full Name"
);
if ConfirmReplace then
Rec."Description" := UserRec."Full Name";
end else
Rec."Description" := UserRec."Full Name";
end else begin
// 使用者取消選取 → 清空欄位
Rec."Name" := '';
Rec."Description" := '';
end;
CurrPage.Update();
exit(true);
end;
}
field(Description; Rec."Description")
{
ApplicationArea = All;
}
}
}
}
}注意事項
- Users 是內建頁面(Page ID 9800)。
PAGE::Users是系統保留常數,可直接使用。 - 若出現「找不到 PAGE::Users」錯誤,請:確保
AL: Download Symbols已執行; - 若你想讓 Name 欄位也可手動輸入,只要不加
Editable = false;即可。
執行的畫面如下:

修正 BasicTablePage.al 查詢視窗回傳值時,欄位:Name 沒更新
執行後,可以發現 Name 的值並沒有更新,而是空白,在 OnLookup 觸發器中,讓 Name 欄位在使用者選擇後,正確顯示所選使用者的名稱。根據上面的程式碼,在 OnLookup 觸發器中執行了以下動作:
- 開啟了 User 列表頁面 (
PAGE::Users) 供使用者選擇。 - 如果使用者確認選擇,將 User 記錄的
User Name欄位值賦予給當前記錄的 Name 欄位 (Rec."Name" := UserRec."User Name";)。
修正的程式碼
問題在於,當你在 OnLookup 觸發器中成功取得並賦值給 Rec."Name" 後,你需要確保這個新的值能夠被顯示出來。由於你已經設定了 exit(true); 並且呼叫了 CurrPage.Update();,照理說欄位會被更新。
然而,AL 語言中對於 OnLookup 觸發器的行為是:
- 如果在
OnLookup中返回true,表示已經處理了查找 (lookup) 動作,並且 系統不會再執行預設的查找動作。 - 通常,當執行
exit(true);時,需要手動將選取的值帶回給Text變數 (也就是欄位原本的值) 才能讓頁面欄位顯示新值。但程式碼已經直接修改了Rec."Name",所以這部分邏輯是正確的。
已經使用了 CurrPage.Update() 來更新頁面,這通常是足夠的。
最可能的原因是 field(Name; Rec."Name") 的 OnLookup 觸發器 雖然修改了 Rec."Name" 的值,但該值在離開觸發器後可能沒有被正確地寫入頁面控制項,特別是在列表頁上。
要確保選取的值能被正確地帶回並顯示在欄位中,需要在 OnLookup 觸發器的開頭使用傳入的 Text 參數 來設定回傳值,並在成功選擇後,將新的值賦予給這個 Text 變數。
page 60100 "Basic Table Page"
{
PageType = List;
ApplicationArea = All;
SourceTable = "Basic Table";
UsageCategory = Lists; // 讓它可以從搜尋直接找到
layout
{
area(content)
{
repeater(Group)
{
field(ID; Rec."ID")
{
ApplicationArea = All;
Editable = false;
}
field(Name; Rec."Name")
{
ApplicationArea = All;
Lookup = true;
trigger OnLookup(var Text: Text): Boolean // <--- 注意這裡的 Text 參數
var
UserRec: Record User;
ConfirmReplace: Boolean;
begin
// **修正步驟 1: 將選取邏輯獨立出來**
if PAGE.RunModal(PAGE::Users, UserRec) = ACTION::LookupOK then begin
// **修正步驟 2: 將選取到的使用者名稱賦值給 Rec 欄位**
Rec."Name" := UserRec."User Name"; // 確定這是你希望儲存的欄位
// **修正步驟 3: 將新的值賦予給 Text 參數**
// 這是確保在 OnLookup 結束後,欄位顯示新值的關鍵步驟
Text := Rec."Name";
// 接著處理 Description 欄位更新邏輯
if Rec."Description" <> '' then begin
ConfirmReplace := Dialog.Confirm(
'此筆資料已有描述 (%1),是否要覆寫為 "%2"?',
false,
Rec."Description",
UserRec."Full Name"
);
if ConfirmReplace then
Rec."Description" := UserRec."Full Name";
end else
Rec."Description" := UserRec."Full Name";
// **修正步驟 4: 移除 CurrPage.Update()**
// 通常情況下,當 OnLookup 傳回 true 且 Text 參數有值時,
// 系統會自動更新欄位。
// 在 List 頁面上,頻繁呼叫 CurrPage.Update()
// 可能會導致效能問題或意外行為。
exit(true); // 傳回 true 表示你已經處理了查找動作
end else begin
// 使用者取消選取 → 清空欄位
Rec."Name" := '';
Rec."Description" := '';
Text := ''; // 同時清空 Text 參數
exit(true); // 傳回 true
end;
// 舊的 CurrPage.Update() 已經不需要了
// CurrPage.Update();
// exit(true); // 已經在 if/else 區塊中處理
end;
}
field(Description; Rec."Description")
{
ApplicationArea = All;
}
}
}
}
}修正說明
在 OnLookup 觸發器中加入了以下關鍵行:
Text := Rec."Name";OnLookup觸發器的Text參數是一個var(by reference) 變數,它代表了欄位的最終顯示值。- 在成功選擇使用者後,已經將值賦予給記錄 (
Rec."Name"),但要讓頁面控制項立即顯示這個新值,你必須將這個值也賦予給Text變數。
- 移除
CurrPage.Update();- 當
OnLookup傳回true時,頁面控制項通常會自動使用Text變數的值進行更新。在列表頁上,手動呼叫CurrPage.Update()有時會干擾這個自動流程,或導致不必要的頁面重繪。透過正確設定Text參數並exit(true),可以依賴 AL 的內建機制來處理。
- 當
這樣修改後,當使用者從 “Users” 頁面選擇一個使用者時,Rec."Name" 會被設定,同時 Text 變數也會被設定,確保名稱會立即顯示在頁面上的 Name 欄位中。




