JavaScript Functions for Tables

Click the paperclip at the top of the page to access and download a sample application that demonstrates these functions in action.


Check Table for Entry

Checks to see if the specified text exists anywhere (any cell, any row) within the table. If the string is found then the function returns true, otherwise false.

This is a really helpful function if you want to determine available features based on the existence of an item within a table.  For example, you might want to make sure that a user can only enter text into the table once.  This function would enable you to provide that functionality. 

1. Copy the function code to the Settings...Events...onStart section of your application.

// table: This is the actual table object, not the string ID (e.g. BO.F_Table)
// entry: this is the value of what you want to search for, the content of the table column will be converted to a string
// usage: This is usually called within the onClick of a button that adds rows to a table.  Or you might want to search to see if a record exists in the table.

app.getSharedData().checkTableForEntry = function(table, entry) {
  var entryFound = false;

  for(var i = 0; i < table.getLength(); i++) {
      var row = table.get(i);
      var tmpentry = entry;

      //loop through all the columns of the source table
      var cols = row.getChildren();
      for(var j=0;j<cols.getLength();j++) {
          var fld = cols.get(j);
          var c = fld.getValue();
  
          if(typeof c === "string") {
            if(c.indexOf(tmpentry) !== -1) {
              return true;
            }
          } else if(fld.getType() === "date" || fld.getType() === "timeStamp") {
            var entryAsDt = new Date(entry);
            if(entryAsDt.getYear() === c.getYear() && entryAsDt.getMonth() === c.getMonth() && entryAsDt.getDay() === c.getDay()) {
              return true;
            }    
          } else {

            //Convert to string for comparison             
            c = c.toString();            

            if(c === entry) {
              return true;
            }
          }
        } //inner for
      } //outer for
  return entryFound;
}

2. This type of feature is typically called within the onClick event of a button that adds a row to the table.

var entryFound = false;
entryFound = app.getSharedData().checkTableForEntry(BO.F_Table, theSearchValue);

or

if(!app.getSharedData().checkTableForEntry(BO.F_Table, theSearchValue)) {
     //the string was NOT found in the table, so now do something!
}


Check Table for Entry By Column

This function checks a specific column of a table for the specified string.  If the string is found then the function returns true, otherwise false. 

1. Copy the function code to the Settings...Events...onStart section of your application.

// table: This is the actual table object, not the string ID (e.g. BO.F_Table)
// column: This is the string of the columnID that you want to seach
// entry: this is the value of what you want to search for, the data type should match that of the column you are searching (i.e. string, int, date)
app.getSharedData().checkTableForEntryByColumn = function(table, column, entry) {
  var entryFound = false;

  for(var i = 0; i < table.getLength(); i++) {
    var row = table.get(i);
    var tmpfld = get(row,column);
    var tmpval = tmpfld.getValue();
    if(typeof tmpval === "string") {
      if(tmpval.indexOf(entry) !== -1) {
        entryFound = true;
        break;
      }
    } else if(tmpfld.getType() === "date" || tmpfld.getType() === "timeStamp") {
      var entryAsDt = new Date(entry);
      if(entryAsDt.getYear() === tmpval.getYear() && entryAsDt.getMonth() === tmpval.getMonth() && entryAsDt.getDay() === tmpval.getDay()) {
       entryFound = true;
       break;
      }    
    } else {
      tmpval = tmpval.toString();
      if(tmpval === entry) {
        entryFound = true;
        break;
      }
    }
  }
  return entryFound;
}

2. To use this function you need to decide where and how you want to check the value that the user might be trying to add. When you call the function you would use the following statement:

app.getSharedData().checkTableForEntryByColumn(BO.F_theTableObj, 'F_theColumnToSearch', 'theValueYouAreSearchingFor');

Get First Table Row that Contains Entry By Column

1. Copy the code to the Settings...Events...onStart section of your application.

app.getSharedData().getTableRowThatContainsEntry= function(table, column, entry) {
  var tblRow = null;
  for(var i = 0; i < table.getLength(); i++) {
    if(get(table.get(i),column).getValue() === entry) {
      tblRow = table.get(i);
      break;
    }
  }

  return tblRow;
}


2. To use this function you need to decide where and how you want to check the value that the user might be trying to add. When you call the function you would use the following statement:

var row = app.getSharedData().getTableRowThatContainsEntry(BO.F_theTableObj, 'F_theColumnToSearch','theValueYouAreSearchingFor');
if(row !== null) {
 //do something with the row
}

