JS nativo

Native JS permette di eseguire codice JavaScript all'interno di uno script Ninox e di condividere con esso funzioni e variabili. In questo modo Native JS può leggere le variabili Ninox e chiamare le funzioni dichiarate nella stessa formula; le funzioni del codice globale, invece, si chiamano con ninext.callNinoxFunction (vedere più avanti, «Chiamare una funzione del codice globale»).

Native JS è attivo per impostazione predefinita; si disattiva nelle Impostazioni Ninext, sezione «Integrazione JavaScript» (JavaScript nativo).

image.png

Limiti

Modificare una variabile Ninox in NativeJS non ha effetto sulla variabile Ninox.

Il JavaScript viene eseguito solo in locale. Aggiungere JavaScript a codice che gira sul server non ha alcun effetto. Allo stesso modo, chiamare una funzione globale che contiene JavaScript da codice eseguito sul server non ha alcun effetto. A partire dalla versione 3.2.0, la scheda Errori segnala un blocco posto direttamente in un trigger di tabella (dopo la creazione, dopo la modifica) o in un do as server.

Qui sotto trovate quali codici vengono eseguiti in locale e quali sul server :

image.png

Sintassi

Esecuzione

Per indicare a Ninox che si passa a JavaScript, occorre racchiudere il codice tra #{ ... }#.:

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

Ricordate che Ninox è un linguaggio precompilato, che deve conoscere in anticipo il tipo di ogni variabile e di ogni funzione. Lo stesso vale per Native JS: dovete indicare il tipo restituito dal codice JavaScript. Per impostazione predefinita Native JS restituisce del testo, ma potete indicare il tipo di variabile da restituire specificandolo all'inizio del codice con la notazione :[type]. :

L'annotazione si attacca a #{, senza alcuno spazio: #{:number …, e non #{ :number …. Allo stesso modo, due annotazioni si susseguono senza spazio (:text:callback). Con uno spazio, l'annotazione non viene riconosciuta: il blocco è tipizzato come testo, e l'annotazione resta nel codice JavaScript, dove provoca un errore di sintassi.

Valore restituito

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

I tipi possibili sono: text, number, date, timestamp (data e ora), time, timeinterval, appointment, boolean, html, color, icon, email, phone, location, file, user, json e any. L'elenco completo, con choice, link, style e styled, è proposto dalla Console (vedere più avanti).

Attenzione

Una data con ora si annota :timestamp, mentre un parametro di funzione dello stesso tipo si dichiara datetime. :datetime non esiste come annotazione: il blocco sarebbe tipizzato come testo. A partire dalla versione 3.2.0, la scheda Errori segnala un'annotazione sconosciuta.

Date, ore e durate si scambiano in millisecondi: una variabile Ninox di questo tipo arriva nel JavaScript sotto forma di numero, e un blocco :date o :array(date) deve restituire dei millisecondi (ciò che dà getTime() di una data JavaScript).

Tipi array

Per restituire un array di valori, indicate il tipo dei suoi elementi con :array(type) (a partire dalla versione 3.2.0):

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

Sono possibili tutti i tipi semplici: :array(text), :array(date), :array(any)…

Fino alla versione 3.1.2 si può restituire solo un array di tipo any. Questa scrittura resta valida, come equivalente di :array(any):

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

La scrittura :number(array), presentata un tempo in questa pagina, non restituisce un array. Dalla versione 3.2.0 la scheda Errori la segnala.

Tipi record

Per restituire dei record, usate nid o rid, seguito dal nome della tabella tra parentesi:

Se usate nid, dovete restituire un array di valori di testo contenenti l'identificatore completo del record, cioè l'identificatore della tabella + il numero del record:

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

Se usate rid, dovete restituire il numero del record :

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

Per i nomi di tabella composti, non aggiungete ' come in Ninox, ma usate semplicemente il nome completo della tabella :

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

Con questa sintassi, Native JS restituisce variabili che si comportano come nel codice Ninox. Significa che potete usare la sintassi seguente come se fosse un select :

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

image.png

Aiuto alla digitazione nella Console

A partire dalla versione 3.2.0, la Console aiuta a scrivere un blocco:

  • nell'annotazione: digitando : subito dopo #{, propone i tipi di ritorno, array(type), nid(Table) e rid(Table); digitando ( dopo nid o rid, l'elenco delle tabelle, e dopo array, quello dei tipi; un secondo : propone i modificatori callback e async;
  • nel JavaScript: il completamento automatico propone le variabili e le funzioni Ninox che il blocco riceve, nella loro forma JavaScript, poi, dopo un punto, i membri dell'oggetto — per un record, i nomi dei suoi campi (vedere «Il completamento automatico JavaScript nei blocchi nativi» nella pagina Console).

Questo aiuto esiste solo nella Console.

Ciò che Ninext verifica

A partire dalla versione 3.2.0 (beta). La scheda Errori controlla i blocchi #{ … }# di tutto il database, e la Console quelli del codice che vi scrivete.

Vengono segnalati:

  • un errore di sintassi JavaScript;
  • un'annotazione sconosciuta (come :datetime) o una tabella introvabile in :nid(…) o :rid(…): il blocco sarebbe tipizzato come testo;
  • un blocco tipizzato senza alcun return, o di cui alcuni percorsi non ne raggiungono nessuno: restituirebbe un valore vuoto;
  • un blocco :callback o :async che non chiama mai callback: la formula non otterrebbe mai un valore;
  • un blocco posto direttamente in un trigger di tabella (dopo la creazione, dopo la modifica) o in un do as server, dove non viene eseguito.

