Chidi Okwudire IT Professional. ERP Enthusiast. NetSuite Certified (Administrator, SuiteCloud Developer II, and ERP Consultant). Celigo Certified (Level 4+). Passionate About Empowerment Through Knowledge Sharing. Always Eager to Learn.

NetSuite Input Dialog v2.0

7 min read

Last updated on April 7, 2022.

The 2022.1 NetSuite bi-annual release broke Input Dialog v1.0. Upgrade to v2.0 to fix the issue of duplicate buttons and missing textarea value in the callback.

What do you do when you need to capture user input just before they submit a form or right after they click that "Approve" or "Reject" button? The answer is simple: Use the NetSuite Input Dialog! Click To Tweet

TL;DR

NetSuite Input Dialog in Action
NetSuite Input Dialog in Action
  • NetSuite’s N/ui/dialog module does not include an input dialog although there are situations where capturing user input based on dynamic criteria is necessary.
  • Existing solutions compromise on user experience, are rigid, and/or are too tightly-coupled with NetSuite’s internal dependencies which may change without notice.
  • The NetSuite Input Dialog illustrated above is built on NetSuite’s dialog module, thereby providing a native, elegant, and easy-to-integrate answer to the input dialog problem.
  • This module could be yours right now, at no cost, for use in your non-commercial and commercial projects; grab it here!

Known Issue: The Input Dialog will not work with records that use the “new” UI e.g. InboundShipments or Projects (Note: The Projects record has a setting to turn off the new UI; see SuiteAnswers ID: 98178). This is because the new UI does not support HTML in the N/ui/dialog module. We hope that this is an omission that will be fixed in an upcoming release before the new UI becomes too pervasive.

Common Use Cases Requiring a (Modal) Input Dialog

NetSuite’s entry forms are the primary means of interacting with users and capturing input. Fields on forms can be shown conditionally, made optional or mandatory, and so on. This can be done using static configuration at the form level, via workflows, and/or scripting. However, there are scenarios where these options do not suffice. Let’s consider a few examples. (Feel free to skip to the next section if the need for an input dialog is already sufficiently evident to you.)

Rejection Reasons in Approval Workflows

In approval processes, it is typical that when a user rejects an entry, they are required to provide a rejection reason. Since we do not know upfront if a user will approve or reject the entry, we cannot use a preconfigured mandatory “Rejection Reason” field on the record.

This is a classical example of where a modal input dialog to capture the rejection reason would be logical: Upon pressing the “Reject” button, the input dialog is shown and the user is required to enter a rejection reason before being able to reject the entry. Because the modal input dialog is shown over the entry to be approved, the user does not need to transition between screens, producing a much better user experience.

I’ve seen several NetSuite approval workflows over the years and all of them approach the rejection reason requirement in an awkward, non-intuitive way: When the user clicks the “Reject” button, they are redirected to a new page where they should enter the rejection reason. Upon entering the rejection reason and saving (what is actually a separate record), they are redirected back to the original entry being approved. Talk about poor user experience!

As an aside, I recall when I first got exposed to NetSuite and had to approve vendor bills. I can’t tell you how often I ended up thinking I had rejected bills but not quite because I clicked the “Reject” button and went my way without anticipating the separate rejection reason screen. I’ve even seen less prudent implementations where the entry is actually rejected before being redirected to the rejection reason screen! This offers a convenient backdoor for “lazy” managers to circumvent entering a rejection reason entirely. But I’m digressing now…

I strongly believe that one of the reasons why most approval workflows have resorted to the awkward flow described above is the absence of a native input dialog in NetSuite. Well, now that there’s one, I look forward to seeing approval workflows that leverage this approach to produce a smoother user experience. To anyone ready to take up the challenge, this article by “NetSuite Developer”[I]NetSuite Developer (April 21, 2019). Trigger Client Scripts before Workflow Actions fire. Available at http://www.netsuiterp.com/2019/04/trigger-client-scripts-before-workflow.html [Accessed November … Continue reading might come in handy.

3-Way Match Validation

In my case, this was the scenario that led me to tackle the Input Dialog challenge. 3-way matching is a common accounts payable control process in which vendor bills, item receipts, and purchase orders are validated based on predefined thresholds. If the variance exceeds those thresholds, the outcome is a “no-match”, typically leading to additional approvals. Similar to the approval workflow scenario, we do not know upfront what the outcome of a 3-way match execution will because the user may make changes to the transaction which could affect the results. However, if there’s a “no-match”, we want to prompt the user to enter an explanation that could be passed on to approvers or simply stored for reference.

I was glad to see a solution by SquareWorks Consulting that captures the 3-way match failure reason using a modal input dialog with native look-and-feel. Unfortunately, as at the time of writing, their implementation is not publicly available. I do not know how they tackled the problem, but I will tell you how I did next.

Note: Although the demo in this article shows the Input Dialog in action in edit mode, you can trigger it in view mode if desired. You can draw inspiration from this comment from a reader who went that route.

