原生 JS

原生 JS 讓您可以在 Ninox 腳本中執行 JavaScript 程式碼,並與腳本共用函式與變數。如此一來,原生 JS 就能讀取 Ninox 變數,並呼叫同一個公式中宣告的函式;至於全域程式碼中的函式,則要透過 ninext.callNinoxFunction 呼叫(參見下文「呼叫全域程式碼中的函式」)。

原生 JS 預設為啟用;它可以在 Ninext 設定的「JavaScript 整合」區段中停用(原生 JavaScript)。

image.png

限制

在原生 JS 中修改 Ninox 變數,並不會影響該 Ninox 變數。

JavaScript 只會在本機執行。把 JavaScript 加進在伺服器上執行的程式碼中不會有任何效果;同樣地,從在伺服器上執行的程式碼呼叫含有 JavaScript 的全域函式,也不會有任何效果。自 3.2.0 版起,錯誤分頁會標示直接放在表格觸發器(建立後、修改後)或 do as server 中的區塊。

哪些程式碼在本機執行、哪些在伺服器上執行,請參見下圖:

image.png

語法

執行

若要告訴 Ninox 您正切換到 JavaScript,必須把程式碼包在 #{ ... }# 之中:

Ninox
#{ alert("hello world") }#;

別忘了,Ninox 是預先編譯的語言,必須事先知道每個變數與函式的型別。原生 JS 也是如此:您必須指定 JavaScript 程式碼所回傳的型別。原生 JS 預設回傳 text,但您可以在程式碼最開頭以 :[type] 這種寫法指出想回傳的變數型別。

註解要緊貼著 #{,不能有任何空格:#{:number …,而不是 #{ :number …。同樣地,兩個註解要緊接著寫,中間不留空格(:text:callback)。若有空格,註解就不會被辨識:區塊會被當作文字型別,而註解會留在 JavaScript 程式碼中,造成語法錯誤。

回傳值

Ninox
#{:text return "hello world"}#;

可用的型別有:text、number、date、timestamp(日期與時間)、time、timeinterval、appointment、boolean、html、color、icon、email、phone、location、file、user、json 以及 any。完整的清單(包括 choice、link、style 與 styled)會由控制台提示(見下文)。

注意

帶時間的日期要註記為 :timestamp,而同一型別的函式參數則宣告為 datetime。註解中並沒有 :datetime:區塊會被當作文字型別。自 3.2.0 版起,錯誤分頁會標示未知的註解。

日期、時間與時長都以毫秒交換:這類型別的 Ninox 變數會以數字形式進入 JavaScript,而 :date 或 :array(date) 區塊必須回傳毫秒(也就是 JavaScript 日期的 getTime() 所提供的值)。

陣列型別

若要回傳值的陣列,請以 :array(type) 指定其元素的型別(自 3.2.0 版起):

Ninox
#{:array(number) return [1, 2, 3] }#;

所有簡單型別皆可使用::array(text)、:array(date)、:array(any)…

在 3.1.2 版(含)之前,只能回傳型別為 any 的陣列。這種寫法仍然有效,等同於 :array(any):

Ninox
#{:any(array) return [1, 2, 3] }#;

本頁過去所介紹的 :number(array) 寫法並不會回傳陣列。自 3.2.0 版起,錯誤分頁會將這種寫法標示出來。

記錄型別

若要回傳記錄,請使用 nid 或 rid,後面接著放在括號中的表格名稱:

如果使用 nid,就必須回傳一個文字值陣列,內容為完整的記錄識別碼,也就是表格識別碼 + 記錄編號:

Ninox
#{:nid(customer) return ['A1','A2','A3'] }#

如果使用 rid,則應回傳記錄編號:

Ninox
#{:rid(customer) return [1,2,3] }#

若表格名稱由多個字組成,不必像在 Ninox 中那樣加上 ',直接使用表格的完整名稱即可:

Ninox
#{:rid(my long name table) return [1,2,3] }#

採用這種語法時,原生 JS 回傳的變數,其行為與 Ninox 程式碼中的變數完全相同。也就是說,您可以像使用 select 一樣寫出下列語法:

