Native JS

Native JS permet d'exécuter du code JavaScript à l'intérieur d'un script Ninox et de partager avec lui des fonctions et des variables. Native JS peut ainsi lire les variables Ninox et appeler les fonctions déclarées dans la même formule ; les fonctions du code global, elles, s'appellent par ninext.callNinoxFunction (voir plus bas, « Appeler une fonction du code global »).

Native JS est actif par défaut ; il se désactive dans les Paramètres Ninext, section « Intégration JavaScript » (JavaScript natif).

image.png

Limites

Modifier une variable Ninox dans NativeJS n'a aucun effet sur la variable Ninox.

Le JavaScript n'est exécuté que localement. Ajouter du JavaScript à du code qui s'exécute sur le serveur reste sans effet. De même, appeler depuis un code exécuté sur le serveur une fonction globale qui contient du JavaScript reste sans effet. À partir de la version 3.2.0, l'onglet Erreurs signale un bloc placé directement dans un déclencheur de table (après création, après modification) ou dans un do as server.

Voyez ici les codes qui s'exécutent localement et ceux qui s'exécutent sur le serveur :

image.png

Syntaxe

Exécution

Pour signaler à Ninox que vous passez au JavaScript, il faut entourer le code de #{ ... }# :

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

Rappelez-vous que Ninox est un langage précompilé, qui doit connaître à l'avance le type de chaque variable et de chaque fonction. Il en va de même pour Native JS : vous devez indiquer le type renvoyé par le code JavaScript. Par défaut, Native JS renvoie du texte, mais vous pouvez préciser le type de variable à renvoyer en l'indiquant tout au début du code, avec la notation :[type].

L'annotation se colle à #{, sans aucun espace : #{:number …, et non #{ :number …. De même, deux annotations se suivent sans espace (:text:callback). Avec un espace, l'annotation n'est pas reconnue : le bloc est typé texte, et l'annotation reste dans le code JavaScript, où elle provoque une erreur de syntaxe.

Valeur de retour

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

Les types possibles sont : text, number, date, timestamp (date et heure), time, timeinterval, appointment, boolean, html, color, icon, email, phone, location, file, user, json et any. La liste complète, avec choice, link, style et styled, est proposée par la Console (voir plus bas).

Attention

Une date avec heure s'annote :timestamp, alors qu'un paramètre de fonction du même type se déclare datetime. :datetime n'existe pas en annotation : le bloc serait typé texte. À partir de la version 3.2.0, l'onglet Erreurs signale une annotation inconnue.

Les dates, les heures et les durées s'échangent en millisecondes : une variable Ninox de ce type arrive dans le JavaScript sous forme de nombre, et un bloc :date ou :array(date) doit renvoyer des millisecondes (ce que donne getTime() d'une date JavaScript).

Types tableau

Pour renvoyer un tableau de valeurs, indiquez le type de ses éléments avec :array(type) (à partir de la version 3.2.0) :

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

Tous les types simples sont possibles : :array(text), :array(date), :array(any)…

Jusqu'à la version 3.1.2, seul un tableau de type any peut être renvoyé. Cette écriture reste valable, comme équivalent de :array(any) :

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

L'écriture :number(array), que présentait autrefois cette page, ne renvoie pas un tableau. Depuis la version 3.2.0, l'onglet Erreurs la signale.

Types enregistrement

Pour renvoyer des enregistrements, utilisez nid ou rid, suivi du nom de la table entre parenthèses :

Si vous utilisez nid, vous devez renvoyer un tableau de valeurs texte contenant l'identifiant complet de l'enregistrement, c'est-à-dire l'identifiant de la table + le numéro de l'enregistrement :

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

Si vous utilisez rid, vous devez renvoyer le numéro de l'enregistrement :

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

Pour les noms de tables composés, n'ajoutez pas de ' comme dans Ninox : utilisez simplement le nom complet de la table :

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

Avec cette syntaxe, Native JS renvoie des variables qui se comportent exactement comme du code Ninox. Vous pouvez donc employer la syntaxe suivante comme s'il s'agissait d'un select :

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

image.png

Aide à la saisie dans la Console

