Dataviewer Library

What It Is

DataViewer is a library that helps manage the display of search results. It's most often used to display tables, but it can display lists as well. Essentially, DataViewer gives you a way to interact with and displaying your data regardless of source.

What It Does

With DataViewer, you can:

  • Execute a search using Kinetic Bridges, take input from other searches, or take input from fields on the screen
  • Display search results in a consistent format, including DataTable and Unordered Lists
  • Insert custom behavior through callbacks at several points during data search, display, and selection.
  • Field values can be set upon user selection of results

How DataViewer Improves Your Implementation

DataViewer provides a reusable and consistent way to build search functionality into a form and display the results. Commonly used behavior is included in the functionality (for example, the ability to set field values in a row or list selection), and results can be displayed as a table or list. You can also create custom renderers to display search results in an alternate format. The library is configuration driven, reducing the need to create JavaScript when creating simple tables from search results.

Installation

DataViewer is included in the default bundle provided by Kinetic Data. If you are using a custom bundle, you'll need to download and install DataViewer.

Download

First, download dataViewer.js from GitHub: https://github.com/KineticCommunity/library-dataviewer-ce.

Requirements

Along with dataViewer.js, the following items are required:

  • A JSON schema to define the Data Viewer configuration (in the item leveraging dataveiwer)
  • There may be additional requirements for Renderers that are used. Ex. the DataTables libraries from datatables.net for the table capability.

Including JavaScript and CSS

When including JavaScript and CSS files such as dataViewer.js we recommend adding them to the header of layout.jsp (or whatever file is rendering your services). Including files here ensures the files are accessible to all Kinetic Request Forms in the bundle.

Usage DataViewer

Bridged Resources

When using DataViewer for searching, a bridged resource defines the search and data returned from it. You must add a bridged resource to the Kinetic Request form if you plann to use the executeSearch function for FataViewer. The DataViewer configuration will reference the bridged resource you created.

Configuration

Form search and display configuration is defined by the form developer in a JSON schema. The schema contains the necessary information to define the bridge used by the search, the behavior of the search, and how to render the results. This configuration is passed to dataViewer as a parameter to execute the search.

You can configure and include multiple configurations in a Kinetic Request form.

Renderers

There are two renderers included with Data Viewer: the "DataTable" render and the "Unordered List" render. The renderers are part of the configuration and are used to determine how the search results will be displayed. You can also use a custom renderer to meet any unique requirements.

Execution

Data Viewer provides the following functions:

FunctionDescription
DataViewer.ucFirst(str)Returns the provided string with the first letter uppercased
DataViewer.executeSearch(destination, configObj)Executes the bridge search provided in the config object, the formats the search results as provided in the config object, and displays those results using the renderer in the config object in the destination specified.
DataViewer.renderFieldValues(destination, configObj)Adds the values from the fields specified in the configuration into the table/object specified. See image below for example.
DataViewer.renderResults(destination, configObj)formats the search results that are in the config object as provided in the config object, and displays those results using the renderer in the config object in the destination specified.

Example of setup that uses renderFieldValues:
RenderFieldValues

Destination (JQuery Object, JQuery Selector, or function).

Destination defines the location of where the search results (Table or List) are to be appended on the page The Destination may be supplied as a JQuery Object, JQuery Selector, function. If a function is provided, it must return a JQuery Object. (This is not the resultscontainerId specified in the Configuration but is where this element will be placed on the page.)

Configuration Object (JSON Object)

This is the Data Viewer Configuration Options created above in a JSON Object to define the behavior of the Data Viewer.

Examples:

//JQuery Selector
DataViewer.executeSearch('#Requested_For', searchConfig.personSearchBridgeTable);
//JQuery Object
DataViewer.executeSearch($('#Requested_For'), searchConfig.personSearchBridgeTable);

OR

DataViewer.renderFieldValues($(K('section[Results Section]').element()), searchConfig.personSearchBridgeTable);
//Function
DataViewer.executeSearch(function(){ return $(K('section[Requested For]').element());}, searchConfig.personSearchBridgeTable);

Styling

Custom styling may be applied to the results through the use of CSS. See Renderers for more information.