Count Table Rows that Contain Entry By Column

Returns the number of rows that contain the specified entry.

1. Copy the code to the Settings...Events...onStart section of your application.

// table: This is the actual table object, not the string ID (e.g. BO.F_Table)
// column: This is the string of the columnID that you want to seach
// entry: this is the value of what you want to search for, 
// the data type should match that of the column you are searching (i.e. string, int, date)
app.getSharedData().countTableRowsForEntry = function(table, column, entry) {
  var entryFoundCount = 0;
  for(var i = 0; i < table.getLength(); i++) {
    var r = table.get(i);
    var fld = get(r,column);
    var val = fld.getValue();
    
    if(typeof val === "string") { 
      if(val.indexOf(entry) !== -1) {
        entryFoundCount++;
      }
    } else {
      if(val.toString() === entry) {
        entryFoundCount++;
      }
    }
  }
  return entryFoundCount;
}

This function as written will not all using a date or timestamp field as the search modifier (ColumnB).  If you want to enable it to support dates then it will need to be modified to include the instanceOf logic that may be found in the checkTableForEntry function.


2. To use this function you need to decide where and how you want to check the value that the user might be trying to add. When you call the function you would use the following statement:

app.getSharedData().countTableRowsForEntry(BO.F_theTableObj,'F_theColumnToSearch','theValueYouAreSearchingFor');

Calculate Sum of ColumnA Where ColumnB Meets Specified Criteria

Returns the sum of columnA for all the rows where columnB matches the specified entry.  For example: Sum all the 'currency fields' in the rows where dropdown = 'approved'

1. Copy the code to the Settings...Events...onStart section of your application.

// table - pass the table object - BO.F_Table
// columnA - the string ID of the field to be part of the summation
// columnB - the string ID of the field used to determine if the row should be included
// entry - the string value to compare against columnB
app.getSharedData().sumColumnA_searchByColumnB = function(table, columnA, columnB, entry) {  
  var sum = 0;
  for(var i = 0; i < table.getLength(); i++) {
    var r = table.get(i);
    var fld = get(r,columnB);
    var val = fld.getValue();

    if(typeof val === "string") { 
      if(val.indexOf(entry) !== -1) {
        sum += parseFloat(get(r,columnA).getValue());
      }
    } else {
      if(val.toString() === entry) {
        sum += parseFloat(get(r,columnA).getValue());
      }
    }
  }
  return sum;
}

This function as written will not all using a date or timestamp field as the search modifier (ColumnB).  If you want to enable it to support dates then it will need to be modified to include the instanceOf logic that may be found in the checkTableForEntry function.

2. This function assumes that you are trying to sum a set of rows of a table where a field in those rows meets a specified criteria.  Copy the function into the Settings...Events section of your application.

To use this function you will need to place the following in the onShow event of the field where the sum is being shown, as well as the onAdd and onRemove events of the table: 

BO.F_Currency4.setValue(app.getSharedData().sumColumnA_searchByColumnB(BO.F_Table, 'F_Currency', 'F_DropDown4', 'Approved'));

Copy Table Contents to another Table

Copy all the rows from one table to another.  This assumes that the tables have the same number of rows and it will copy the columns in order.  There is no error checking so if you try to copy a datatype that does not match it may throw a run-time error.

// sourceTable - The table object you want to copy (e.g. BO.F_Table)
// targetTable - The table object you want to copy into (e.g. BO.F_Table1)
// clearTable - true/false - If true will empty the target table before copying.
app.getSharedData().copyRowsToAnotherTable = function(sourceTable, targetTable, clearTable) {
    if(clearTable) {
        targetTable.setValue(new Array());
    }

  for(var i = 0; i < sourceTable.getLength(); i++) {
      var row = sourceTable.get(i);
      var newRow = targetTable.createNew();

      //loop through all the columns of the source table
      var cols = row.getChildren();
      for(var j=0;j<cols.getLength();j++) {
          //get the row object of the target table

          var newCols = newRow.getChildren();

          //for each row of the source table we set the value into the same row of the target table
          newCols.get(j).setValue(cols.get(j).getValue());
      }
        targetTable.add(newRow);
  }
}

USAGE: In the event where you want to trigger the function place code like:

app.getSharedData().copyRowsToAnotherTable(BO.F_Table, BO.F_Table0, true);

Duplicate Selected Row and Append to Same Table

This function will duplicate the selected row of a table and then append it to the end of the table.

