Leveraging Open-Source Libraries and the MVC Design Pattern in NetSuite

03/22/2016

Most experienced SuiteScript developers utilize custom Suitelet or Portlet solutions to expand the NetSuite user interface beyond what it does natively.  Suitelets, in particular, allow developers to build custom pages using AJAX to enable a dynamic custom experiences and flows. NetSuite provides rich out-of-the-box functionality within the SuiteScript API library, but some circumstances call for a custom solution that can go beyond standard behaviors to produce an even more dynamic experience.

Generally, when beginning to build a UI flow in SuiteScript, we start with the standard nlobjList and nlobjForm objects. These APIs are documented well and jQuery is easy to include. However, in some cases there is a desire to use Open Source libraries other than jQuery in an application, or in cases when the standard NetSuite fields are too limiting.

In this post, we present a technique for customizing Suitelets to create a rich HTML5 experience inside NetSuite.  This approach leverages open source libraries to rapidly create fully customized applications beyond the confines of SuiteScript UI controls and behavior.  We also present a way of utilizing this approach using the very common Model-View-Controller pattern to better structure code.

Before we dive into our approach, let’s first take a look at how front-end forms in Suitelets are generally developed today.  Conventional Suitelet development, which we refer to as the ‘nlobjForm Approach’, uses three primary steps:

  1. First, instantiate an nlobjForm object.
  2. Second, add elements natively to this object using methods such as nlobjForm.addField() or nlobjForm.addButton().
  3. Finally, output the form with the response.writePage() API function.

This process is very straightforward, but as we have stated, limited.  Developers can add custom AJAX functionality on top of the form in a client-side script, but the NetSuite look and feel remains on all form elements (e.g. drop-downs and text boxes).  It is extremely difficult to control the layout of your form, incorporate CSS, override NetSuite form controls, or use custom UI controls; NetSuite tightly controls how the page is rendered to the browser.

To overcome these limitations, the approach we use relies on creating an HTML file template and storing this in the File Cabinet along with any supporting JavaScript/CSS library files.  Using an HTML template, we gain control of the entire page layout and are able to dynamically inject code and content into sections of the HTML template by replacing substrings in the HTML template itself.  We can also develop the look and feel of the HTML page separately with CSS outside of NetSuite.  In the main Suitelet function we load the contents of the HTML file into an inline html field and render to the browser using response.writePage().  We can have our page take up the entire screen or sit beneath the standard NetSuite menu using this approach.  Figure 1 depicts how the back-end Suitelet sits on top of the NetSuite database and serves up data to the Front End Suitelet or Client Script.  The Front-End Suitelet loads in the HTML template file and any external dependencies such as Images or CSS for the application.  After the page is loaded all the interactive programming is handled by the Client Script, which accesses elements loaded by the Suitelet.

techfino-custom-suitelet-development-stack
Figure 1. Custom Suitelet Development Architecture

From an architecture perspective, this development approach can actually be implemented using the Model-View-Controller (MVC) Design Pattern.  Here, the Model can be represented by any custom record or any other object stored inside NetSuite.  The Front-End Suitelet acts as the View serving up the requested server-side page rendered using the static files (HTML template file, CSS, scripts and images).  The Controller is implemented by adding a client script to the page from the Suitelet (using the API function setScript()) to allow AJAX callbacks to the Back-end Suitelet which ties the front-end user actions to the back-end server functionality in NetSuite.  The Development Stack shown in Figure 1 illustrates each of the elements of this solution.

As an example, we will look at how we can incorporate the Harvest open source JavaScript library called 'Chosen', which offers an excellent solution for searching and styling drop-down lists that are available for both jQuery and prototypes.  In this example, we show a simple application to allow the user to select a country from a list read dynamically from the NetSuite database and submit their selection to our back-end Suitelet would work.  The renderForm() Suitelet function shown below reads the values of the HTML template and CSS/JavaScript file IDs from script parameters and starts by loading the HTML file.  Next, the script creates the Suitelet form.  After that, we show how we can include the external stylesheets and JavaScript files before adding the new inline HTML field containing the contents of the HTML file.  In this particular example, we simply have an HTML page with a type-ahead sub-string matching drop-down control on the page.  To implement this, we also have to include the Chosen JavaScript and CSS file in the Suitelet script below as Script Parameters which are the internal ID’s of these files in the File Cabinet.

function renderForm(request, response) {
    var context = nlapiGetContext();
    var HTMLFileId = context.getSetting('SCRIPT', 'custscript_html_file_id');
    var chosenCSS = context.getSetting('SCRIPT', 'custscript_chosen_css');
    var clientJS = context.getSetting('SCRIPT', 'custscript_client_js');
    var chosenJS = context.getSetting('SCRIPT', 'custscript_chosen_js');
    var htmlFile = nlapiLoadFile(HTMLFileId);
    var htmlContent = htmlFile.getValue();
    var form = nlapiCreateForm('My Custom Form');
    form
        .addField(‘chosen_css’, 'inlinehtml') // Chosen CSS File
        .setDefaultValue('');
    form
        .addField('js_chosen', 'inlinehtml') // Chosen.js Client Script
        .setDefaultValue('');
    form
        .addField('js_client', 'inlinehtml') // Custom Client Script
        .setDefaultValue('');
    var inlineField = form.addField('custpage_div', 'inlinehtml');
    inlineField.setDefaultValue(htmlContent); // Loads the HTML template into the page
    response.writePage(form);
}

