Categories: Servoy Mastery

Add Forms to Tab Panels using a Map

This is a Servoy tutorial that demonstrates how to add a form to a tab panel using a map. It’s a useful technique for controlling exactly where the new form should be placed among other forms in the tab panel, that may or may not be showing.

Photo Credit: jegeor via Compfight.

In the real-world, users click options, and depending on the option, a new form may need to be added to the a tab panel that will contain all the relevant information. For example, assume we are working on a contact record that has a tab panel showing additional information in the lower half of the layout. If the user clicks an option to set the contact type to “Sales Rep”, then maybe a new form should be added to the tab panel that contains additional info for sales reps, like commission, territory, etc. However, the new form typically needs to appear in a logical position, and not just get added to the end. It also should not be there if the contact type is not a sales rep. This is where a tab panel map comes into play.

A tab panel map is nothing more than a literal array with tab names. The tab names are added to an array in the logical order that we would want them to appear, and not in alphabetical order. For example, maybe the sales rep tab should be shown after the address tab, but before the comments tab. I have seen all kinds of implementations to do this, but nothing could be simpler then just using a simple map of names in the order you want.

Shown below is how to setup a map for a tab panel with the tab names you are going to use, and in the logical order you want them to appear. In our example, we are using i18n keys for the tab names.

var _tabMap = [
 i18n.getI18NMessage("lbl.tabD"),
 i18n.getI18NMessage("lbl.tabC"),
 i18n.getI18NMessage("lbl.tabA"),
 i18n.getI18NMessage("lbl.tabB")
];

The utility method shown below is used to add a tab form to a tab panel. It accepts all the necessary parameters, ensures the tab does not already exist, and adds it immediately after the first tab that comes before it in the tab map. If no existing tab is found that comes before the new tab being inserted, then the new tab is inserted at the end. Using this utility, tabs will be inserted precisely according to the tab map, regardless of the order in which they are added.

/**
 * Add a tab according to the tab map
 *
 * @author Gary Dotzlaw - Dotzlaw Consulting
 * @since 2013-11-23
 *
 * @param {String}  sForm - The base form to add the tab to (where the tab element is)
 * @param {String}  sTab - The name of the tab
 * @param {String}  sTabForm - The name of the tab form to be added
 * @param {String}  sTabLabel - The label for the tab (i18n key like "lbl.tabName")
 * @param {String}  sTabRelation - The relationship for the tab
 * @param {Array}  aTabMap - The map for tab order (uses sTabLabel for key)
 *
 * @return {Boolean} TRUE or FALSE
 */
function utils_tabAdd(sForm, sTab, sTabForm, sTabLabel, sTabRelation, aTabMap)
{
 var i = 0,
  k = 0,
  iMax = forms[sForm].elements[sTab].getMaxTabIndex(),
  bTab = false,
  aCurrentTabLabels = [],
  iFoundIndex = -1,
  bFound = false,
  sCurrentTabLabel = "";

 // Get the all the tab names that exist
 for (i = 1; i <= iMax; i++){
  aCurrentTabLabels.push(forms[sForm].elements[sTab].getTabTextAt(i));

  // Check to see if the tab we want to add already exists
  if (i18n.getI18NMessage(sTabLabel) == aCurrentTabLabels[i-1].replace(/ /g, " ")) bTab = true;
 }

 // Tab was not found, so we need to add it
 var iTabIndex;
 if (!bTab && aTabMap){
  // Find where it is supposed to be in the tab map
  iTabIndex = aTabMap.indexOf(i18n.getI18NMessage(sTabLabel));

  // Now search to find any tab that is supposed to be before this one, and insert the new tab after it
  for (k = iTabIndex; k >= 0; k--){
   if (!bFound){
    iMax = aCurrentTabLabels.length;
    for (i = iMax; i >= 1; i--){
     // Clean up the tab name for webclient
     sCurrentTabLabel = aCurrentTabLabels[i-1].replace(/ /g, " ");

     if (sCurrentTabLabel == aTabMap[k]){
      iFoundIndex = i;
      bFound = true;
      break;
     }
    }
   }
  }

  if (iFoundIndex > -1){
   // Add the new tab at the found location
   bTab = forms[sForm].elements[sTab].addTab(forms[sTabForm], null, i18n.getI18NMessage(sTabLabel), null, null, null, null, sTabRelation, iFoundIndex);
  }
 }

 if (!bTab){
  // A tab that the new one needs to go after was never found, so add it to the end
  bTab = forms[sForm].elements[sTab].addTab(forms[sTabForm], null, i18n.getI18NMessage(sTabLabel), null, null, null, null, sTabRelation, forms[sForm].elements[sTab].getMaxTabIndex() + 1);
 }

 return bTab;
}