Modifica di un record

Le variabili di tipo record vengono passate in formato JSON, con una coppia chiave-valore per ogni campo del record.

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

Qui il contenuto della variabile r in JavaScript si presenta così :

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'oggetto porta inoltre una chiave _node: il record così come Ninox lo memorizza, con i campi designati dai loro identificatori interni.

Importante

A seconda che il codice venga eseguito sull'applicazione Mac o sul web, l'ID può avere maiuscole e minuscole diverse.
Per ovviare a questo ed evitare conflitti tra piattaforme diverse, l'ID è ripetuto in tutte le varianti possibili di maiuscole e minuscole.

Modificare il record in JavaScript non lo aggiorna in Ninox.

Esecuzione asincrona

A volte è utile sincronizzare l'esecuzione del codice Ninox con il JavaScript.
Per esempio, quando si usa un timer, è importante che il codice si concluda solo dopo che il timer ha terminato l'esecuzione.
Senza questa sincronizzazione, il codice che segue l'esecuzione JavaScript verrebbe eseguito immediatamente, con possibili comportamenti inattesi.

In questo primo caso il valore «I don't know» viene restituito subito e la funzione alert mostra il risultato prima ancora che la domanda venga presentata all'utente.

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

Passando alla modalità callback (o async, che le è equivalente), la funzione alert di Ninox viene eseguita solo dopo che il codice JavaScript ha chiamato la funzione di callback, cioè dopo che la domanda è stata presentata all'utente. Il tipo viene sempre per primo: :text:callback, mai :callback da solo, che verrebbe preso per 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 );

Registrazione dello schermo 2025-03-03 alle 11.46.26.gif

Chiamata di funzione

È possibile chiamare funzioni Ninox dal JavaScript: un blocco riceve le funzioni dichiarate più in alto nella stessa formula. Non vede quelle del codice globale (vedere più avanti, «Chiamare una funzione del codice globale»).

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

image.png

Per recuperare il valore restituito da una funzione, occorre di nuovo usare la modalità callback (vedere sopra: Esecuzione asincrona):

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

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

image.png

Se non avete altre elaborazioni da fare dopo la chiamata alla procedura, potete passare direttamente la funzione di callback come parametro:

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

image.png

Chiamare una funzione del codice globale

Una funzione del codice globale non rientra nell'ambito di visibilità del blocco: chiamarla per nome vi provoca un errore. Chiamatela con ninext.callNinoxFunction, con null come secondo argomento. Con questa funzione nel codice globale:

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

una formula la chiama così:

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

Per recuperare il valore restituito, passate come ultimo argomento una funzione di callback, che riceve l'eventuale errore e poi il risultato. Con la funzione add qui sopra posta nel codice globale:

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

Tutto su callNinoxFunction: Funzioni di aiuto HTML.

Manipolare record e funzioni

Se disponete di un elenco di ID di record, potete chiamare una funzione Ninox per modificare ogni record uno per uno. Basta chiamare la funzione passandole l'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;

Errori di esecuzione

Se nel vostro codice si verifica un errore di esecuzione, viene restituito il testo del messaggio di errore:

image.png

In modalità callback, questo stesso messaggio diventa il risultato del blocco. L'esempio qui sopra è un errore di sintassi: a partire dalla versione 3.2.0, la scheda Errori lo segnala prima di qualsiasi esecuzione (vedere «Ciò che Ninext verifica»).

All'avvio: onNinextLoaded

All'avvio di Ninox, il trigger «After Open» viene eseguito prima che Ninext sia caricato: lì Native JS non è ancora operativo, e un blocco JavaScript posto in quel punto fallisce senza dire nulla.

Se definite una funzione onNinextLoaded(event) nel trigger «After Open», Ninext la chiama una volta effettivamente caricato, per ogni utente, a ogni apertura del database. event.version fornisce la versione di Ninext:

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

Disponete così di un punto d'ingresso affidabile per eseguire JavaScript all'avvio, o per adattare il vostro database alla versione di Ninext.

Da sapere

Ninox ha già eseguito il trigger «After Open» all'apertura del database; per chiamare onNinextLoaded, Ninext ne valuta di nuovo il codice. A partire dalla versione 3.2.0, ne valuta di nuovo solo le dichiarazioni: le funzioni e, quando una funzione ne legge una, le variabili (var, let). Le altre istruzioni vengono quindi eseguite una sola volta. Solo il valore iniziale di una variabile letta da una funzione viene calcolato una seconda volta: non fatelo dipendere da una scrittura, come la creazione di un record. Nel raro caso in cui queste dichiarazioni non si compilino da sole, Ninext valuta di nuovo tutto il trigger, come nelle versioni precedenti.

Fino alla versione 3.1.2, Ninext valuta di nuovo tutto il codice del trigger: le istruzioni poste al di fuori di qualsiasi funzione vengono eseguite una seconda volta. Tenetene conto per quelle che devono essere eseguite una sola volta: creazione di un record, messaggio, apertura di una tabella.