Below is the contents of the index.html file, what we refer to as the HTML Template file. Note that we're just providing a simple drop-down list HTML control for chosen to be added to.

<div id="theme-netsuite">
    <div class="page-content">
        <div class="page-container">
             <div class="page-header">
                 <div class="page-title">
                    <h5>Countries Chosen Control Test
                </div>
            </div>
            <div class="page-content">
                <form id="testForm">
                    <div class="section-container">
                        <div class="form-field">
                            <label for="select_element">Countries Selected
                            <span class="select">
                                <select class="chosen countries" name="countries" id="countries">
                                    <option value="" disabled selected>Select an option
                                </select>
                            </span>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

We can then implement a Controller as the entry point into the application.  The function below shows how to call different Views which can render new pages, or even backend functionality from the same Suitelet.  Here, ‘ViewPage1’ takes the user to a simple form containing a Country drop-down control;  we implement this as the first page the user visits when accessing the application, and when the form is submitted the form it directs the user to the ‘ViewPage2’ page to enter in additional information and continue the process of data entry.  On the front-end, if there is a need to load a drop-down list containing a list of countries stored as a record in NetSuite, we can call ‘getCountries()’ to retrieve the list countries containing the id, value pair of countries when the page loads or asynchronously with an AJAX call to the controller.

function myController(request, response) {
    var requestType = request.getParameter('requesttype');
    switch (requestType) {

        case 'ViewPage1':
            renderForm(request, response);
            break;

        case 'ViewPage2':
            renderForm(request, response);
            break;…;

        case 'getCountries':
            getCountryList(request, response);
            break;
    }
}

Using the nlapiRequestURL asynchronous callback or a jQuery AJAX call, we can quickly write an asynchronous method that loads the Chosen drop-down independent of the page’s control flow:

(function() {
    var requestParam;
    requestParam["requesttype"] = "getCountries";
    nlapiRequestURL(data_url, parameters, null, function(response) { // or could use jQuery instead
        if (response.getCode() != 200) {
            // request is not successful
            ...Error Handling...
        }
        try {
            var data = JSON.parse(response.getBody());
        } catch (error) {
            // body is not parsable
            ...Error Handling...
        }
        var countries = $(“.countries”);
        for (var i = 0; i & lt; data.length; i++) {
            countries.append(“”+data[i].text + ””);
        }
        countries.chosen();
    });
});

Calling the controller with parameter ‘ViewPage1’, we render a HTML template containing a simple Country drop-down applying the Chosen Harvest behavior on our drop-down control.  Now we could request the list of countries statically when we load the page from the back-end Suitelet, but in this case we decided to load the drop-down shown in Figure 2 asynchronously with a call to our ‘getCountries()’ back-end Suitelet function in case we want to get an updated list of countries without reloading the entire page.  As shown below, as the user types the control responds dynamically showing which values in the select drop-down list match the substring as the user types:

chosen-dropdown-blog
Figure 2. Custom Suitelet rending an open-source UI Control

Some things to note, this technique is largely applicable for problems where standard NetSuite forms are not optimal for a customer’s process or when data entry becomes cumbersome and slow.  As a best practice, it is strongly recommended to use your own namespace for JavaScript functions and CSS/HTML elements added to the DOM to ensure future compatibility with NetSuite upgrades and no conflicts with NetSuite.

In conclusion, our development approach allows NetSuite customers and other SuiteScript developers to take full advantage of the NetSuite Platform-as-a-Service to build truly customized applications without any limits.  This development stack allows for the use of popular open-source JavaScript libraries to rapidly add new functionality and customization in NetSuite that would otherwise be very difficult and time-consuming.  In a future post, we will provide a real-world example along with source code that demonstrates exactly how to use this approach to build a fully custom List View screen using the DataTables.net Open Source Library.  With DataTables you can develop a Suitelet or Portlet to allow for inline searching and sorting on a large data table without having to post back to the server or make heavy use of Saved Searches.

We hope you find this customization technique helpful in leveraging the full power of the NetSuite platform to meet the ever-challenging needs of businesses today.  Stay tuned for more tips and techniques.

Happy Coding!

Get Started Now 

The easiest way to get started is to contact Techfino today. If you’d like a little more information first, you can download our ContinuedSuccess Whitepaper. Either way, we hope you’ve found this guide helpful and hope that we can further assist you on your path to leveling up your NetSuite Support.