Solution: The NetSuite Input Dialog

The solution uses the same client-side logic as NetSuite’s native ‘N/ui/dialog with slight adaption to include a custom event handler which allows us to capture and process the inputs.

An earlier implementation relied on injecting a script into the dialog’s HTML message along with jQeury to achieve the same result. The beauty of that solution was that it did not require us to know or replicate NetSuite’s internal dialog logic. However, with the 2022.1 release, NetSuite silently withdrew support for executing scripts in the HTML body of the N/ui/dialog module. That broke our solution and forced us to get more tightly coupled with NetSuite internals, a practice that we generally frown on.

The solution relies on NetSuite client-side internal logic/APIs and could thus break if NetSuite changes this logic in the future

Nonetheless, fixing the dialog in such a case will be easy and straightforward, provided NetSuite continues to support dialogs with HTML, which ironically happens not to be the case in the “new” UI used by some records such as Inbound Shipments or Advanced Projects. Thus, there are really no guarantees and this remains a hack with some risk. Please refer to my previous “NetSuite Stick Headers” article where I provide insights on how to assess this risk for your specifc use case. Although that discussion is in the context of DOM manipulation (which we are no longer doing here with the latest version), the conclusions are still relevant: Do not use hacks for business-critical processes.

Learning by Example

The Input Dialog module is available here free of charge including installation steps and API documentation/examples.

Using the input dialog is remarkably easy since the API is very much aligned with NetSuite’s dialog.create(), thereby allowing you to leverage existing patterns for creating your dialogs. Here’s a complete example of the Input Dialog in action.

NetSuite Input Dialog Example
NetSuite Input Dialog Example

Let’s quickly review the code that produced the above results.

/**
 * @NApiVersion 2.0
 * @NScriptType ClientScript
 * @NModuleScope SameAccount
 */
define(['/SuiteScripts/netsuite-insights.com/utils/NSI_CM_InputDialog'],

function(nsiInputDialog) {
    /**
     * Validation function to be executed when record is saved.
     */
    function saveRecord() {
        var options = {
            title: 'Input Dialog',
            message: 'Please enter your rejection reason below:',
            buttons: [{
                label: 'Reject', 
                value: 1
            },{
                label: 'Cancel',
                value: 2
            }],
            textarea: {
                rows: 6,
                cols: 48,
                isMandatory: true,
                caption: 'Rejection Reason',
                fieldId: 'memo',
                actionButtons: [1]
            }
        };

        var failure = function (reason) {
            console.log('Failure: ' + reason);
        }
        
        var success function (result, value) {
            alert('Input dialog closed by clicking button ' + result + ' | value: ' + value);
            // Do something with the actual result here.
        }
        
        nsiInputDialog.create(options, success, failure);
    }
 
    return {
       saveRecord: saveRecord
    };
});
  • Line 6: We now import the NetSuite Insights’ Input Dialog module NSI_CM_InputDialog instead of N/ui/dialog. This module is available for free here.
  • Lines 23-30: We define the text area configuration. The rest of the options variable is identical to what we would specify if we were using N/ui/dialog! This makes integrating the Input Dialog simple as it leverages existing patterns.
  • Line 37: Notice that our success callback takes an additional parameter, namely the value entered by the user. This allows you to do whatever you want with the input in case writing it to a text field on the form (line 28) is not sufficient for your use case.

That’s all there is to it. However, before you we go, I’ll like to highlight alternative solutions that you might want to consider.

Alternative Solutions

Our approach is not the only way to solve this problem. Here are pointers to other solutions out there in the wild, along with my take on when they might be viable alternatives.

Community member Adolfo Garza, in his 2016 blog post “SuiteScript 2.0 Dialog Prompt + On SaveRecord”[II]Adolfo Garza (December 29, 2016). SuiteScript 2.0 Dialog Prompt + On SaveRecord. Available at https://ursuscode.com/netsuite-tips/suitescript-2-0-dialog-prompt-on-saverecord [Accessed November 17, … Continue reading, captures the two common alternative solutions (the screenshots below come from that article).

  1. Use the prompt() method which is implemented by all standard browsers. While this will get the job done, if you care about a native look-and-feel, it is not really an option. Moreover, the user can suppress future popups by checking the “Prevent this page from creating additional dialogs” option. This could potentially derail your solution. Also, there’s no way to make the input mandatory.
    JS Prompt Dialog
  2. Hack deeper and directly use the Ext JS library that NetSuite uses internally to produce dialogs. In my opinion, it is not wise to build a solution that depends on NetSuite’s internal logic. NetSuite might move away from the library or upgrade to a new version (they’re currently still on a very old version so an upgrade scenario is realistic). I’m not afraid to hack as you can tell, but I feel this is the wrong level to hack at. Moreover, the look-and-feel, while closer to the NetSuite dialogs’, is still noticeably different. For example, there’s an “X” button which allows you to close the dialog possibly without entering any input.
    Ext JS Input Dialog
  3. A third approach is to ditch NetSuite’s dialog completely in favor of your favorite javascript dialog module. There are lots of them out there. A community member posted a solution on the NetSuite Professionals Slack group some time back. He used the popular SweetAlerts2 (a.k.a. swal2) library with some CSS tweaks to make it look like native NetSuite and inherit the theme colors. The results illustrated below for a different popup type are quite good and this is definitely a viable option. At this time, I do not have enough information about this approach to detail it. Hopefully, we can get the author to write about it here in the near future.

    swal2 based dialog with NS look-and-feel

