2

Ninext: Calling a Ninox Function from HTML / JavaScript (callNinoxFunction)

Several of you have shown interest in the possibility offered by Ninext to call a Ninox function directly from JavaScript. The documentation is available here:
https://docs.ninext.fr/html-helping-functions-15

Iโ€™d like to share a few concrete examples of how this can be used, focusing on more advanced input fields.

๐Ÿ‘‰ First example: simple code

This example shows a set of simple fields that call a Ninox function, either to pass values as parameters or to retrieve a value returned by Ninox. The code is intentionally simple and explicit, to help you understand how to implement it in your own projects.

 

๐Ÿ‘‰ More advanced HTML example: sliders
Each time the Linear slider or Circular slider is modified, the value is immediately sent to Ninox via callNinoxFunction.

๐Ÿ‘‰ Same principle with a Choice field
The selected value updates the Ninox data in real time, again through a function call.

๐Ÿ‘‰ Last example: a Multi-choice field to manage tags
Here, multiple elements can be selected, with a direct update on the Ninox side.

 

The last two examples share an interesting feature:
they directly use the configuration defined in the Ninox schema to automatically retrieve titles, IDs, colors, and icons. No unnecessary duplication ๐Ÿ‘

โ„น๏ธ Note : as you can see, data refresh is triggered by switching from one tab to another, which forces the Ninox formula to be recreated.

 

๐Ÿ‘‰ All of these examples are available in the downloadable application here:
https://docs.ninext.fr/ninext-app-38

If you have ideas, experiments, or implementations using HTML / CSS / JS with callNinoxFunction, feel free to share them here ๐Ÿ˜€

5 replies

