JavaScript nativo

JavaScript nativo permite ejecutar código JavaScript dentro de un script de Ninox y compartir funciones y variables con él. Así, JavaScript nativo puede leer las variables de Ninox y llamar a las funciones declaradas en la misma fórmula; las funciones del código global, en cambio, se llaman mediante ninext.callNinoxFunction (véase más abajo, «Llamar a una función del código global»).

JavaScript nativo está activado por defecto; se desactiva en la Configuración de Ninext, sección «Integración JavaScript» (JavaScript nativo).

imagen.png

Limitaciones

Modificar una variable de Ninox en NativeJS no afecta a la variable de Ninox.

JavaScript solo se ejecuta localmente. Añadir JavaScript a código que se ejecuta en el servidor no tiene ningún efecto. Del mismo modo, llamar desde código ejecutado en el servidor a una función global que contiene JavaScript tampoco tiene efecto. A partir de la versión 3.2.0, la pestaña Errores señala un bloque colocado directamente en un trigger de tabla (después de crear, después de modificar) o en un do as server.

Vea aquí qué códigos se ejecutan localmente y cuáles en el servidor:

imagen.png

Sintaxis

Ejecución

Para indicar a Ninox que pasa a JavaScript, hay que encerrar el código entre #{ ... }#:

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

Recuerde que Ninox es un lenguaje precompilado que necesita conocer de antemano el tipo de cada variable y de cada función. Lo mismo ocurre con JavaScript nativo: debe indicar el tipo devuelto por el código JavaScript. De forma predeterminada, JavaScript nativo devuelve texto, pero puede indicar el tipo de variable que desea devolver especificándolo justo al principio del código con la notación :[type].

La anotación va pegada a #{, sin ningún espacio: #{:number …, y no #{ :number …. Del mismo modo, dos anotaciones se suceden sin espacio (:text:callback). Con un espacio, la anotación no se reconoce: el bloque queda tipado como texto, y la anotación permanece en el código JavaScript, donde provoca un error de sintaxis.

Valor devuelto

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

Los tipos posibles son: text, number, date, timestamp (fecha y hora), time, timeinterval, appointment, boolean, html, color, icon, email, phone, location, file, user, json y any. La lista completa, con choice, link, style y styled, la propone la Consola (véase más abajo).

Atención

Una fecha con hora se anota :timestamp, mientras que un parámetro de función del mismo tipo se declara datetime. :datetime no existe como anotación: el bloque quedaría tipado como texto. A partir de la versión 3.2.0, la pestaña Errores señala una anotación desconocida.

Las fechas, las horas y las duraciones se intercambian en milisegundos: una variable de Ninox de ese tipo llega al JavaScript en forma de número, y un bloque :date o :array(date) debe devolver milisegundos (lo que da getTime() de una fecha JavaScript).

Tipos de tabla (array)

Para devolver un array de valores, indique el tipo de sus elementos con :array(type) (a partir de la versión 3.2.0):

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

Todos los tipos simples son posibles: :array(text), :array(date), :array(any)…

Hasta la versión 3.1.2, solo se puede devolver un array de tipo any. Esta escritura sigue siendo válida, como equivalente de :array(any):

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

La escritura :number(array), que esta página presentaba antes, no devuelve un array. Desde la versión 3.2.0, la pestaña Errores la señala.

Tipos de registro

Para devolver registros, use nid o rid, seguido del nombre de la tabla entre paréntesis:

Si usa nid, debe devolver un array de valores de texto con el identificador completo del registro, que incluye el identificador de la tabla + el número de registro:

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

Si usa rid, debe devolver el número de registro:

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

Para nombres de tabla compuestos, no añada ' como en Ninox: use simplemente el nombre completo de la tabla:

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

Con esta sintaxis, JavaScript nativo devuelve variables que se comportan igual que el código de Ninox. Es decir, puede usar la sintaxis siguiente como si se tratara de un select:

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

imagen.png

Ayuda para escribir en la Consola

A partir de la versión 3.2.0, la Consola ayuda a escribir un bloque:

  • en la anotación: al teclear : justo después de #{, propone los tipos de retorno, array(type), nid(Table) y rid(Table); al teclear ( después de nid o rid, la lista de las tablas, y después de array, la de los tipos; un segundo : propone los modificadores callback y async;
  • en el JavaScript: el autocompletado propone las variables y las funciones de Ninox que recibe el bloque, en su forma JavaScript, y después, tras un punto, los miembros del objeto —para un registro, los nombres de sus campos— (véase «El autocompletado de JavaScript en los bloques nativos» en la página Consola).

Esta ayuda solo existe en la Consola.

Lo que Ninext verifica

A partir de la versión 3.2.0 (beta). La pestaña Errores controla los bloques #{ … }# de toda la base de datos, y la Consola los del código que usted escribe en ella.

