Natywny JS

Natywny JS pozwala wykonywać kod JavaScript wewnątrz skryptu Ninox oraz dzielić z nim funkcje i zmienne. Dzięki temu natywny JS może odczytywać zmienne Ninox i wywoływać funkcje zadeklarowane w tej samej formule; funkcje kodu globalnego wywołuje się natomiast przez ninext.callNinoxFunction (zob. niżej, „Wywołanie funkcji kodu globalnego”).

Natywny JS jest domyślnie aktywny; wyłącza się go w panelu Ustawienia Ninext, w sekcji „Integracja JavaScript” (Natywny JavaScript).

obraz.png

Ograniczenia

Zmiana zmiennej Ninox w natywnym JS nie wpływa na tę zmienną w Ninox.

JavaScript wykonuje się wyłącznie lokalnie. Dodanie JavaScriptu do kodu uruchamianego na serwerze nie daje żadnego efektu. Podobnie wywołanie funkcji globalnej zawierającej JavaScript z kodu wykonywanego na serwerze nie daje żadnego efektu. Od wersji 3.2.0 karta Błędy sygnalizuje blok umieszczony bezpośrednio w wyzwalaczu tabeli (po utworzeniu, po modyfikacji) lub w do as server.

Tutaj widać, które fragmenty kodu wykonują się lokalnie, a które na serwerze:

obraz.png

Składnia

Wykonanie

Aby wskazać Ninox, że przechodzą Państwo na JavaScript, należy zamknąć kod w #{ ... }#:

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

Proszę pamiętać, że Ninox jest językiem prekompilowanym, który musi z góry znać typ każdej zmiennej i funkcji. To samo dotyczy natywnego JS: trzeba określić typ zwracany przez kod JavaScript. Domyślnie natywny JS zwraca tekst, ale mogą Państwo wskazać typ zwracanej zmiennej, podając go na samym początku kodu w notacji :[typ].

Adnotacja przylega do #{ bez żadnej spacji: #{:number …, a nie #{ :number …. Podobnie dwie adnotacje następują po sobie bez spacji (:text:callback). Ze spacją adnotacja nie zostaje rozpoznana: blok otrzymuje typ tekstowy, a adnotacja pozostaje w kodzie JavaScript, gdzie powoduje błąd składni.

Wartość zwracana

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

Możliwe typy to: text, number, date, timestamp (data i godzina), time, timeinterval, appointment, boolean, html, color, icon, email, phone, location, file, user, json oraz any. Pełną listę, z choice, link, style i styled, proponuje Konsola (zob. niżej).

Uwaga

Datę z godziną opisuje się adnotacją :timestamp, natomiast parametr funkcji tego samego typu deklaruje się jako datetime. :datetime nie istnieje jako adnotacja: blok otrzymałby typ tekstowy. Od wersji 3.2.0 karta Błędy sygnalizuje nieznaną adnotację.

Daty, godziny i czasy trwania są przekazywane w milisekundach: zmienna Ninox tego typu trafia do JavaScriptu jako liczba, a blok :date lub :array(date) musi zwrócić milisekundy (to, co daje getTime() daty JavaScript).

Typy tablicowe

Aby zwrócić tablicę wartości, należy wskazać typ jej elementów za pomocą :array(type) (od wersji 3.2.0):

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

Możliwe są wszystkie typy proste: :array(text), :array(date), :array(any)…

Do wersji 3.1.2 włącznie można zwrócić wyłącznie tablicę typu any. Ten zapis pozostaje prawidłowy jako odpowiednik :array(any):

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

Zapis :number(array), przedstawiany dawniej na tej stronie, nie zwraca tablicy. Od wersji 3.2.0 sygnalizuje go karta Błędy.

Typy rekordowe

Aby zwrócić rekordy, należy użyć nid lub rid, po którym następuje nazwa tabeli w nawiasach:

Jeśli używają Państwo nid, trzeba zwrócić tablicę wartości tekstowych zawierających pełny identyfikator rekordu, czyli identyfikator tabeli + numer rekordu:

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