My greatest concern with solutions (1) and (2) above though is that, should you want to capture other kinds of inputs, e.g. checkboxes or multi-select options, you’ll pretty much be out of luck. Solution (3) though more involved and potentially compromising a bit on native look-and-feel would be my recommendation, if you decide not to go with the Input Dialog.

The approach used by the Input Dialog can be easily extended to capture other inputs. (Depending on demand/needs, I might implement such extensions in the future. Drop a comment below if you have interesting use cases not currently covered. Also, if you implement extensions before me, be sure to share!)

Parting Words

Undoubtedly, others have solved the input dialog problem in the past but have decided not to share their solution with the community (or I was not skilled enough to find them during my extensive googling). Here’s my take: It is bad enough that NetSuite does not provide basic utilities like an input dialog. It is equally sad that those who have solved such basic problems do not see the need to share with the community, often trying to monetize their solutions instead. NetSuite Insights is dedicated to making sure that common utilities (or at least the logic behind them) are put out there for the benefit of all. And I call on other community leaders to emulate! It is obviously a business environment and money must be made. But some things are so basic that they need not be monetized…


Do you have utilities lying around that you know would be useful to the community? I encourage you to clean them up and share them. If you need some help or inspiration, feel free to reach out to us at stories[at]netsuite-insights.com


There are more NetSuite Insights and (free) utilities in the works so be sure to subscribe to our no-nonsense mailing list to get notified when new NetSuite Insights and freebies are published (approximately 2-3 mails per month).

Other Interesting Utilities

Further Reading[+]

Chidi Okwudire IT Professional. ERP Enthusiast. NetSuite Certified (Administrator, SuiteCloud Developer II, and ERP Consultant). Celigo Certified (Level 4+). Passionate About Empowerment Through Knowledge Sharing. Always Eager to Learn.