Se señalan:

  • un error de sintaxis JavaScript;
  • una anotación desconocida (como :datetime) o una tabla no encontrada en :nid(…) o :rid(…): el bloque quedaría tipado como texto;
  • un bloque tipado sin ningún return, o en el que algunas rutas no llegan a ninguno: devolvería un valor vacío;
  • un bloque :callback o :async que nunca llama a callback: la fórmula nunca obtendría un valor;
  • un bloque colocado directamente en un trigger de tabla (después de crear, después de modificar) o en un do as server, donde no se ejecuta.

Modificación de registros

Las variables de tipo registro se pasan en formato JSON, con un par clave-valor por cada campo del registro.

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

Aquí, el contenido de la variable r en JavaScript es el siguiente:

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

El objeto lleva además una clave _node: el registro tal como lo almacena Ninox, con sus campos designados por sus identificadores internos.

Importante

Según se ejecute el código en la aplicación de Mac o en la web, el ID puede llevar mayúsculas o minúsculas distintas.
Para resolverlo y evitar conflictos entre plataformas, el ID se repite con todas las variantes posibles de mayúsculas y minúsculas.

Modificar el registro en JavaScript no lo actualiza en Ninox.

Ejecución asíncrona

A veces resulta útil sincronizar la ejecución del código de Ninox con la de JavaScript.
Por ejemplo, al usar un temporizador, es importante que el código solo termine después de que el temporizador haya terminado de ejecutarse.
Sin esa sincronización, el código posterior a la ejecución de JavaScript se ejecutaría de inmediato, lo que podría provocar comportamientos inesperados.

En este primer caso, el valor «I don't know» se devuelve de inmediato, y la función alert muestra el resultado antes de que la pregunta llegue siquiera a presentarse al usuario.

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

imagen.png

Al pasar al modo callback (o async, que es equivalente), la función alert de Ninox se ejecuta solo después de que el código JavaScript haya llamado a la función de retorno, es decir, después de haber presentado la pregunta al usuario. El tipo va siempre primero: :text:callback, nunca :callback solo, que se tomaría por un tipo.

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

Grabación de pantalla 2025-03-03 a las 11.46.26.gif

Llamada a funciones

Se puede llamar a funciones de Ninox desde JavaScript: un bloque recibe las funciones declaradas más arriba en la misma fórmula. No ve las del código global (véase más abajo, «Llamar a una función del código global»).

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

imagen.png

Para recuperar el valor devuelto por una función, hay que usar de nuevo el modo callback (véase más arriba: Ejecución asíncrona):

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

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

imagen.png

Si no tiene ningún tratamiento pendiente después de llamar al procedimiento, puede pasar directamente la función de retorno como parámetro:

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

imagen.png

Llamar a una función del código global

Una función del código global no está dentro del ámbito del bloque: llamarla por su nombre provoca un error. Llámela mediante ninext.callNinoxFunction, con null como segundo argumento. Con esta función en el código global:

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

una fórmula la llama así:

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

Para recuperar el valor devuelto, pase como último argumento una función de retorno, que recibe el posible error y después el resultado. Con la función add anterior colocada en el código global:

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

Todo sobre callNinoxFunction: Funciones auxiliares HTML.

Manipular registros y funciones

Si dispone de una lista de identificadores de registro, puede llamar a una función de Ninox para modificar cada registro uno a uno. Para ello, basta con llamar a la función pasándole el identificador.

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;

Errores de ejecución

Si se produce un error de ejecución en su código, se devolverá el texto del mensaje de error:

imagen.png

En modo callback, ese mismo mensaje se convierte en el resultado del bloque. El ejemplo anterior es un error de sintaxis: a partir de la versión 3.2.0, la pestaña Errores lo señala antes de cualquier ejecución (véase «Lo que Ninext verifica»).

Al inicio: onNinextLoaded

Al iniciar Ninox, el trigger «After Open» se ejecuta antes de que Ninext se cargue: JavaScript nativo todavía no funciona en él, y un bloque JavaScript colocado allí falla sin avisar.

Si define una función onNinextLoaded(event) en el trigger «After Open», Ninext la llama una vez cargado realmente, para cada usuario, cada vez que se abre la base de datos. event.version indica la versión de Ninext:

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

Así dispone de un punto de entrada fiable para ejecutar JavaScript al inicio, o para adaptar su base de datos a la versión de Ninext.

A tener en cuenta

Ninox ya ha ejecutado el trigger «After Open» al abrir la base de datos; para llamar a onNinextLoaded, Ninext vuelve a evaluar su código. A partir de la versión 3.2.0, solo vuelve a evaluar sus declaraciones: las funciones y, cuando una función lee alguna, las variables (var, let). Por tanto, las demás instrucciones solo se ejecutan una vez. Únicamente el valor inicial de una variable leída por una función se calcula una segunda vez: no lo haga depender de una escritura, como la creación de un registro. En el caso poco frecuente de que esas declaraciones no compilen por sí solas, Ninext vuelve a evaluar todo el trigger, como en las versiones anteriores.

Hasta la versión 3.1.2, Ninext vuelve a evaluar todo el código del trigger: las instrucciones situadas fuera de cualquier función se ejecutan una segunda vez. Téngalo en cuenta para las que solo deben ejecutarse una vez: creación de un registro, mensaje, apertura de una tabla.