null
    • szormpas
    • 4 days ago
    • Reported - view

     Hi,

    I am trying to implement something like the following code:

    function createURLs() do
        for i in select Customer do
            shareFile(importFile(i, printAndSaveRecord(i, "Kunde"), "test.pdf"))
        end
    end;
    html("
      <button id='myButton' onclick='myFunction()'>Push to try it</button>
      <script>
            function myFunction() {
                const urls = ninext.callNinoxFunction(""createURLs"", ""myButton"");
                alert(urls.toString());
                // more code to insert here...
            }
      </script>
    ")
    

    I've got a question: Is there a way for myFunction to wait for the value to be returned by the Ninox function that was called before the rest of the code executes?

    Thanks for making Ninext better every day!

      • Ninox developper
      • Jacques_TUR
      • 4 days ago
      • Reported - view

      Hi  

      Your code was fine โ€” it was my documentation that was incomplete. I updated it 2 hours ago to explain how to retrieve the returned value: https://docs.ninext.fr/html-helping-functions-15#_luZTii8A

      callNinoxFunction returns a JSON object with either {error: [error value]} or {result: [result of call]}. This allows you to distinguish between a call error and a return with a null value or a text message.

      javascript

      function myFunction() {
          const response = ninext.callNinoxFunction("createURLs", "myButton");
          if (response.error) {
              alert("Error: " + response.error);
          } else {
              const urls = response.result;
              alert(urls.toString());
              // rest of your code here...
          }
      }

      Hope this helps!

      • szormpas
      • 4 days ago
      • Reported - view

        when I run the following code inside the #Ninext 2-3 app:

      function createURLs() do
          for i in select Customer do
              shareFile(importFile(i, printAndSaveRecord(i, "Kunde"), "test.pdf"))
          end
      end;
      html("
        <button id='myButton' onclick='myFunction()'>Push to try it</button>
        <script>
      function myFunction() {
          const response = ninext.callNinoxFunction('createURLs', 'myButton');
          if (response.error) {
              alert('Error: ' + response.error);
          } else {
              const urls = response.result;
              alert('urls: ' + urls);
          }
      }
        </script>
      ")
      

      First, I get the alert (see screenshot), then the Ninox function is executed.

      • Ninox developper
      • Jacques_TUR
      • 2 days ago
      • Reported - view

       

      Thank you so much Sotirios for this very useful example! ๐Ÿ™

      It helped me discover that callNinoxFunction was not handling asynchronous calls correctly. The tests I had done until now were only on simple function returns, which worked despite the issue.

      Good news: Ninext version 2.3.6 fixes this problem! You can now call asynchronous functions and retrieve the result via a callback function.

      Here is your code modified to work with asynchronous return values:

      function createURLs() do
          for i in select Customer do
              shareFile(importFile(i, printAndSaveRecord(i, "Kunde"), "test.pdf"))
          end
      end;
      html("
      <button id='myButton' onclick='myFunction()'>Push to try it</button>
      <script>
      // MODIFIED: removed 'async' keyword - no longer needed with callback pattern
      function myFunction() {
          // MODIFIED: removed 'await'
          // MODIFIED: parameters order is now (elementId, functionName, callback)
          ninext.callNinoxFunction( 'createURLs', 'myButton', function(error, result) {
              // MODIFIED: callback receives a 'error' and 'result' parameters
              if (error) {
                  alert('Error: ' + error);
              } else {
                  alert('URLs: ' + result);
              }
          });
      }
      </script>
      ")

      ๐Ÿ“– Updated documentation: https://docs.ninext.fr/html-helping-functions-15

      Feel free to reach out if you encounter any other issues!

    • szormpas
    • 2 days ago
    • Reported - view

     , thanks for the support on the asynchronous capability!

    Now, I can create reports attached to records in my database and print them all in one merged PDF file with just one click!

    If anyone wants to check the above functionality, first download the #Ninext 2-3 app and then copy the following code into a formula field:

    function createURLs() do
        let urls := for i in select Customer do
                shareFile(importFile(i, printAndSaveRecord(i, "Kunde"), "customer.pdf"))
            end;
        parseJSON(formatJSON(urls[!= null]))
    end;
    html("<head>
      <style>
        .custom-button {
          color: rgb(86, 110, 177);
          background-color: rgba(255,255,255,0);
          padding: 5px 40px;
          border: 1px solid rgb(86, 110, 177);
          border-radius: 5px;
          cursor: pointer;
          font-size: 14px;
          font-weight: 600;
          transition: all 0.3s ease;
        }
        .custom-button:disabled {
          opacity: 0.6;
          cursor: not-allowed;
        }
      </style>
    </head>
    <body>
      <button id='merge-pdf-btn' class='custom-button' onclick='ninext.callNinoxFunction(""createURLs"", this, (error, result) => {mergePDFs(result, ""merge-pdf-btn"");});'>Push to print reports</button>
      <script src='https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js'></script>
      <script>
        async function mergePDFs(data, buttonId) {
          const button = document.getElementById(buttonId);
          if (!button || !Array.isArray(data)) return;
          const originalText = button.textContent;
          button.disabled = true;
          button.textContent = 'Processing...';
          try {
            const urls = data.filter(url => url?.trim());
            if (!urls.length) {
              alert('There are no reports for these customers!');
              return;
            }
            const pdfDoc = await PDFLib.PDFDocument.create();
            for (let i = 0; i < urls.length; i++) {
              button.textContent = `Processing ${i + 1}/${urls.length}...`;
              const res = await fetch(urls[i]);
              const buffer = await res.arrayBuffer();
              const pdf = await PDFLib.PDFDocument.load(buffer);
              const pages = await pdfDoc.copyPages(pdf, pdf.getPageIndices());
              pages.forEach(p => pdfDoc.addPage(p));
            }
            button.textContent = 'Creating PDF...';
            const blob = new Blob([await pdfDoc.save()], { type: 'application/pdf' });
            const url = URL.createObjectURL(blob);
            window.open(url, '_blank');
            setTimeout(() => URL.revokeObjectURL(url), 2000);
          } catch (error) {
            alert('Error creating PDF. Please try again.');
          } finally {
            button.disabled = false;
            button.textContent = originalText;
          }
        }
      </script>
    </body>")
    

    What this does:

    • Generates individual PDF reports for each customer using a print layout
    • Merges all PDFs into a single document
    • Opens the combined PDF in a new tab
    • Shows progress during processing

    How to adapt it to your database:

    1. Replace Customer with your table name
    2. Replace "Kunde" with your print layout name
    3. Adjust the filter criteria in the select statement as needed

    The magic happens through the pdf-lib js open-source library combined with Ninext's asynchronous function calls. The button shows real-time progress as it processes each record.

     Hope this helps others who need bulk PDF operations! ๐ŸŽ‰

Content aside

  • 2 Likes
  • 2 days agoLast active
  • 5Replies
  • 65Views
  • 2 Following