Configuration Option Details

  • resource: (nested JSON schema)
    • name:(String) Name of Bridge Resource
    • parameters: (Not used)
  • data: (Array of 1 or more nested JSON schemas) When using DataTabes column options may be passed through to it. More info at https://datatables.net/reference/option/columns
    • name: (String) Name matches to data returned in the results
    • title: (String)(Optional) Title is used for display purposes to identify the data to the user
    • visible: (Boolean)(Optional) whether the item displays in the result or is hidden, defaults to true
    • className: (String)(Optional) HTML class of the values DOM element
    • setField:(String)(Optional) A field on the Form to receive the selected value upon click.
    • defaultContent: (String) (Optional) A text or HTML string that will be the defaulted value in the displayed results. The value here will appear will all results sets. This can be used when the data to be displayed doesn't come from the search results.
    • render: (Function ( data, type, record )) (Optional) Used to modify the value of the data before display. The results returned from this function will be displayed in the results.
  • renderer: (nested JSON schema)
    • type: (Function) A function to render the results of the search. "KDSearch.Renderers.DataTables" and "KDSearch.Renderers.UnorderedList" included with the library. A custom function may also be supplied instead.
    • options: (Nested JSON schema) Values supplied here are specific to the Renderer and define the behavior of the Renderer.
      • before: (Function)(Optional) A function to execute before the search is performed
      • success: (Function)(Optional) A function to execute after the search is performed but before processing the data
      • successEmpty: (Function)(Optional) A function to execute after the search is performed when no results are returned
      • error: (Function)(Optional) A function to execute when an error is returned from the search
      • complete: (Function)(Optional) A function to execute after the search is performed and after the results are processed. Executes after success and error.
      • clickCallback: (Function) (Optional) A function to execute when the results are clicked. Returns element click (jQuery Obj), selected results (JSON)
      • resultsContainerId: (String) Supply an arbitrary value which will be applied to the DataTable element id when it is created. This value is be applied to the id attribute of the DataTable. Example:resultsContainerId: 'sampleTable' will result in: <table id="sampleTable" >
      • removeOnClick: (Boolean)(Optional defaulted "true") "true" or "false"  value to indicate if the search results should removed from display after a result is clicked.

Note that options not directly used by dataViewer will be passed on, unmodified, to the renderer used. This is particularly useful when other libraries, such as dataTables, are being used for the renderer.

Examples

Below is a configuration would be used with the renderResults function, because it has no bridge information and cannot be used with execute search (unless that is passed in dynamically).

searchConfig.addressTable = {
  resource: {
    name: ""
  },
  resultsContainer:
    '<table cellspacing="0", border="0", class="table table-striped table-bordered table-condensed dataTable">',
  data: [
    {
      name: null,
      orderable: false,
      className: "select-checkbox",
      defaultContent: "",
      width: "30px"
    },
    {
      title: "id",
      name: "address.id",
      visible: false
    },
    {
      title: "Location Name",
      name: "address.siteLocationName",
      orderable: true
    },
    {
      title: "GEOLOC Code",
      name: "geoloc.code",
      visible: true
    },
    {
      title: "Address",
      name: "address.streetAddress",
      visible: true
    },
    {
      title: "City",
      name: "address.city",
      visible: true
    },
    {
      title: "State",
      name: "address.state.abbreviation",
      visible: true
    },
    {
      title: "Zip Code",
      name: "address.zipCode",
      visible: true
    },
    {
      title: "Country",
      name: "address.state.countryId",
      visible: true
    }
  ],
  resultsContainerId: "address-table",
  before: function before(configObj) {},
  success: function success(configObj) {},
  error: function error(configObj) {},
  complete: function complete(configObj) {},
  removeOnClick: false,
  renderer: {
    type: DataViewer.Renderers.DataTables, // Passing a function here allows for better customization
    options: {
      // Options for Reneder
      processSingleResult: true,
      // DataTable OPTIONS; Passing options here make it clear that they are being passed to data tables
      // responsive: OPTIONAL Default for "BridgeDataTable" is true but can be over written.
      responsive: false,
      dom: "ftip",
      order: [[2, "asc"]],
      paging: true,
      pageLength: 20,
      lengthChange: false,
      deferRender: true,
      scrollCollapse: true,
      select: {},
      createdRow: function createdRow(row, data, dataIndex) {},
      initComplete: function initComplete() {}
    }
  }
    },