1. Copy the function to the Settings...Events...onStart or Form onLoad section of your application.

// theTable - The table object you want to copy (e.g. BO.F_Table)
// theRow - the row to duplicate
app.getSharedData().duplicateSelectedRow = function(theTable, theRow) {
      var newRow = theTable.createNew();

      //loop through all the columns of the source table
      var cols = theRow.getChildren();
      for(var j=0;j<cols.getLength();j++) {
          //get the row object of the target table
          var newCols = newRow.getChildren();

          //for each row of the source table we set the value into the same row of the target table
          newCols.get(j).setValue(cols.get(j).getValue());
      }
        theTable.add(newRow);
}

2. Call the function by placing the following code in the desired event (i.e. onClick of a button or the table iteself):  

app.getSharedData().duplicateSelectedRow(BO.F_Table, page.F_Table.getSelection());

Move a Table Row Up One Position

Change the order of the table by moving a row up one position.

// theTable: the BO object of the table
// theRow: the BO object of the row to be moved
app.getSharedData().moveSelectionUp = function(theTable, theRow) {
    var rowObjs = new Array();
    var rowMoved = false;

    //remove all rows from the table
    //store each row in a separate array
    while(theTable.getLength() > 0) {
        rowObjs.push(theTable.get(0));
        theTable.remove(theTable.get(0));
    }
    for(var i = 0; i < rowObjs.length; i++) {
        var firstRow = get(rowObjs, i);
        var secondRow = get(rowObjs, i+1);

        if(!rowMoved) {
            if(secondRow === theRow) {
                //add second row first, then the current row
                theTable.add(secondRow);
                i++; //increment counter again because we just processed two rows in one loop
                rowMoved = true;
            }
        }
        theTable.add(firstRow);
    }
}

Place in a button's onClick event. Clearing the selection after moving a row is critical otherwise errors may insue.

app.getSharedData().moveSelectionUp(BO.F_InputParameters, page.F_InputParameters.getSelection())
page.F_InputParameters.setSelection(-1);

Move a Table Row Down One Position

Change the order of the table by moving a row down one position.

// theTable: the BO object of the table
// theRow: the BO object of the row to be moved
app.getSharedData().moveSelectionDown = function(theTable, theRow) {
    var rowObjs = new Array();
    var rowMoved = false;
    //remove all rows from the table
    //store each row in a separate array
    while(theTable.getLength() > 0) {
        rowObjs.push(theTable.get(0));
        theTable.remove(theTable.get(0));
    }
    for(var i = 0; i < rowObjs.length; i++) {
        var firstRow = get(rowObjs, i);

        // check to make sure we are not the last row, if we are just put the row back in the table
        if(i < rowObjs.length - 1) {
          var secondRow = get(rowObjs, i+1);
          if(!rowMoved) {
            if(firstRow === theRow) {
                //add second row, then the current row
                theTable.add(secondRow);
                i++; //increment counter again because we just processed two rows in one loop
                rowMoved = true;
            }
          }

        }
        theTable.add(firstRow);
    }
}

Place in a button's onClick event. Clearing the selection after moving a row is critical otherwise errors may insue.

app.getSharedData().moveSelectionDown(BO.F_InputParameters, page.F_InputParameters.getSelection())
page.F_InputParameters.setSelection(-1);

Function:

Get Index of Row in Table

Description:

This is a function that will allow you to determine the index of a specific row in a table.  Now the intention of this function is to use it in conjunction with the table.getSelection() which returns the "selected" row, by passing that into this function you can get back the "index" of that row in the table.

// theTable: the BO object of the table
// theRow:   the BO object of the row to be moved
app.getSharedData().getIndexInTable = function(theTable, theRow) {   

   if(theTable !== null && theRow !== null) {

       for(var i=0; i<theTable.getLength(); i++) {
           var tr = theTable.get(i);
           if(tr === theRow) {
               return i;
           }
       }
    }
    return -1; //if we get here then the row was not found in the table
}

The use case that birthed this function is that a user wanted to be able to set some table fields to read-only if the selected row was the "last" row in the table.  The row business object does not know anything about its position in its parent table, therefore we have to walk the table comparing the selected row against each one that we find.  So in the aforementioned example the function was used like (the code was placed in the onShow event of a field in the table):

var inx = app.getSharedData().getIndexInTable(app.getForm('F_Form1').getBO().F_Table1,app.getSharedData().selectedRow);