Jeśli używają Państwo rid, należy zwrócić numer rekordu:

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

W przypadku nazw tabel złożonych z kilku słów nie należy dodawać ' jak w Ninox, lecz po prostu użyć pełnej nazwy tabeli:

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

Dzięki tej składni natywny JS zwraca zmienne, które zachowują się tak samo jak w kodzie Ninox. Oznacza to, że można używać poniższej składni tak, jakby to był select:

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

obraz.png

Pomoc przy wpisywaniu w Konsoli

Od wersji 3.2.0 Konsola pomaga zapisać blok:

  • w adnotacji: po wpisaniu : tuż po #{ proponuje typy zwracane, array(type), nid(Table) i rid(Table); po wpisaniu ( po nid lub rid — listę tabel, a po array — listę typów; drugi znak : proponuje modyfikatory callback i async;
  • w JavaScripcie: autouzupełnianie proponuje zmienne i funkcje Ninox, które otrzymuje blok, w ich postaci JavaScript, a po kropce — składowe obiektu; w przypadku rekordu — nazwy jego pól (zob. „Autouzupełnianie JavaScriptu w blokach natywnych” na stronie Konsola).

Ta pomoc istnieje wyłącznie w Konsoli.

Co sprawdza Ninext

Od wersji 3.2.0 (beta). Karta Błędy kontroluje bloki #{ … }# całej bazy, a Konsola — bloki kodu, który Państwo w niej piszą.

Sygnalizowane są:

  • błąd składni JavaScript;
  • nieznana adnotacja (taka jak :datetime) lub tabela, której nie znaleziono w :nid(…) albo :rid(…): blok otrzymałby typ tekstowy;
  • blok z typem bez żadnego return albo taki, w którym niektóre ścieżki nie docierają do żadnego: zwróciłby pustą wartość;
  • blok :callback lub :async, który nigdy nie wywołuje callback: formuła nigdy nie otrzymałaby wartości;
  • blok umieszczony bezpośrednio w wyzwalaczu tabeli (po utworzeniu, po modyfikacji) lub w do as server, gdzie się nie wykonuje.

Modyfikacja rekordu

Zmienne typu rekordowego są przekazywane w formacie JSON, z parą klucz-wartość dla każdego pola rekordu.

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

Zawartość zmiennej r w JavaScripcie wygląda tutaj następująco:

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

Obiekt ma ponadto klucz _node: rekord w postaci, w jakiej przechowuje go Ninox, z polami wskazanymi przez ich wewnętrzne identyfikatory.

Ważne

W zależności od tego, czy kod wykonuje się w aplikacji na Maca, czy w przeglądarce, ID może mieć różną wielkość liter.
Aby temu zaradzić i uniknąć konfliktów między platformami, ID jest powtarzane we wszystkich możliwych wariantach wielkości liter.

Modyfikacja rekordu w JavaScripcie nie aktualizuje go w Ninox.

Wykonanie asynchroniczne

Czasem przydaje się zsynchronizowanie wykonania kodu Ninox z JavaScriptem.
Na przykład przy użyciu timera ważne jest, aby kod zakończył się dopiero po zakończeniu działania timera.
Bez takiej synchronizacji kod następujący po wykonaniu JavaScriptu uruchomiłby się natychmiast, co mogłoby prowadzić do nieoczekiwanych zachowań.

W pierwszym przypadku wartość „I don't know” zwracana jest natychmiast, a funkcja alert wyświetla wynik zanim pytanie zostanie w ogóle zadane użytkownikowi.

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

obraz.png

Po przejściu w tryb callback (lub async, który jest mu równoważny) funkcja alert Ninox wykonuje się dopiero po wywołaniu przez kod JavaScript funkcji zwrotnej, czyli po zadaniu pytania użytkownikowi. Typ zawsze występuje jako pierwszy: :text:callback, nigdy samo :callback, które zostałoby potraktowane jako typ.

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