À partir de la version 3.2.0, la Console aide à écrire un bloc :

  • dans l'annotation : en tapant : juste après #{, elle propose les types de retour, array(type), nid(Table) et rid(Table) ; en tapant ( après nid ou rid, la liste des tables, et après array, celle des types ; un second : propose les modificateurs callback et async ;
  • dans le JavaScript : l'autocomplétion propose les variables et les fonctions Ninox que reçoit le bloc, sous leur forme JavaScript, puis, après un point, les membres de l'objet — pour une fiche, les noms de ses champs (voir « L'autocomplétion JavaScript dans les blocs natifs » dans la page Console).

Cette aide n'existe que dans la Console.

Ce que Ninext vérifie

À partir de la version 3.2.0 (bêta). L'onglet Erreurs contrôle les blocs #{ … }# de toute la base, et la Console ceux du code que vous y écrivez.

Sont signalés :

  • une faute de syntaxe JavaScript ;
  • une annotation inconnue (comme :datetime) ou une table introuvable dans :nid(…) ou :rid(…) : le bloc serait typé texte ;
  • un bloc typé sans aucun return, ou dont certains chemins n'en atteignent aucun : il renverrait une valeur vide ;
  • un bloc :callback ou :async qui n'appelle jamais callback : la formule n'obtiendrait jamais de valeur ;
  • un bloc placé directement dans un déclencheur de table (après création, après modification) ou dans un do as server, où il ne s'exécute pas.

Modification d'un enregistrement

Les variables de type enregistrement sont transmises au format JSON, avec une paire clé-valeur pour chaque champ de l'enregistrement.

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

Ici, le contenu de la variable r en JavaScript ressemble à ceci :

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'objet porte en outre une clé _node : l'enregistrement tel que Ninox le stocke, ses champs désignés par leurs identifiants internes.

Important

Selon que le code s'exécute dans l'application Mac ou sur le web, la casse de l'ID peut varier.
Pour parer à cela et éviter les conflits entre plateformes, l'ID est répété dans toutes les variantes de casse possibles.

Modifier l'enregistrement en JavaScript ne le met pas à jour dans Ninox.

Exécution asynchrone

Il est parfois utile de synchroniser l'exécution du code Ninox avec celle du JavaScript.
Par exemple, avec une minuterie, il importe que le code ne s'achève qu'après l'exécution de la minuterie.
Sans cette synchronisation, le code qui suit l'exécution du JavaScript se déroulerait immédiatement, avec des comportements inattendus à la clé.

Dans ce premier cas, la valeur « I don't know » est renvoyée immédiatement, et la fonction alert affiche le résultat avant même que la question ne soit posée à l'utilisateur.

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

En passant en mode callback (ou async, qui lui est équivalent), la fonction alert de Ninox ne s'exécute qu'après que le code JavaScript a appelé la fonction de rappel, c'est-à-dire après que la question a été posée à l'utilisateur. Le type vient toujours en premier : :text:callback, jamais :callback seul, qui serait pris pour un type.

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

Appel de fonction

Il est possible d'appeler des fonctions Ninox depuis le JavaScript : un bloc reçoit les fonctions déclarées plus haut dans la même formule. Il ne voit pas celles du code global (voir plus bas, « Appeler une fonction du code global »).

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

image.png

Pour récupérer la valeur de retour d'une fonction, il faut là encore passer par le mode callback (voir ci-dessus : Exécution asynchrone) :

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

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

image.png

Si vous n'avez aucun traitement à faire après l'appel de la procédure, vous pouvez passer directement la fonction de rappel en paramètre :

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

image.png

Appeler une fonction du code global

Une fonction du code global n'est pas dans la portée du bloc : l'appeler par son nom y provoque une erreur. Appelez-la par ninext.callNinoxFunction, avec null en deuxième argument. Avec cette fonction dans le code global :

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

une formule l'appelle ainsi :

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

Pour récupérer la valeur de retour, passez en dernier argument une fonction de rappel, qui reçoit l'erreur éventuelle puis le résultat. Avec la fonction add ci-dessus placée dans le code global :

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

Tout sur callNinoxFunction : Fonctions d'aide HTML.

Manipuler enregistrements et fonctions

Si vous disposez d'une liste d'identifiants d'enregistrements, vous pouvez appeler une fonction Ninox pour modifier chaque enregistrement l'un après l'autre. Il suffit d'appeler la fonction en lui passant l'identifiant.

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;

Erreurs d'exécution

Si une erreur d'exécution survient dans votre code, le texte du message d'erreur est renvoyé :

image.png

En mode callback, ce même message devient le résultat du bloc. L'exemple ci-dessus est une faute de syntaxe : à partir de la version 3.2.0, l'onglet Erreurs la signale avant toute exécution (voir « Ce que Ninext vérifie »).

Au démarrage : onNinextLoaded

Au lancement de Ninox, le déclencheur « After Open » s'exécute avant que Ninext ne soit chargé : Native JS n'y est pas encore opérationnel, et un bloc JavaScript placé là échoue sans rien dire.

Si vous définissez une fonction onNinextLoaded(event) dans le déclencheur « After Open », Ninext l'appelle une fois réellement chargé, chez chaque utilisateur, à chaque ouverture de la base. event.version donne la version de Ninext :

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

Vous disposez ainsi d'un point d'entrée fiable pour exécuter du JavaScript au démarrage, ou pour adapter votre base à la version de Ninext.

À savoir

Ninox a déjà exécuté le déclencheur « After Open » à l'ouverture de la base ; pour appeler onNinextLoaded, Ninext en évalue de nouveau le code. À partir de la version 3.2.0, il n'en reprend que les déclarations : les fonctions, et les variables (var, let) quand une fonction en lit une. Les autres instructions ne s'exécutent donc qu'une fois. Seule la valeur initiale d'une variable lue par une fonction est calculée une seconde fois : ne la faites pas dépendre d'une écriture, comme la création d'une fiche. Dans le cas rare où ces déclarations ne se compilent pas seules, Ninext reprend tout le déclencheur, comme dans les versions précédentes.

Jusqu'à la version 3.1.2, Ninext reprend tout le code du déclencheur : les instructions placées hors de toute fonction y sont exécutées une seconde fois. Tenez-en compte pour celles qui ne doivent s'exécuter qu'une fois : création d'une fiche, message, ouverture d'une table.