46 Replies to “NetSuite Input Dialog v2.0”

  1. Hello!

    This is an awesome code snippet that we can use for rejection notes for different types of records that require approval. I noticed that in your code, followed by your example, this only functions in Edit mode. I was hoping you would know of a way in which this can be used in View mode? We would like to implement this where users can mark a record as rejected without being able to go in and edit. If the user can edit the record, they could just put in their rejection note straight in the field without the use of the Input Dialog.

    1. Hi Alejandro,

      Thanks for reaching out. I demonstrate this solution in Edit mode but there’s no reason why it won’t work in View mode. There are techniques for adding buttons in view mode e.g. as explained here. That should do the trick.

      Cheers.

    2. Were you able to get this working from View mode? I am using the jQuery script snippet and it works in edit mode, but I can’t get the fields to update in view mode.

      1. Hi Dave,

        Sorry about the late response, I just got back to this now because I’m running into a separate issue haha.

        I was able to get this working in View mode, and we use it for adding reject notes on expenses and signing an agreement when they sign out a piece of equipment.

        – We use a UserEvent script to add a button to the page. (var form = context.form; form.clientScriptFileId = FileID; where FileID is a parameter given within the script deployment. The function it has to run within that other script is also given as a parameter in the deployment).
        – The UserEvent script triggers the input dialog, choosing the right function based on that deployment (this is how we can reuse this for different purposes).
        – When the user submits within the InputDialog window, this calls a Suitelet using url.resolveScript. This Suitelet handles the actual processing (storing the value in the InputDialog text field into the record), and then does a redirect back to the record you were in. From the user’s perspective, it looks like the page is processing and refreshing.

        The current issue I’m running into right now is that the buttons are now appearing twice:
        https://imgur.com/a/Go4XZsY

        The top two buttons don’t do anything, but not sure as to why they are now showing twice. We haven’t touched the NSI_CM_InputDialog.js file at all.

        1. Hi Alejandro, thanks for sharing. It brings us great job to see this as we firmly believe that it makes the community stronger.

          As per the other issue, unfortunately 2022.1 broke our solution. But, we have an even more robust solution ready for release testing. I’ll PM the details and once validated, we’ll release an updated post.

          1. Hello,

            I received a new version over email and can confirm that it has fixed my issue 🙂

          2. Excellent! Thanks for the confirmation. We’ll release the updated version next week.

          3. Hello, please can I have the user eventscript and workflow action script code you used here?

          4. Hi, unfortunately, we don’t have any scripts ready to offer you. For my tests, we simply hooked up a client script to the form to trigger the functionality. Cheers.

  2. Hello! I tried to use this with Save button but did not succeed. Use case is that when user edits a transaction and then saves it, if certain criteria are true user would need to add additional note explaining the changes. I tested this so that I added the setValue to few different places in the script, but no luck. Every time the text from input dialog is lost in the save. Is there a way to do this with NetSuite Input Dialog? Thanks!

    1. Hi Markus,

      Please share the relevant piece of your code and how you’re triggering it (I suspect the client-side saveRecord event). Without seeing what you’re doing, it’s hard to tell what might be going wrong.

      Cheers

      1. Hi!

        Here is the code I used. Very simple, I just added the setValue there.



        /**
        * @NApiVersion 2.0
        * @NScriptType ClientScript
        * @NModuleScope SameAccount
        */
        define(['NSI_CM_InputDialog'],

        function(nsiInputDialog) {
        /**
        * Validation function to be executed when record is saved.
        */
        function saveRecord() {
        var options = {
        title: 'Input Dialog',
        message: 'Please enter your rejection reason below:',
        buttons: [{
        label: 'Reject',
        value: 1
        },{
        label: 'Cancel',
        value: 2
        }],
        textarea: {
        rows: 6,
        cols: 48,
        isMandatory: true,
        caption: 'Rejection Reason',
        fieldId: 'memo',
        actionButtons: [1]
        }
        };

        var failure = function (reason) {
        console.log('Failure: ' + reason);
        }

        var success function (result, value) {
        alert('Input dialog closed by clicking button ' + result + ' | value: ' + value);
        // Do something with the actual result here.
        newRecord.setValue({
        fieldId: 'custbody_edit_notes',
        value: value
        });
        }

        nsiInputDialog.create(options, success, failure);
        }

        return {
        saveRecord: saveRecord
        };
        });

        1. Hey Markus,

          Your code has some issues. Please open the debugger (F12) and you’ll likely see errors. Here are the ones I spotted:

          1. var success function() should be var success = function.
          2. newRecord is not defined anywhere. Your saveRecord callback should have a parameter on which you can access currentRecord (not newRecord). You’ve not defined that parameter in the saveRecord and so nothing will work.

          With these fixes, I was able to run your code in my demo account and it worked.

          Here are some more points to take into account. Since you’re intercepting the native save button, the save action will not proceed (your saveRecord is implicitly returning false which tells NetSuite not to save and that’s what you want since you’re evaluating criteria). To trigger a save after your done, you can make this *undocumented* call: getNLMultiButtonByName(‘multibutton_submitter’).onMainButtonClick(this); as described at https://www.abaci-us.com/netsuite-suitescript-2-dialog-confirm-workaround/

          Let me know how it goes.

      2. Oh and sure there is also the load of the record, that is missing from previous example. So this is the part I added:

        var newRecord = context.currentRecord;

        newRecord.setValue({
        fieldId: ‘custbody_edit_notes’,
        value: value
        });

  3. Hello again! Sorry, my previous example was not so good. It was quick copy paste because I didn’t have my actual code with me at that moment. Here is an actual example I tested in my SB.

    —-
    /**
    * @NApiVersion 2.0
    * @NScriptType ClientScript
    * @NModuleScope SameAccount
    */
    define([‘./NSI_CM_InputDialog’],

    function(nsiInputDialog) {
    /**
    * Validation function to be executed when record is saved.
    */
    function saveRecord() {
    var options = {
    title: ‘Input Dialog’,
    message: ‘Please enter your edit reason below:’,
    buttons: [{
    label: ‘Ok’,
    value: 1
    },{
    label: ‘Cancel’,
    value: 2
    }],
    textarea: {
    rows: 6,
    cols: 48,
    isMandatory: true,
    caption: ‘Rejection Reason’,
    // fieldId: ‘memo’,
    actionButtons: [1]
    }
    };

    var failure = function (reason) {
    console.log(‘Failure: ‘ + reason);
    }

    var success = function (result, value) {
    alert(‘Input dialog closed by clicking button ‘ + result + ‘ | value: ‘ + value);
    // Do something with the actual result here.

    if (result == 1) {

    var currentRecord = context.currentRecord;

    currentRecord.setValue({
    fieldId: ‘custbody_edit_notes’,
    value: value
    });

    getNLMultiButtonByName(‘multibutton_submitter’).onMainButtonClick(this);
    }

    }

    nsiInputDialog.create(options, success, failure);
    }

    return {
    saveRecord: saveRecord
    };
    });

    So the result I would like to have:
    – User does changes to record
    – User selects Save
    – Pop up will show and user will add note
    – Record will be saved right after pop up is closed with “Ok”.

    Issues:
    – With this code the value is not stored to the custbody_edit_notes field
    – Record saving process will not proceed.

    If you were able to make it work, could you share your script example? Thanks!

    1. Hey Markus,

      1. Updating the notes field didn’t work because your code has an error: You’re referencing undefined variable ‘context’. If you looked at the console (F12), you’d have seen this error as well.
      2. The logic you’ve implemented to retrigger the save does not match the pattern described in the article I referenced. You need at least one variable to track your state. Please review and understand that article.

      Here’s working code (validated in my test account). Cheers!


      /**
      * @NApiVersion 2.0
      * @NScriptType ClientScript
      * @NModuleScope SameAccount
      */
      define(['./NSI_CM_InputDialog'],

      function(nsiInputDialog) {

      // Use variable to track state as per trick explained at: https://www.abaci-us.com/netsuite-suitescript-2-dialog-confirm-workaround/
      var editReasonCaptured = false;

      /**
      * Validation function to be executed when record is saved.
      */
      function saveRecord(context) {

      if (!editReasonCaptured) {
      var options = {
      title: 'Input Dialog',
      message: 'Please enter your edit reason below:',
      buttons: [{
      label: 'Ok',
      value: 1
      },{
      label: 'Cancel',
      value: 2
      }],
      textarea: {
      rows: 6,
      cols: 48,
      isMandatory: true,
      caption: 'Edit Reason',
      actionButtons: [1]
      }
      };

      var failure = function (reason) {
      console.log('Failure: ' + reason);
      }

      var success = function (result, value) {
      //alert('Input dialog closed by clicking button ' + result + ' | value: ' + value);
      if (result == 1) {

      var currentRecord = context.currentRecord;

      currentRecord.setValue({
      fieldId: 'custbody_edit_notes',
      value: value
      });

      editReasonCaptured = true
      getNLMultiButtonByName('multibutton_submitter').onMainButtonClick(this); // Trigger NetSuite to reinvoke the saveRecord callback
      }

      }

      nsiInputDialog.create(options, success, failure);
      } else {
      editReasonCaptured = false; // Reset flag
      return true; // Allow NetSuite to proceed with saving
      }
      }

      return {
      saveRecord: saveRecord
      };
      });

      1. Hello!

        Thank you, this one works! It was that Abaci Trick that didn’t work with my test. This is great 🙂

        1. Good to hear. You’re welcome! Glad to see another use case of this “hack” 😉

          1. Hi! I noticed one issue still. As you know there might be other actions linked to the Save button. By default like “Save & New” or with approval “Save & Approve”. With this Abaci Trick it does not follow what user has selected, it will follow what has been the last selection and is visible as the Main Button. This is dangerous, because with this user might click “Save” but result might be “Save & Approve”.

            Any ideas how to fix this?

          2. Hi Markus,

            Interesting find… This issue has to do with NetSuite’s internal behavior and not the input dialog module and I don’t directly know how you can address it. Have you had a look at the documentation? Maybe NetSuite APIs offer a way to detect the save mode. If not, perhaps you can inspect the code in the browser and see if there’s a way to detect the different save options. Another alternative could be to create a custom save button and hide the default one. In that way, you’re fully in control and not dependent on undocumented NetSuite functions.

            Cheers

  4. Hello!
    We are trying to expose the standard List/record field in this dialog window for the user to be able to select and click on update (Custom button).

    Some changes I have made in your custom module. you can refer to the following functions:
    1.) _parseOptions
    2.) _buildBody

    /**
    * @NApiVersion 2.0
    * @NModuleScope SameAccount
    */

    /**
    * @file An input dialog utility to capturing user input and optionally writing it directly a form field.
    * @copyright Copyright (c) 2020 SMASH ICT.
    * @see {@link https://netsuite.smash-ict.com/2020/11/netsuite-input-dialog/|Blog Article}
    *
    * @author Chidi Okwudire
    * @license MIT (https://opensource.org/licenses/MIT)
    * @date 2020-11-08
    * @version 1.0.0
    */
    define([‘N/ui/dialog’],

    function(dialog) {

    const LINE_BREAK = ‘\n’;

    /**
    * A utility class extending NetSuite’s N/ui/dialog module with the ability to capture user input.
    * @exports NSI/InputDialog
    * @namespace InputDialog
    */
    function InputDialog(options) {
    var _options;

    _parseOptions(options);

    /// Private functions ////
    /*
    * A console.log wrapper. Only logs if the debug=t parameter is specified in the page’s URL.
    * @param {*} val The value to be logged.
    */
    function _log(val) {
    if ((window.location.search || ”).toLowerCase().indexOf(‘debug=t’) >= 0) {
    console.log(val);
    }
    }

    /*
    * Parses the inputs, setting up default values and performing validations as per specs.
    * @param {object} [input] The input dialog options to be parsed.
    */
    function _parseOptions(input) {
    _log(‘Options before processing: ‘ + JSON.stringify(input));

    var options = {};
    if (input) {
    options = JSON.parse(JSON.stringify(input)); // Deep copy
    }

    if (!options.textarea) {
    options.textarea = {};
    }

    if (!Array.isArray(options.buttons) || options.buttons.length < 1) {
    options.buttons = [{ label: 'OK', value: 1}];
    }
    // NetSuite will validate button properties upon dialog creation.

    var rows = parseInt(options.textarea.rows)
    if (isNaN(rows) || rows <= 0) {
    options.textarea.rows = 5;
    }
    var rows_date = parseInt(options.date.rows);
    if(isNaN(rows_date) || rows_date <= 0){
    options.date.rows = 1;
    }
    var cols = parseInt(options.textarea.cols)
    if (isNaN(cols) || cols <= 0) {
    options.textarea.cols = 40;
    }
    var cols_date = parseInt(options.date.cols);
    if (isNaN(cols_date) || cols_date <= 0) {
    options.date.cols = 40;
    }
    if (options.textarea.initialValue == null) {
    options.textarea.initialValue = '';
    }
    if (options.date.initialValue == null) {
    options.date.initialValue = '';
    }

    if (options.select.initialValue == null) {
    options.select.initialValue = '-1';
    }

    if (options.textarea.isMandatory == null) {
    options.textarea.isMandatory = false;
    }
    if (options.date.isMandatory == null) {
    options.date.isMandatory = false;
    }
    if (options.select.isMandatory == null) {
    options.select.isMandatory = false;
    }

    if (options.textarea.caption == null) {
    options.textarea.caption = '';
    }
    if (options.date.caption == null) {
    options.date.caption = '';
    }
    if (options.select.caption == null) {
    options.select.caption = '';
    }

    if (options.textarea.isMandatory) {
    if (options.textarea.caption === '') {
    options.textarea.caption = 'Input field';
    }

    options.textarea.caption += ' *';
    }

    var actionButtons = options.textarea.actionButtons
    if (!Array.isArray(actionButtons)) {
    options.textarea.actionButtons = [];
    } else {
    // Make sure that the specified action buttons are valid.
    var index, button;
    var tmpArr = actionButtons.splice();
    for (var i = 0; i = 0) {
    tmpArr.splice(index, 1);
    }
    }

    if (tmpArr.length !== 0) {
    throw ‘The following action button(s) do not match any of the input button values: ‘ + JSON.stringify(tmpArr);
    }
    }

    _log(‘Options after processing: ‘ + JSON.stringify(options))
    _options = options;
    }

    /*
    * Custom event handler for button clicks
    * @param {string} buttonId
    */
    function _onClick(buttonId) {
    _log(arguments.callee.name + ‘ Button ID: ‘ + buttonId);

    var canClose = true;
    var text = ”;
    window.nsiInputFieldValue = ”;

    // Parse input only when clicked button is an action button.
    if (_options.textarea.actionButtons.length === 0 || _options.textarea.actionButtons.indexOf(buttonId) >= 0) {
    text = (jQuery(“#nsi-inputdialog-textarea”).val() || ”).trim();

    if (_options.textarea.isMandatory && !text) {
    canClose = false;
    // We use an alert here for a consistent experience as NetSuite uses this approach to validate mandatory fields.
    alert(“Please enter a value in the mandatory input text field.”);
    } else {
    if (_options.textarea.fieldId) {
    nlapiSetFieldValue(_options.textarea.fieldId, text);
    }
    // Store the result in a global variable for access later.
    window.nsiInputFieldValue = text;
    }
    }

    if (canClose) {
    // Click the original NetSuite button that we’ve shadowed
    // to trigger the normal processing which includes closing the dialog
    // and passing the ID Of the clicked button
    jQuery(‘:button[value=”‘ + buttonId + ‘”]’).click();
    }
    }

    /*
    * Generates HTML for the input dialog’s text area.
    */
    function _buildBody() {
    var output = ”;
    if (_options.message) {
    output += ” + _options.message + ”;
    }

    const textarea = _options.textarea;
    if (textarea.caption) {
    output += ”+ textarea.caption.toString().toUpperCase() + ”;
    }
    output += ” + textarea.initialValue + ”;

    const dateField = _options.date;
    if(dateField.caption){
    output += ”+dateField.caption.toString().toUpperCase()+”;
    }
    output += ”;

    const selectField = _options.select;
    if(selectField.caption){
    output += ”+selectField.caption.toString().toUpperCase()+”;
    }

    if(selectField.list){
    var list = selectField.list
    output += ”;
    output += ‘ ‘;
    for(var i=0; i<list.length; i++){
    output += '’+list[i].text+’ ‘;
    }
    output += ”;
    }

    return output;
    }

    /*
    * Generates HTML for the custom input dialog button bar that shadows the native one, enabling us to intercept and handle user input.
    */
    function _buildButtons() {
    var output = ” + LINE_BREAK; // We use the same class used by NetSuite to style the buttons for consistency
    for (var i = 0; i < _options.buttons.length; ++i) {
    output += _buildButton(_options.buttons[i]) + LINE_BREAK;
    }
    output += '’
    return output;
    }

    /*
    * Generates an individual shadow button.
    * @param {Object} button The button configuration.
    */
    function _buildButton(button) {
    // Add a shadow button that will get displayed and handle the onclick event.
    return ” + button.label + ”;
    }

    /// Privileged functions ///
    /*
    * Creates the actual input dialog including the HTML used to decorate the native
    * NetSuite dialog with input capturing capabilities.
    */
    this.build = function () {
    var htmlMessage = ” +
    ‘var _options = ‘ + JSON.stringify(_options) + LINE_BREAK +
    _log.toString() + LINE_BREAK +
    _onClick.toString() + LINE_BREAK +
    // The following function runs upon pag initialization and hides the native button action bar
    ‘(function($) {‘ + LINE_BREAK +
    ‘$(function($, undefined) {‘ + LINE_BREAK +
    ‘$(“.uir-message-buttons”).last().hide();’ + LINE_BREAK +
    ‘});’ + LINE_BREAK +
    ‘})(jQuery);’ + LINE_BREAK +
    ” + LINE_BREAK +
    _buildBody() + LINE_BREAK +
    _buildButtons();

    _log(htmlMessage);
    var options = {
    title: _options.title,
    message: htmlMessage,
    buttons: _options.buttons
    };
    _log(options);
    return options;
    }
    }

    /**
    * Creates an input dialog with the specified options.
    * @memberof InputDialog
    * @method create
    *
    * @param {Object} [options] Configuration options for the input dialog.
    * @param {string} [options.title] The dialog title. Defaults to an empty string.
    * @param {string} [options.message] Text to be displayed about the input field. Defaults to an empty string.
    * @param {object[]} [options.buttons] A list of buttons to be included in the dialog.
    * Each item in the button list must be an object that contains a label and a value property.
    By default, a single button with the label OK and the value 1 is used.
    * @param {Object} [options.textarea] The configuration for the input text area. If not specified, default values as specified below are used.
    * @param {Object} [options.textarea.rows] The input text area’s default height expressed in rows. Defaults to 5.
    * @param {Object} [options.textarea.cols] The input text area’s default width expressed in columns. Defaults to 40. A value above 50 is NOT recommended.
    * @param {Object} [options.textarea.isMandatory] Indicates whether user input is mandatory.
    * If true and the user presses an action button without entering any input, an alert popup will be shown and the input dialog will stay open. Defaults to false.
    * @param {Object} [options.textarea.caption] The caption to show above the input text area. Defaults to ‘Input field *’ if isMandatory = true; omitted otherwise.
    * @param {Object} [options.textarea.initialValue] The initial value to be displayed in the input text area. Defaults to an empty string.
    * @param {Object} [options.textarea.fieldId] The ID of the field on the current page to which the user input should be written upon closing the Input dialog using any of the action buttons.
    * If specified, in addition to writing the text to this field, the text will still be passed to the success callback function if provided.
    * @param {int[]} [options.textarea.actionButtons] A list of buttons (value properties only) that will trigger validation and persisting the input.
    * Defaults to all buttons added to the input dialog. Using this option, the cancel button can be excluded as an action button, enabling it to
    * be used to close an input dialog without providing input.
    * @param {function} [success] A callback function to be executed (asynchronously) when the dialog is closed.
    * It will be passed two parameters: (1) The value of the button pressed and (2) the input entered by the user.
    * @param {function} [failure] A callback function to be executed (asynchronously) if anything goes wrong.
    * It simply forward whatever NetSuite’s native dialog.create() passes into the catch portion of the Promise object.
    */
    function create(options, success, failure) {
    console.log(JSON.stringify(options));
    dialog.create(new InputDialog(options).build())
    .then(function(result) {
    if (success) {
    success(result, window.nsiInputFieldValue)
    }
    })
    .catch(function(reason) {
    if (failure) {
    failure(reason)
    }
    });
    }

    return {
    create: create
    }
    });

    Here is the function from my client script which calls custom module:
    function getVendorNo(){
    try{
    var rec1 = currentRecord.get();
    var recordType = rec1.type;
    var rec = record.load({
    type: recordType,
    id: rec1.id,
    isDynamic: true,
    });
    var recordid =rec1.id;

    var options = {
    title: ‘Vendor Acknowledgement’,
    message: ‘Please enter the acknowledgement’,
    buttons: [{
    label: ‘Update’,
    value: 1
    },{
    label: ‘Cancel’,
    value: 2
    }],
    textarea: {
    rows: 6,
    cols: 48,
    isMandatory: true,
    caption: ‘Acknowledgement’,
    fieldId: ‘acknowledgement’,
    initialValue: rec.getText({fieldId: ‘entity’}),
    actionButtons: [1]
    },
    date: {
    rows: 1,
    cols: 40,
    isMandatory: true,
    caption: ‘Date’,
    fieldId: ‘_date’,
    initialValue: rec.getText({fieldId: ‘trandate’})
    },
    select: {
    rows: 1,
    cols: 40,
    isMandatory: true,
    caption: ‘Select’,
    fieldId: ‘_select’,
    list: [{value: 1, text: ‘ABC’}, {value: 2, text: ‘BCD’}, {value: 3, text: ‘CDE’}]
    }
    };

    var failure = function (reason) {
    console.log(‘Failure: ‘ + reason);
    }

    var success = function (result, value) {
    alert(‘Input dialog closed by clicking button ‘ + result + ‘ | value: ‘ + value);
    // Do something with the actual result here.
    }

    InputDialog.create(options, failure, success);

    }
    catch(error){
    console.log(‘Error in vendor acknowledgment: ‘+error.toString());
    }
    }

    If you were able to make it work, could you share your script example? Thanks!

  5. This is a great post! I love this type of thing.

    I’ll be looking into your module and creating my own. Maybe I use yours until my more specific to our account module is completed… or I use yours and add another module to use the other functionality I may need in the future.

    Huge help. Thank you!

  6. Thanks for your sharing!

    But I found one problem when I use the example in the blog,
    I got two “Reject” and two “Cancel” button;
    And when I update “_buildButtons” function to return ” instead, things back to its road.

  7. Any idea for NetSuite 2022.1 version, I found this app do not fix to 2022.1 version, that it had 2 “reject” and 2 “cancel” buttons, and the value in text area had not been caught!

    1. Were you able to get this to work in 2022.1? Mine was working yesterday but not today. Same issues you describe.

      1. Confirming that the 2022.1 release broke the input dialog. We’re working on a fix. Stay tuned!

        1. Hi David, FYI: the new version is live; the article has also been updated.

  8. Encountered the same issue afrer our account upgrade to 2022.1

    Hope this gets resolved. Thank you! 😊

  9. I’ve upgraded to 2.0 and it fixed the button issue but I can’t update my script as I’m getting this error now..

    “Module does not exist: N/utilityFunctions.js”

    Which is referenced in your script. I do not see this documented in netsuite either.

    1. Hi Jason,

      Are you sure you’re calling the module from a client script? N/utilityFunction is indeed not a public-facing API but used on client-side by the native NetSuite dialog so it should work fine unless you’re doing something on server-side which is defeats the purpose of a user-facing dialog. Of course, you can add a button in your user event script but the actual execution of the code must be on client-side.

      I hope this helps.

      Cheers

      1. I only get this error when I attempt to save changes to my client script.

        I have to temporary update your script to blank, then update my script and then update your script again with the original code.

        I do not get any errors at runtime.. it’s only when i try to update my script. I should mention I’m updating from inside Netsuite manually.

        1. Hi Jason, thanks for the additional information. That makes sense as NetSuite does additional validation via the UI. I reviewed the code and I can easily and safely remove that dependency on an internal module. I’ll do a minor release with that in the coming weeks. Be on the lookout and thanks for pointing this out. Cheers.

          1. Any update on this? I have the same issue as Jason.

            My solution has three parts

            1. Before Load to create a custom button. This script also attaches a client script that contains the button functions.
            2. Client script that will be called when button is clicked. This is where I am using the library.
            3. Suitelet that will called from client to update the record.

            I am getting the error: Module does not exist: N/utilityFunctions.js when my before load script executes. I believe this is because Netsuite is validating the client script I am trying to attach.

  10. Nice article. I like this solution, it’s so much cleaner than a horrible almost blank Suitelet page with only a few options.

    I’m looking at implementing something similar, except the client also wishes to have a dropdown single selection box, as an alternative to the text box.

    Saurabh Singh’s comment covers some of this, any other ideas? Thanks

    1. The current implementation does not but the code can be extended to support more controls, if you like.

  11. Hello! I’m running into a separate issue now with this module. My code is saved and it is working, but when I need to edit the script that uses the module, I’m getting this error:

    Fail to evaluate script: {“type”:”error.SuiteScriptModuleLoaderError”,”name”:”{stack=[Ljava.lang.Object;@4eaafb1a, toJSON=org.mozilla.javascript.InterpretedFunction@492ee809, name=MODULE_DOES_NOT_EXIST, toString=org.mozilla.javascript.InterpretedFunction@26458f46, id=, message=Module does not exist: N/utilityFunctions.js, TYPE=error.SuiteScriptModuleLoaderError}”,”message”:””,”stack”:[]}

    I noticed that the latest version for this module references N/utilityFunctions, but this module doesn’t exist (or no longer exists) within the Netsuite Help section. Has this changed?

    1. Hi,

      The issue you’ve reported has already been fixed. I believe you’re still on v2.0.0. Please upgrade to v2.0.1 via the link at the top of this article.

      Cheers

Leave a Reply

Your email address will not be published. Required fields are marked *

You deserve to know as soon as we share a new NetSuite Insight.
We won't disturb you with anything else or share/sell your data.