Ninox
(#{:rid(customer) return [1,2,3] }# order by 'Last Name').'First Name';

image.png

控制台中的輸入輔助

自 3.2.0 版起,控制台會協助您撰寫區塊:

  • 在註解中:在 #{ 之後緊接著輸入 : 時,它會提示回傳型別、array(type)、nid(Table) 與 rid(Table);在 nid 或 rid 之後輸入 ( 時,會列出表格,在 array 之後則列出型別;第二個 : 會提示修飾詞 callback 與 async;
  • 在 JavaScript 中:自動完成會以 JavaScript 形式,提示區塊所收到的 Ninox 變數與函式,接著在句點之後,提示物件的成員——若是記錄,則是其欄位名稱(參見控制台頁面中的「原生區塊中的 JavaScript 自動完成」)。

這項輔助只存在於控制台中。

Ninext 會檢查什麼

自 3.2.0 版(測試版)起。 錯誤分頁會檢查整個資料庫中的 #{ … }# 區塊,控制台則檢查您在其中撰寫之程式碼裡的區塊。

會被標示的有:

  • JavaScript 語法錯誤;
  • 未知的註解(例如 :datetime),或在 :nid(…) 或 :rid(…) 中找不到的表格:區塊會被當作文字型別;
  • 有型別卻完全沒有 return 的區塊,或部分路徑一個也無法到達的區塊:它會回傳空值;
  • 從未呼叫 callback 的 :callback 或 :async 區塊:公式將永遠得不到值;
  • 直接放在表格觸發器(建立後、修改後)或 do as server 中的區塊,它在那裡不會執行。

修改記錄

記錄型別的變數以 JSON 格式傳遞,記錄的每個欄位對應一組鍵值對。

Ninox
var r := first(select Customer);
#{:text return r['First Name'] }#;

在此,變數 r 在 JavaScript 中的內容看起來像這樣:

Ninox
{
 "Created on": 1648910817206,
 "Id": "A1",
 "Last modified": 1714817525840,
 "Modified by": "7zBqhAKXxfQAPmH7Y",
 "First Name": "Sofia",
 "Last Name": "Young",
 "Company": "Kollwitz GmbH",
 "Postal Code": "12345",
 "City": "New Jersey",
 "Address": "11,South Commerce Street",
 "Email": "sofi@kollwitz-gmbh.com",
 "Customer No": "K0001",
 "Title": "2",
 "Invoices": [
  1,
  16,
  17
 ],
 "Photo": "shutterstock_307218608.jpg",
 "Category": "1",
 "Position": "Marketing head",
 "Checked": true,
 "ID": "A1",
 "id": "A1"
}

此外,物件還帶有一個 _node 鍵:也就是 Ninox 所儲存的記錄,其欄位以內部識別碼表示。

重要

視程式碼是在 Mac 應用程式還是在網頁上執行,ID 的大小寫可能不同。
為了因應這一點並避免不同平台之間發生衝突,ID 會以所有可能的大小寫形式重複出現。

在 JavaScript 中修改記錄,並不會更新 Ninox 中的記錄。

非同步執行

有時候,把 Ninox 程式碼與 JavaScript 的執行同步起來是很有用的。
舉例來說,使用計時器時,程式碼必須在計時器執行完畢之後才結束。
少了這道同步,JavaScript 執行之後的程式碼會立刻跑起來,可能造成非預期的行為。

在第一個例子中,「I don't know」這個值會立刻被回傳,而 alert 函式在問題還沒呈現給使用者之前就已經顯示了結果。

Ninox
var r:= #{:text
   setTimeout( () => {
      if (confirm("Are you ok ?"))
        return "of course";
      else
        return "Never !";
   }, 2000);
  return "I don't know";
}#;
alert( "Your answer is : "+r );

image.png

改用 callback 模式(或與之等效的 async)之後,Ninox 的 alert 函式只會在 JavaScript 程式碼呼叫回呼函式之後才執行,也就是在問題已經呈現給使用者之後。型別一律放在最前面::text:callback,絕不能只寫 :callback,否則它會被當成型別。

Ninox
var r:= #{:text:callback
   setTimeout( () => {
      if (confirm("Are you ok ?"))
         callback( "of course" );
      else
        callback( "Never !" );
   }, 2000);
   return "I don't know";
}#;
alert( "Your answer is : "+r );

