An alternative to using Innovator Lists that supports a dynamic Add capability.
Requirements:
1. Automatically inserts an <Add> selection for designated for Selection Lists, unless the Form is not opened for update. The Selection LIsts do not have to be pre-populated with the <Add> selection.
2. If the user selects <Add> then a pop-up window will be displayed that allows the user to enter a new text/value combination for the Selection List. This pop-up window is the same one that appears if the user had the privilege to add a record to the list via /Administrator/Lists. Thus, the new entry is reflected in the List on the Form and in the Innovator database.
3. After the user adds a new text/value combination then it will be automatically selected in the forms Selection List
4. When a Form is displayed/re-displayed then the Selection Lists will automatically be re-populated with the latest values from the database. This over-comes the biggest problem with Innovator "out-of-the-box" lists, they are not dynamically repopulated (ie. In Innovator 9.0.1 if the list contains 3 text/value pairs when user A logs on the Innovator and displays a form that uses the list, then the Administrator adds a 4th text/value pair in another Innovator session, even if user A refreshes the form display they will not see the 4th text/value pair unless they log off Innovator and log back on).
Solution:
1. I did not utilize the Innovator List itemtype to store my lists, instead I created my own itemtypes. For example, I created a "lbrand" itemtype and included the following 2 fields (which are similar to the properties in an Innovator List itemtype):
llabel String 25 characters Required, Unique (there is no requirement to always make this 25 characters)
lvalue String 1 character Required, Unique (there is no requirement to always make this 1 character, e.g. you could have 2 character values. Also you are not required to have a lvalue property if your list is just a list of labels)
I included llabel in the keyed_name for the lbrand itemtype. So this defines a specific list.
2. I created a Form for the lbrand itemtype so that I could pre-populate it with labels/values.
examples:
label value
Ford F
Pontiac P
Chrysler C
3. I included lbrand as a property on any itemtypes where it was required (e.g. the Part itemtype). Generally, I defined this property to match the corresponding lvalue property so that the lvalue would be stored. I have also used it to store the ID of the record from the list.
4. To include this lbrand as a selection list on a form (e.g. the Part form), I defined an html field type on the form and gave it the name lbrand. I defined the html field as follows:
<select name="lbrand" size="1" style="font-weight: bold" itemlist="1" itemtype="lbrand" itemadd="1"
onpropertychange="if (event.propertyName=='selectedIndex' && this.selectedIndex!=-1) if (window.handleItemChange) handleItemChange('lbrand',this.options(this.selectedIndex).value)">
</select>
Note that I included 3 attributes in this Select that are non-standard:
itemlist="1" is there so that my code can find all of these special Select objects on the Form
itemtype='lbrand" is there so that my code can determine which SQL table to find the label/values for this Select
itemadd="1" is there so that my code can determine if this Select should support the <Add> capability
Note the onpropertychange procedure. The key bit of code is "handleItemChange('lbrand',this.options(this.selectedIndex).value)". This
is an Innovator built-in method that will take the value of the selected row from the list and use it to update the lbrand property (e.g.
in the Part table).
5. I wrote a Method called, LoadList, and include it as an "OnFormPopulated" Form event (e.g. in the Part form definition). Disclaimer:
I have removed some Customer-specific code that I can not share, it is possible that this code may not compile if I made a mistake doing this.
// name:LoadList.js
// purpose: Initialize all <select> elements that are to be populated from
// item data. These are used instead of the standard Innovator List controls
// that are populated from Innovator lists. These <select> elements have:
// 1. a custom attribute named "itemlist" set to "1". [required]
// 2. a custom attribute named "itemtype" set to the itemtype that contains the list values. [required]
// 3. a custom attribute named "itemwhere" set to a SQL where clause appropriate for the itemtype. Used to filter the list [optional]
// 4. a custom attribute named "itemadd" set to "1" if an "<Add>" entry is to be appended to the beginning of the list values. [optional]
// 5. a custom attribute named "itemvalue" set to "1" if Item values are to be displayed instead of the normal Item labels. [optional]
//
// created: 11/19/2008
//
// ====================================================================================================
var innovator = document.thisItem.newInnovator(); // thisItem is the part item that will be created
var loadListCache; // Global variable used in lLoadListAdd and lLoadListCache functions
function lLoadListAdd(){
// purpose: Called as a field onChange event by item data <select>'s. This function implements the code to support
// the <Add> functionality for the pull down list.
// ======================================================================================================
var innovator = document.thisItem.newInnovator(); // thisItem is the part item that will be created
// Note, "this" is not an Innovator Item, rather is it the HTML Select object from the form
var lval = this.value; // This captures the value selected from the list
var lname = this.itemtype; // This captures the name of the associated item
if (lval == '<Add>') {
var newItemNd = top.aras.uiNewItemEx(lname);
var newItemId = newItemNd.getAttribute("id");
do {
var waitRet = confirm("Please add the new list entry. Save, unlock and close it, then return to this dialog box and click OK ");
if (waitRet === false){
top.aras.uiCloseWindowEx(newItemId);
top.aras.removeFromCache(newItemNd);
this.value = loadListCache; // Reset back to previous selection
return;
}
newItemNd = innovator.getItemById(lname,newItemId);
if(newItemNd){
var newPC = newItemNd.getProperty("keyed_name");
var newPV = newItemNd.getProperty("lvalue");
if (newPV === undefined){newPV = newPC;} //some Lists do not have the lvalue property
}
} while (!newItemNd);
var listControl = this;
var oOption = document.createElement("OPTION");
oOption.text = newPC;
if (listControl.itemvalue == "1"){oOption.text = newPV;}
oOption.value = newPV;
oOption.selected = true;
listControl.add(oOption);
}
else if (lval == '<-Select->') { // See comments below concerning <-Select->
alert("You can not pick <-Select->, make another selection");
this.value = loadListCache; // Reset back to previous selection
}
} //End of function lLoadListAdd() definition
function lLoadListCache() {
// purpose: Called as a field onclick event by item data <select>'s. This function implements the code to support
// resetting selection back to previous selection.
// ======================================================================================================
loadListCache = this.value;
} //End of function lLoadListCache() definition
// Start of main procedure
// Loop through the "select" elements
var elements = document.getElementsByTagName("select");
for (var i=0; i<elements.length; i++){
// Look for a custom attribute named "itemlist"
if (elements[i].itemlist == "1") {
var listControl = elements[i];
// If control has already been loaded then just enable/disable it as appropriate
if (listControl.length > 0) {
if (!document.isEditMode) {listControl.disabled = true;}
else {
listControl.disabled = false;
// Here you could add Application-specfic code to disable controls
// listControl.disabled = true;
}
continue;
}
// The next 2 statements register these procudures as onchange/onclick evenets
listControl.onchange = lLoadListAdd;
listControl.onclick = lLoadListCache;
// If itemadd flag is set then create an <Add> option
if (elements[i].itemadd == "1") {
var oOption2 = document.createElement("OPTION");
oOption2.text = "<-Select->"; //<-Select-> is needed if the visible length of the control = 1
//This is due to an Internet Explorer bug that pre-selects the first
//option in a list if the visible length = 1.
oOption2.value = oOption2.text;
listControl.add(oOption2);
var oOption = document.createElement("OPTION");
oOption.text = "<Add>";
oOption.value = oOption.text;
listControl.add(oOption);
}
if (!document.isEditMode) {listControl.disabled = true;}
else {
// Here you could add Application-specfic code to disable controls
// listControl.disabled = true;
}
// Now retrieve the entries to be added as options to the select
var pcListItem = innovator.newItem(listControl.itemtype,'get');
pcListItem.setAttribute('select','keyed_name,lvalue');
pcListItem.setAttribute('orderBy','keyed_name');
if (listControl.itemvalue == "1"){pcListItem.setAttribute('orderBy','lvalue');}
if (listControl.itemwhere){pcListItem.setAttribute('where',listControl.itemwhere);}
var pcListItems = pcListItem.apply();
var pcListCount = pcListItems.getItemCount;
if (pcListCount > 0) {
for (var j=0; j<=pcListCount-1; j++) {
oOption = document.createElement("OPTION");
oOption.text = pcListItems.getItemByIndex(j).getProperty('keyed_name');
if (listControl.itemvalue == "1"){oOption.text = pcListItems.getItemByIndex(j).getProperty('lvalue');}
oOption.value = pcListItems.getItemByIndex(j).getProperty('lvalue');
// Some item lists will not have a "value" field, in addition to "keyed_name"
if (oOption.value == "undefined"){oOption.value = oOption.text;}
// If thisItem has a value=Option.text, then set this Option=selected
var strItemType = listControl.itemtype;
if (listControl.name == "lparent") {
var recordValue = document.thisItem.getProperty("lparent");
}
else {
recordValue = document.thisItem.getProperty(strItemType);
}
listControl.add(oOption);
if ((recordValue != "undefined") & (recordValue == oOption.value)){
oOption.selected = true;
}
}
}
}
}
// Loop through the "input" elements on the Form that have itemlist = "1", they will always be readonly
elements = document.getElementsByTagName("input");
for (i=0; i<elements.length; i++){
// Look for a custom attribute named "itemlist"
if (elements[i].itemlist == "1") {
var inputControl = elements[i];
var searchTable = inputControl.itemtype;
var searchValue = document.thisItem.getProperty(searchTable);
// Now retrieve the entry to be display in the <input> element
var ipListItem = innovator.newItem(searchTable,'get');
ipListItem.setAttribute('select','keyed_name,lvalue');
ipListItem.setAttribute('where',searchTable + ".lvalue = '" + searchValue + "'" );
var ipListItems = ipListItem.apply();
var ipListCount = ipListItems.getItemCount;
if (ipListCount == 1) {
inputControl.value = ipListItems.getItemByIndex(0).getProperty('keyed_name');
}
// Otherwise just replace with value from thisItem
else {inputControl.value = searchValue;}
}
}
Issues:
If the user selects <Add> from the list, the form that is displayed for the user to add a new list entry is initially displayed "mimimized". I have yet to determine how to fix this.