This utility is used to simply remove the tab panel by i18n key. Nothing complicated here at all; it simply loops through the tabs and deletes the right one, or exits if it is not found.

/**
 * Remove a tab
 *
 * @author Gary Dotzlaw - Dotzlaw Consulting
 * @since 2013-11-23
 *
 * @param {String}  sForm - The base form to remove the tab from (where the tab element is)
 * @param {String}  sTab - The name of the tab element
 * @param {String}  sTabLabel - The name of the tab form to be removed (i18n key like "lbl.tabName")
 *
 * @return {Boolean} TRUE or FALSE
 *
 * @properties={typeid:24,uuid:"8AD5AD48-466D-41C1-81AD-7134FFBD5B94"}
 */
function utils_tabRemove(sForm, sTab, sTabLabel)
{
 var sCurrentTabLabel = "",
  i = 0,
  iMax = forms[sForm].elements[sTab].getMaxTabIndex(),
  bTab = false;

 for (i = 1; i <= iMax; i++){
  // Clean up the tab name for webclient
  sCurrentTabLabel = forms[sForm].elements[sTab].getTabTextAt(i).replace(/ /g, " ");

  // If we find the tab we are looking for, then remove it
  if (sCurrentTabLabel == i18n.getI18NMessage(sTabLabel)){
   bTab = forms[sForm].elements[sTab].removeTabAt(i);
  }
 }
 return bTab;
}

This is how to add a tab panel by name using the map, and also how to remove a tab panel by name (or i18n key).

utils_tabAdd(
 "myParentForm",
 "myTabPanelName",
 "myTabForm",
 "lbl.tabC",
 "myRelationParentToTab", 
 _tabMap
);

utils_tabRemove(
 "myParentForm",
 "myTabPanelName",
 "lbl.tabC"
);

I hope this Servoy tutorial was helpful and gives you a new way of adding forms to tab panels based on a map of tab names. I have used this technique over and over again in my projects. Add the utility methods to your library and put it to good use. It will help you get your user interface under control by allowing you to move groups of optional data onto tab panels, allowing them to be added or removed based on options selected.

Dotzlaw Consulting

Dotzlaw Consulting brings over 20 years of experience in professional software development, serving over 100 companies across the USA and Canada. Specializing in all facets of the project lifecycle—from feasibility analysis to deployment—we deliver cutting-edge solutions such as AI-powered workflows, legacy system modernization, and scalable applications. Our expertise in Servoy development and advanced frameworks allows us to modernize fixed-positioning solutions into responsive platforms like ng Titanium with Bootstrap and core.less styling. With a passion for knowledge-sharing, our team has authored numerous tutorials on topics like object-oriented programming, AI agent development, and workflow automation, empowering businesses to achieve scalable, future-ready success.

Recent Posts

Optimizing Code Performance

This is a Servoy tutorial on how to optimize code performance. A while back, I had…

12 years ago

Servoy Tutorial: Using an Object as a Cache

This is an object-oriented Servoy tutorial on how to use an object as a cache in…

12 years ago

Function Memoization

This is an object-oriented Servoy tutorial on how to use function memoization with Servoy. Function memoization…

12 years ago

Object-Oriented Programming

This is an object-oriented Servoy tutorial on how to use object-oriented programming in Servoy. Javascript’s core…

12 years ago

Inheritance Patterns

This is an object-oriented Servoy tutorial on how to use inheritance patterns in Servoy. I use…

12 years ago

Prototypal Inheritance

This is an object-oriented Servoy tutorial on how to use prototypal inheritance in Servoy. When…

12 years ago