Native JS

Native JS us permet executar codi JavaScript dins d'un script de Ninox i compartir-hi funcions i variables. D'aquesta manera, Native JS pot llegir les variables de Ninox i cridar les funcions declarades a la mateixa fórmula; les funcions del codi global, en canvi, es criden amb ninext.callNinoxFunction (vegeu més avall, «Cridar una funció del codi global»).

Native JS està actiu per defecte; es desactiva a la Configuració de Ninext, secció «Integració JavaScript» (JavaScript natiu).

imatge.png

Limitacions

Modificar una variable de Ninox dins de NativeJS no afecta la variable de Ninox.

El JavaScript només s'executa localment. Afegir JavaScript a codi que s'executa al servidor no té cap efecte. Igualment, cridar una funció global que conté JavaScript des de codi executat al servidor no té cap efecte. A partir de la versió 3.2.0, la pestanya Errors assenyala un bloc situat directament en un activador de taula (després de crear, després de modificar) o en un do as server.

Consulteu aquí quins codis s'executen localment i quins al servidor:

imatge.png

Sintaxi

Execució

Per indicar a Ninox que passeu a JavaScript, cal que envolteu el codi amb #{ ... }#:

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

Recordeu que Ninox és un llenguatge precompilat que necessita conèixer per endavant el tipus de cada variable i de cada funció. El mateix passa amb Native JS: heu d'especificar el tipus retornat pel codi JavaScript. Per defecte, Native JS retorna text, però podeu indicar el tipus de variable que voleu retornar especificant-lo al començament mateix del codi amb la notació :[type].:

L'anotació s'enganxa a #{, sense cap espai: #{:number …, i no #{ :number …. De la mateixa manera, dues anotacions se succeeixen sense espai (:text:callback). Amb un espai, l'anotació no es reconeix: el bloc es tipa com a text, i l'anotació es queda al codi JavaScript, on provoca un error de sintaxi.

Valor retornat

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

Els tipus possibles són: text, number, date, timestamp (data i hora), time, timeinterval, appointment, boolean, html, color, icon, email, phone, location, file, user, json i any. La llista completa, amb choice, link, style i styled, la proposa la Consola (vegeu més avall).

Atenció

Una data amb hora s'anota :timestamp, mentre que un paràmetre de funció del mateix tipus es declara datetime. :datetime no existeix com a anotació: el bloc es tiparia com a text. A partir de la versió 3.2.0, la pestanya Errors assenyala una anotació desconeguda.

Les dates, les hores i les durades s'intercanvien en mil·lisegons: una variable de Ninox d'aquest tipus arriba al JavaScript en forma de nombre, i un bloc :date o :array(date) ha de retornar mil·lisegons (el que dona getTime() d'una data JavaScript).

Tipus de taula (array)

Per retornar una taula de valors, indiqueu el tipus dels seus elements amb :array(type) (a partir de la versió 3.2.0):

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

Es poden fer servir tots els tipus simples: :array(text), :array(date), :array(any)…

Fins a la versió 3.1.2, només es pot retornar una taula de tipus any. Aquesta escriptura continua sent vàlida, com a equivalent de :array(any):

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

L'escriptura :number(array), que aquesta pàgina presentava abans, no retorna una taula. Des de la versió 3.2.0, la pestanya Errors l'assenyala.

Tipus de registre

Per retornar registres, feu servir nid o rid, seguit del nom de la taula entre parèntesis:

Si feu servir nid, heu de retornar una taula de valors de text que contingui l'identificador complet del registre, és a dir, l'identificador de la taula + el número del registre:

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

Si feu servir rid, cal que retorneu el número del registre:

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

Per als noms de taula compostos, no hi afegiu ' com a Ninox, sinó que feu servir simplement el nom complet de la taula:

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

Amb aquesta sintaxi, Native JS retorna variables que es comporten igual que el codi de Ninox. Això vol dir que podeu fer servir la sintaxi següent com si fos un select:

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

imatge.png

Ajuda per escriure a la Consola

A partir de la versió 3.2.0, la Consola ajuda a escriure un bloc:

  • a l'anotació: en teclejar : just després de #{, proposa els tipus de retorn, array(type), nid(Table) i rid(Table); en teclejar ( després de nid o rid, la llista de taules, i després d'array, la dels tipus; un segon : proposa els modificadors callback i async;
  • al JavaScript: l'autocompletació proposa les variables i les funcions de Ninox que rep el bloc, en la seva forma JavaScript i, després d'un punt, els membres de l'objecte — per a un registre, els noms dels seus camps (vegeu «L'autocompletació JavaScript als blocs natius» a la pàgina Consola).

Aquesta ajuda només existeix a la Consola.

Què verifica Ninext

A partir de la versió 3.2.0 (beta). La pestanya Errors controla els blocs #{ … }# de tota la base de dades, i la Consola els del codi que hi escriviu.