Enregistrement de l’écran 2025-03-03 à 11.46.26.gif

呼叫函式

您可以從 JavaScript 呼叫 Ninox 函式:區塊會收到同一個公式中較早宣告的函式。它看不到全域程式碼中的函式(參見下文「呼叫全域程式碼中的函式」)。

Ninox
function display( message : text ) do
    alert( message );
end;
#{ display('hello world') }#;

image.png

若要取得函式的回傳值,同樣必須使用回呼模式(參見上文:非同步執行):

Ninox
function add( a : number, b : number ) do
    a + b;
end;

#{:number:callback
   add( 1, 2, (returnValue) => {
     callback( returnValue )
   })
}#;

image.png

如果呼叫該程序之後沒有其他要處理的事,您也可以直接把回呼函式當作參數傳入:

Ninox
function add( a : number, b : number ) do
    a + b;
end;
#{:number:callback
   add( 1, 2, callback )
}#;

image.png

呼叫全域程式碼中的函式

全域程式碼中的函式不在區塊的作用範圍內:在區塊中以名稱呼叫它會造成錯誤。請改以 ninext.callNinoxFunction 呼叫它,並以 null 作為第二個引數。若全域程式碼中有這個函式:

Ninox
function hello(message : text) do
    alert(message)
end;

公式便這樣呼叫它:

Ninox
#{ ninext.callNinoxFunction('hello', null, 'hello world') }#;

若要取得回傳值,請在最後一個引數傳入回呼函式,它會依序收到可能的錯誤與結果。以放在全域程式碼中的上述 add 函式為例:

Ninox
#{:number:callback
   ninext.callNinoxFunction('add', null, 1, 2, (error, result) => callback(result))
}#;

關於 callNinoxFunction 的一切:HTML 輔助函式。

操作記錄與函式

如果您有一份記錄 ID 的清單,可以呼叫 Ninox 函式逐一修改每筆記錄。做法很簡單:呼叫該函式並把 ID 傳進去即可。

Ninox
function setCustomerChecked( id : text, checked : boolean ) do
   var n := number(replacex(id, "\D",""));
   var rec := record( Customer, n );
   rec.Checked := checked;
end;
var c := select Customer;
var isChecked := not first(c).Checked;
#{
   c.forEach( v => { setCustomerChecked( v, isChecked ) } );
}#;
c.checked;

執行錯誤

如果您的程式碼發生執行錯誤,就會回傳錯誤訊息的文字:

image.png

在 callback 模式下,同樣的訊息會成為區塊的結果。上面的範例是語法錯誤:自 3.2.0 版起,錯誤分頁會在任何執行之前就將它標示出來(參見「Ninext 會檢查什麼」)。

啟動時:onNinextLoaded

Ninox 啟動時,「After Open」觸發器會在 Ninext 載入之前執行:此時原生 JS 尚無法運作,放在那裡的 JavaScript 區塊會悄悄地失敗。

若您在「After Open」觸發器中定義 onNinextLoaded(event) 函式,Ninext 會在真正載入完成後呼叫它——對每位使用者、在每次開啟資料庫時皆然。event.version 提供 Ninext 的版本:

Ninox
function onNinextLoaded(event : any) do
   alert("Ninext " + event.version + " is ready");
   "… your JavaScript / NativeJS code here";
end;

如此一來,您便有一個可靠的進入點,可在啟動時執行 JavaScript,或讓您的資料庫配合 Ninext 的版本加以調整。

須知

開啟資料庫時,Ninox 已經執行過「After Open」觸發器;為了呼叫 onNinextLoaded,Ninext 會重新求值其程式碼。自 3.2.0 版起,它只重新求值其中的宣告:各個函式;若有函式讀取了其中的變數(var、let),也包括這些變數。因此,其他指令只會執行一次。只有被函式讀取的變數,其初始值會被再次計算:請勿讓它依賴於寫入操作,例如建立記錄。在少數情況下,若這些宣告單獨無法編譯,Ninext 會像先前的版本一樣,重新求值整個觸發器。

在 3.1.2 版(含)之前,Ninext 會重新求值觸發器中的所有程式碼:放在任何函式之外的指令會被執行第二次。對於只應執行一次的指令,請將這點納入考量:建立記錄、顯示訊息、開啟表格。