原生 JS

原生 JS 让您可以在 Ninox 脚本中执行 JavaScript 代码,并与之共享函数和变量。这样,原生 JS 就能读取 Ninox 变量,并调用在同一公式中声明的函数;而全局代码中的函数则要通过 ninext.callNinoxFunction 调用(参见下文“调用全局代码中的函数”)。

原生 JS 默认启用;可以在 Ninext 设置的“JavaScript 集成”部分中停用(原生 JavaScript)。

图片.png

限制

在原生 JS 中修改 Ninox 变量不会影响该 Ninox 变量。

JavaScript 只在本地执行。把 JavaScript 加入在服务器上运行的代码不会产生任何效果。同样地,从服务器上执行的代码中调用包含 JavaScript 的全局函数也不会产生任何效果。自 3.2.0 版起,错误标签页会标出直接放在表触发器(创建后、修改后)或 do as server 中的代码块。

关于哪些代码在本地执行、哪些在服务器上执行,请参见此处:

图片.png

语法

执行

要告诉 Ninox 您正在切换到 JavaScript,需要把代码包在 #{ ... }# 之中:

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

请记住,Ninox 是一种预编译语言,需要事先知道每个变量和函数的类型。原生 JS 也一样:您必须指定 JavaScript 代码所返回的类型。默认情况下,原生 JS 返回文本,但您可以在代码最开头用 :[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';

图片.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 或 :async 代码块从不调用 callback:公式将永远得不到值;
  • 直接放在表触发器(创建后、修改后)或 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 );

图片.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 );

屏幕录制 2025-03-03 11.46.26.gif

函数调用

可以从 JavaScript 调用 Ninox 函数:代码块会接收到同一公式中在它之前声明的函数。它看不到全局代码中的函数(参见下文“调用全局代码中的函数”)。

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

图片.png

要取回函数的返回值,同样必须使用回调模式(参见上文:异步执行):

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

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

图片.png

如果调用该过程之后不再需要做其他处理,您可以直接把回调函数作为参数传入:

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

图片.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;

执行错误

如果您的代码发生执行错误,将返回错误消息文本:

图片.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 会重新对触发器中的全部代码求值:放在任何函数之外的语句会被再次执行。对于只应执行一次的语句——创建记录、显示消息、打开表——请务必考虑到这一点。