Native JS
Native JS позволяет выполнять код JavaScript внутри скрипта Ninox и обмениваться с ним функциями и переменными. Благодаря этому Native JS может читать переменные Ninox и вызывать функции, объявленные в той же формуле; функции глобального кода вызываются через ninext.callNinoxFunction (см. ниже «Вызов функции из глобального кода»).
Native JS включён по умолчанию; он отключается в Настройках Ninext, раздел «Интеграция JavaScript» (Нативный JavaScript).

Ограничения
Изменение переменной Ninox в NativeJS не затрагивает саму переменную Ninox.
JavaScript выполняется только локально. Добавление JavaScript в код, выполняемый на сервере, не даёт никакого эффекта. Точно так же не даёт эффекта вызов глобальной функции с JavaScript из кода, выполняемого на сервере. Начиная с версии 3.2.0 вкладка Ошибки сообщает о блоке, помещённом непосредственно в триггер таблицы (после создания, после изменения) или в do as server.
Здесь показано, какие коды выполняются локально, а какие на сервере:

Синтаксис
Выполнение
Чтобы указать Ninox, что вы переходите на JavaScript, заключите код в #{ ... }#:
#{ alert("hello world") }#;Помните: Ninox — предварительно компилируемый язык, которому нужно заранее знать тип каждой переменной и функции. То же относится и к Native JS: вы должны указать тип, возвращаемый кодом JavaScript. По умолчанию Native JS возвращает текст, но нужный тип можно задать в самом начале кода записью :[type]:
Аннотация пишется вплотную к #{, без пробела: #{:number …, а не #{ :number …. Точно так же две аннотации следуют друг за другом без пробела (:text:callback). С пробелом аннотация не распознаётся: блок получает тип «текст», а аннотация остаётся в коде JavaScript, где вызывает синтаксическую ошибку.
Возвращаемое значение
#{: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) должен возвращать миллисекунды (их даёт getTime() даты JavaScript).
Типы массивов
Чтобы вернуть массив значений, укажите тип его элементов с помощью :array(type) (начиная с версии 3.2.0):
#{:array(number) return [1, 2, 3] }#;Допустимы все простые типы: :array(text), :array(date), :array(any)…
До версии 3.1.2 включительно можно вернуть только массив типа any. Такая запись по-прежнему допустима как эквивалент :array(any):
#{:any(array) return [1, 2, 3] }#;Запись :number(array), которую раньше приводила эта страница, не возвращает массив. Начиная с версии 3.2.0 вкладка «Ошибки» сообщает о ней.
Типы записей
Чтобы вернуть записи, укажите nid или rid, а за ним имя таблицы в скобках:
Если вы используете nid, нужно вернуть массив текстовых значений с полным идентификатором записи, то есть идентификатором таблицы + номером записи:
#{:nid(customer) return ['A1','A2','A3'] }#Если вы используете rid, следует вернуть номер записи:
#{:rid(customer) return [1,2,3] }#Для составных имён таблиц не добавляйте ', как в Ninox, а просто пишите полное имя таблицы:
#{:rid(my long name table) return [1,2,3] }#При таком синтаксисе Native JS возвращает переменные, которые ведут себя так же, как в коде Ninox. Значит, можно писать так, будто это select:
(#{:rid(customer) return [1,2,3] }# order by 'Last Name').'First Name';
Помощь при вводе в Консоли
Начиная с версии 3.2.0 Консоль помогает писать блок:
- в аннотации: когда вы вводите
:сразу после#{, она предлагает возвращаемые типы,array(type),nid(Table)иrid(Table); когда вводите(послеnidилиrid— список таблиц, а послеarray— список типов; второе:предлагает модификаторыcallbackиasync; - в JavaScript: автодополнение предлагает переменные и функции Ninox, которые получает блок, в их JavaScript-виде, а после точки — члены объекта; для записи — имена её полей (см. «Автодополнение JavaScript в нативных блоках» на странице Консоль).
Эта помощь есть только в Консоли.
Что проверяет Ninext
Начиная с версии 3.2.0 (бета). Вкладка Ошибки проверяет блоки
#{ … }#во всей базе, а Консоль — в коде, который вы в ней пишете.
Сообщается о следующем:
- синтаксическая ошибка JavaScript;
- неизвестная аннотация (например,
:datetime) или таблица, не найденная в:nid(…)или:rid(…): блок получил бы тип «текст»; - типизированный блок без единого
returnили с путями, на которых он не достигается: он вернул бы пустое значение; - блок
:callbackили:async, который никогда не вызываетcallback: формула так и не получила бы значения; - блок, помещённый непосредственно в триггер таблицы (после создания, после изменения) или в
do as server, где он не выполняется.
Изменение записи
Переменные типа «запись» передаются в формате JSON, где каждому полю записи соответствует пара ключ-значение.
var r := first(select Customer);
#{:text return r['First Name'] }#;Здесь содержимое переменной r в JavaScript выглядит так:
{
"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 показывает результат раньше, чем вопрос будет задан пользователю.
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 );
Если перейти в режим callback (или равнозначный ему async), функция alert в Ninox выполнится только после того, как код JavaScript вызовет функцию обратного вызова, то есть после того, как вопрос будет задан пользователю. Тип всегда идёт первым: :text:callback, но никогда не :callback отдельно — это было бы принято за тип.
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 );
Вызов функции
Из JavaScript можно вызывать функции Ninox: блок получает функции, объявленные выше в той же формуле. Функций глобального кода он не видит (см. ниже «Вызов функции из глобального кода»).
function display( message : text ) do
alert( message );
end;
#{ display('hello world') }#;
Чтобы получить возвращаемое значение функции, снова понадобится режим callback (см. выше: Асинхронное выполнение):
function add( a : number, b : number ) do
a + b;
end;
#{:number:callback
add( 1, 2, (returnValue) => {
callback( returnValue )
})
}#;
Если после вызова процедуры никакой дальнейшей обработки не требуется, функцию обратного вызова можно передать параметром напрямую:
function add( a : number, b : number ) do
a + b;
end;
#{:number:callback
add( 1, 2, callback )
}#;
Вызов функции из глобального кода
Функция глобального кода не входит в область видимости блока: вызов по имени приводит к ошибке. Вызывайте её через ninext.callNinoxFunction с null во втором аргументе. Если в глобальном коде есть такая функция:
function hello(message : text) do
alert(message)
end;формула вызывает её так:
#{ ninext.callNinoxFunction('hello', null, 'hello world') }#;Чтобы получить возвращаемое значение, передайте последним аргументом функцию обратного вызова, которая получает возможную ошибку, а затем результат. Если функция add, приведённая выше, находится в глобальном коде:
#{:number:callback
ninext.callNinoxFunction('add', null, 1, 2, (error, result) => callback(result))
}#;Всё о callNinoxFunction: Вспомогательные функции HTML.
Работа с записями и функциями
Если у вас есть список идентификаторов записей, можно вызвать функцию 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;Ошибки выполнения
Если в коде происходит ошибка выполнения, возвращается текст сообщения об ошибке:

В режиме callback это же сообщение становится результатом блока. Пример выше — синтаксическая ошибка: начиная с версии 3.2.0 вкладка «Ошибки» сообщает о ней ещё до выполнения (см. «Что проверяет Ninext»).
При запуске: onNinextLoaded
При запуске Ninox триггер «After Open» выполняется до того, как загрузится Ninext: Native JS там ещё не работает, и помещённый туда блок JavaScript молча завершается ошибкой.
Если определить функцию onNinextLoaded(event) в триггере «After Open», Ninext вызовет её, когда действительно загрузится, — у каждого пользователя, при каждом открытии базы. event.version содержит версию Ninext:
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 заново вычисляет весь код триггера: инструкции, находящиеся вне каких-либо функций, выполняются второй раз. Учитывайте это для тех, что должны выполняться только один раз: создание записи, сообщение, открытие таблицы.