This example does have a bridge config and executed as shown below the configuration:

//put existing, formatted info into the search config
 searchConfig.addressTable.response = addrArray;
 //render this information
 DataViewer.renderResults(function() {
     return $(K('content[Addr Table]').element());
 }, searchConfig.addressTable);


 searchConfig.personTable = {
     resource: {
         name: 'people by Organization Id'
     },
     resultsContainer: '<table cellspacing="0", border="0", class="table table-striped table-bordered table-condensed dataTable">',
     data: [{
         title: 'id',
         name: 'id',
         visible: false,
     }, {
         title: 'Title',
         name: 'title'
     }, {
         title: 'Name',
         name: 'fullName',
         render: function render(data, type, row) {
             if (type === "display") {
                 if (row['IsNew'] && row['IsNew'] !== 'true') {
                     return data + " <i style='color:purple;' class='fa fa-user' aria-hidden='true'></i>";
                 } else if (bundle.helpers.nonRefDataExcludeUsers.indexOf(row['userId']) < 0) {
                     return data + " " + bundle.helpers.createNonRefDataTooltip({
                         "dateCreated": moment(row['dateCreated']).format('YYYY-MM-DD HH:mm:ss.SSS'),
                         "dataModified": moment(row['dataModified']).format('YYYY-MM-DD HH:mm:ss.SSS'),
                         "userFullName": row['userId'],
                         "userOrg": row['userOrg'],
                         "userRole": row['userRole']
                     });
                 } else {
                     return data;
                 }
             } else {
                 return data;
             }
         }
     }, {
         title: 'Organization',
         name: 'Org',
     }, {
         title: 'Action',
         name: null,
         render: function render(data, type, row) {
             var buttons = "<div class='action-buttons'><button class='action-button action-button-view btn btn-xs'>View</button> <button class='action-button action-button-remove btn btn-xs'>Remove</button></div>";
             return buttons;
         }
     }],
     resultsContainerId: 'person-table',
     before: function(configObj) {
         $('.message').show();

     },
     error: function(configObj) {},
     complete: function(configObj) {
         $('.message').hide();
         $(K('content[Person Table]').element()).find('.message').hide();

         //repopulate Person Summary that holds the JSON data representing the table.
         K('field[Person Summary]').value(JSON.stringify(configObj.tableObj.data().toArray()));
         K('field[Person Summary]').trigger('change');

         // On View Button
         configObj.tableObj.on('click', 'button.action-button-view', function() {
             var row = $(this).parents('tr'),
                 data = configObj.tableObj.row(row).data();
             bundle.helpers.personReview(data);
         });

         // On Remove Button
         configObj.tableObj.on('click', 'button.action-button-remove', function() {
             var row = $(this).parents('tr'),
                 data = configObj.tableObj.row(row).data();
             configObj.tableObj.row(row).remove().draw();
             //repopulate Person Summary that holds the JSON data representing the table.
             K('field[Person Summary]').value(JSON.stringify(configObj.tableObj.data().toArray()));
             K('field[Person Summary]').trigger('change');
         });
     },
     removeOnClick: false,
     renderer: {
         type: DataViewer.Renderers.DataTables, // Passing a function here allows for better customization
         options: {
             // Options for Render
             processSingleResult: false,
             // DataTable OPTIONS; Passing options here make it clear that they are being passed to data tables
             // responsive: OPTIONAL Default for "BridgeDataTable" is true but can be over written.
             responsive: false,
             dom: 'tip',
             order: [
                 [3, 'asc']
             ],
             paging: false,
             scrollY: '60vh',
             scrollCollapse: true,
             select: {
                 style: 'single',
                 selector: 'td:first-child'
             },
             createdRow: function createdRow(row, data, dataIndex) {
                 $(row).find('a[name="nonRefData"]').popover({
                     trigger: 'hover',
                     container: 'body'
                 });
             },
             deferRender: true,
         }
     }
 }
 };

Example calls for the above configuration:

  DataViewer.executeSearch(function () {
  return $(K('content[person Table]').element());
  }, searchConfig.personTable);

or this way

  searchConfig.personTable.response = pocArray;
   DataViewer.renderResults(function () {
  return $(K('content[person Table]').element());
  }, searchConfig.personTable);