if(inx === app.getForm('F_Form1').getBO().F_Table1.getLength() - 1) {
      item.setActive(false);  //this is the current item
      //this is how you would reference another item in the same table      
      page.F_SingleLine2.setActive(false);
    }

Calling the function from within a table (we have to begin the reference from the app level to get the right object):

var inx = app.getSharedData().getIndexInTable(app.getForm('F_Form1').getBO().F_Table1,app.getSharedData().selectedRow);

Calling the function from within a field on the form:

var inx = app.getSharedData().getIndexInTable(BO.F_Table1,page.F_Table1.selectedRow);

 Print The Contents of a Table

I created this function because I wanted a quick way to render the content of a table, that comes back from a service call, in a pure text format.  This is an iterative function that walks all the rows and columns of a table and converts its content to a string.  This is just an example of the type of thing that could be accomplished.  Ideally you would take the premise and expand it for your situation.  You may want to add more detailed html or css to really fine-tune what the printed result looks like.

app.getSharedData().printTable = function(table) {
var s = "<p>"; //can change your html properties here

  //loop through all the table rows
  for(var i = 0; i < table.getLength(); i++) {
    var row = table.get(i); //get the first row
    var ol = row.getChildren(); //get the list object that contains the row fields
    for(var j=0;j<ol.getLength();j++) {
     var f = ol.get(j);
     var v = f.getValue();
     s += f.getId() + ": " + v + "<BR />"; //this controls the formatting of each column in the table row
    }
    s += "<BR /><BR />"; //this controls what happens when we move to the next row
  }
  s += "</p>"; //close the html elements that were started at the beginning
  return s;
}

The most common use case would use this function to display the table content in an HTML Area so that you can apply your own custom HTML to provide the most detailed formatting.  You could also place the content in a Text item but you would not have any control over the formatting (i.e. applying bold, italic, etc).

If you are using this as I did, where a service populates a table then you will want to do the processing after the service completes, something like:

var srv = form.getServiceConfiguration('SC_ServiceConfig0');
srv.connectEvent("onCallFinished", function(success)
{
  if(success) {
    form.getPage('P_NewPage').F_HTMLArea.setContent(app.getSharedData().printTable(BO.F_Table1));
  }
});

There are a few things I should mention:

1. As of 8.6.0 there is no way to get the title of a field using the JS API and so I resort to giving each field an id that matches its content.  If I wanted I could then further modify my JS function to change the output based on the ID found, this is not ideal and we are adding JS functions to get field titles in a future version.

2. You can modify the function in any way to add your own HTML or css references to "beautify" the content.  Here is an image of me using the function in one of my sample apps (that also happens to ask our dear friend Watson about the location of "Sleepy Hollow"!)...


Convert Table to JavaScript Object

This function will convert a FEB table into a javaScript object that you can then reference within your javaScript code.

app.getSharedData().convertTableToObj = function(table) {
  //setting the global table properties
  var tblObj = {rows: new Array(),id: table.getBOAttr().getId(),length: table.getBOAttr().getLength(),type:table.getBOAttr().getType(), active: table.getActive(), title: table.getTitle(), visible: table.getVisible(), hintText: table.getHintText(), hoverText: table.getHoverText(), cssClasses: table.getClasses()};

  //loop through all the table rows to create the rows object
  for(var i = 0; i < table.getBOAttr().getLength(); i++) {
    var rowObj = {};
    var row = table.getBOAttr().get(i); //get the first row
    var ol = row.getChildren(); //get the list object that contains the row fields
    for(var j=0;j<ol.getLength();j++) {
     var f = ol.get(j);
     var v = f.getValue();
     set(rowObj,f.getId(), v);
    }
    tblObj.rows.push(rowObj);
  }
  return tblObj;
}

Pass the UI object of the table to the function:

var obj = app.getSharedData().convertTableToObj(page.F_Table1);

The table object has the following structure:

{rows:[],
id:"F_Table1",
length:0,
type:"BusinessObjectList",
active:true,
title:"Table",
visible:true,
hintText:"",
cssClasses:[]}

rows is an array that will contain all the field ids and their stored values, for example:

"rows":[{"F_SingleLine2":"Chris","F_Paragraphtext1":"This is a multi-line text field",
"F_Date2":"2017-02-22T08:00:00.000Z","F_Number1":12,"F_DropDown1":"red","F_SelectMany1":"deer__#__bear"}]

You could then convert this object to JSON:

toJson(obj)