S'assenyalen:

  • un error de sintaxi JavaScript;
  • una anotació desconeguda (com ara :datetime) o una taula que no es troba a :nid(…) o :rid(…): el bloc es tiparia com a text;
  • un bloc tipat sense cap return, o en què alguns camins no n'arriben a cap: retornaria un valor buit;
  • un bloc :callback o :async que no crida mai callback: la fórmula no obtindria mai cap valor;
  • un bloc situat directament en un activador de taula (després de crear, després de modificar) o en un do as server, on no s'executa.

Modificació de registres

Les variables de tipus registre es passen en format JSON, amb una parella clau-valor per a cada camp del registre.

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

Aquí, el contingut de la variable r en JavaScript té aquest aspecte:

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

L'objecte porta, a més, una clau _node: el registre tal com el desa Ninox, amb els camps designats pels seus identificadors interns.

Important

Segons si el codi s'executa a l'aplicació per a Mac o al web, l'ID pot tenir majúscules i minúscules diferents.
Per resoldre-ho i evitar conflictes entre plataformes, l'ID es repeteix amb totes les combinacions possibles de majúscules i minúscules.

Modificar el registre en JavaScript no l'actualitza a Ninox.

Execució asíncrona

De vegades és útil sincronitzar l'execució del codi de Ninox amb la del JavaScript.
Per exemple, quan es fa servir un temporitzador, és important que el codi només acabi després que el temporitzador hagi acabat d'executar-se.
Sense aquesta sincronització, el codi posterior a l'execució del JavaScript s'executaria immediatament, cosa que podria provocar comportaments inesperats.

En aquest primer cas, el valor «I don't know» es retorna immediatament, i la funció alert mostra el resultat abans que la pregunta arribi a plantejar-se a l'usuari.

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

imatge.png

En passar al mode callback (o async, que hi és equivalent), la funció alert de Ninox s'executa només després que el codi JavaScript hagi cridat la funció de retorn, és a dir, després que la pregunta s'hagi plantejat a l'usuari. El tipus sempre va primer: :text:callback, mai :callback sol, que es prendria per un tipus.

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

Crida de funcions

Es poden cridar funcions de Ninox des de JavaScript: un bloc rep les funcions declarades més amunt a la mateixa fórmula. No veu les del codi global (vegeu més avall, «Cridar una funció del codi global»).

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

imatge.png

Per recuperar el valor retornat d'una funció, heu de tornar a fer servir el mode de funció de retorn (vegeu més amunt: Execució asíncrona):

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

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

imatge.png

Si no heu de fer cap altre tractament després de cridar el procediment, podeu passar directament la funció de retorn com a paràmetre:

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

imatge.png

Cridar una funció del codi global

Una funció del codi global no és dins de l'abast del bloc: cridar-la pel seu nom hi provoca un error. Crideu-la amb ninext.callNinoxFunction, amb null com a segon argument. Amb aquesta funció al codi global:

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

una fórmula la crida així:

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

Per recuperar el valor retornat, passeu com a darrer argument una funció de retorn, que rep l'error eventual i després el resultat. Amb la funció add de més amunt situada al codi global:

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

Tot sobre callNinoxFunction: Funcions d'ajuda HTML.

Manipular registres i funcions

Si teniu una llista d'identificadors de registre, podeu cridar una funció de Ninox per modificar cada registre un per un. Per fer-ho, només cal cridar la funció i passar-li l'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;

Errors d'execució

Si es produeix un error d'execució al vostre codi, es retornarà el text del missatge d'error:

imatge.png

En mode callback, aquest mateix missatge es converteix en el resultat del bloc. L'exemple de més amunt és un error de sintaxi: a partir de la versió 3.2.0, la pestanya Errors l'assenyala abans de cap execució (vegeu «Què verifica Ninext»).

A l'inici: onNinextLoaded

En iniciar Ninox, l'activador «After Open» s'executa abans que Ninext s'hagi carregat: Native JS encara no hi és operatiu, i un bloc JavaScript situat allà falla sense dir res.

Si definiu una funció onNinextLoaded(event) a l'activador «After Open», Ninext la crida un cop s'ha carregat realment, per a cada usuari, cada vegada que s'obre la base de dades. event.version dona la versió de Ninext:

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

Així disposeu d'un punt d'entrada fiable per executar JavaScript a l'inici, o per adaptar la vostra base de dades a la versió de Ninext.

Cal saber

Ninox ja ha executat l'activador «After Open» en obrir la base de dades; per cridar onNinextLoaded, Ninext en torna a avaluar el codi. A partir de la versió 3.2.0, només en torna a avaluar les declaracions: les funcions i, quan una funció en llegeix alguna, les variables (var, let). Per tant, les altres instruccions només s'executen una vegada. Només el valor inicial d'una variable llegida per una funció es calcula una segona vegada: no el feu dependre d'una escriptura, com ara la creació d'un registre. En el cas poc freqüent que aquestes declaracions no es compilin soles, Ninext torna a avaluar tot l'activador, com en les versions anteriors.

Fins a la versió 3.1.2, Ninext torna a avaluar tot el codi de l'activador: les instruccions situades fora de qualsevol funció s'hi executen una segona vegada. Tingueu-ho en compte per a les que només s'han d'executar una vegada: creació d'un registre, missatge, obertura d'una taula.