Nagranie ekranu 2025-03-03 o 11.46.26.gif

Wywołanie funkcji

Z poziomu JavaScriptu można wywoływać funkcje Ninox: blok otrzymuje funkcje zadeklarowane wyżej w tej samej formule. Nie widzi funkcji kodu globalnego (zob. niżej, „Wywołanie funkcji kodu globalnego”).

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

obraz.png

Aby odebrać wartość zwracaną przez funkcję, trzeba ponownie użyć trybu callback (patrz wyżej: Wykonanie asynchroniczne):

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

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

obraz.png

Jeśli po wywołaniu procedury nie mają Państwo nic więcej do przetworzenia, można przekazać funkcję zwrotną bezpośrednio jako parametr:

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

obraz.png

Wywołanie funkcji kodu globalnego

Funkcja kodu globalnego nie znajduje się w zasięgu bloku: wywołanie jej po nazwie powoduje w nim błąd. Należy ją wywołać przez ninext.callNinoxFunction, z null jako drugim argumentem. Przy takiej funkcji w kodzie globalnym:

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

formuła wywołuje ją w ten sposób:

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

Aby odebrać wartość zwracaną, należy przekazać jako ostatni argument funkcję zwrotną, która otrzymuje ewentualny błąd, a następnie wynik. Gdy powyższa funkcja add znajduje się w kodzie globalnym:

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

Wszystko o callNinoxFunction: Funkcje pomocnicze HTML.

Operowanie na rekordach i funkcjach

Jeśli mają Państwo listę identyfikatorów rekordów, mogą Państwo wywołać funkcję Ninox, aby zmodyfikować kolejno każdy rekord. Wystarczy wywołać funkcję i przekazać jej identyfikator.

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;

Błędy wykonania

Jeśli w kodzie wystąpi błąd wykonania, zwrócony zostanie tekst komunikatu o błędzie:

obraz.png

W trybie callback ten sam komunikat staje się wynikiem bloku. Powyższy przykład to błąd składni: od wersji 3.2.0 karta Błędy sygnalizuje go przed jakimkolwiek wykonaniem (zob. „Co sprawdza Ninext”).

Przy uruchamianiu: onNinextLoaded

Przy uruchamianiu Ninox wyzwalacz „After Open” wykonuje się zanim Ninext zostanie wczytany: natywny JS nie działa w nim jeszcze, a umieszczony tam blok JavaScript kończy się niepowodzeniem bez żadnego komunikatu.

Jeśli zdefiniują Państwo funkcję onNinextLoaded(event) w wyzwalaczu „After Open”, Ninext wywoła ją, gdy zostanie faktycznie wczytany, u każdego użytkownika, przy każdym otwarciu bazy. event.version podaje wersję Ninext:

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

Daje to niezawodny punkt wejścia do uruchamiania JavaScriptu przy starcie albo do dostosowania bazy do wersji Ninext.

Warto wiedzieć

Ninox wykonał już wyzwalacz „After Open” przy otwarciu bazy; aby wywołać onNinextLoaded, Ninext ponownie wykonuje jego kod. Od wersji 3.2.0 ponownie wykonuje tylko jego deklaracje: funkcje oraz — gdy któraś funkcja odczytuje zmienną — zmienne (var, let). Pozostałe instrukcje wykonują się więc tylko raz. Jedynie wartość początkowa zmiennej odczytywanej przez funkcję jest obliczana po raz drugi: nie należy uzależniać jej od zapisu, na przykład od utworzenia rekordu. W rzadkim przypadku, gdy same deklaracje się nie kompilują, Ninext ponownie wykonuje cały wyzwalacz, jak we wcześniejszych wersjach.

Do wersji 3.1.2 włącznie Ninext ponownie wykonuje cały kod wyzwalacza: instrukcje umieszczone poza jakąkolwiek funkcją są wykonywane po raz drugi. Należy to uwzględnić w przypadku tych, które powinny wykonać się tylko raz: utworzenia rekordu, komunikatu, otwarcia tabeli.