Repeating Subform elements

Comments

2 comments

  • Avatar
    Karl Makatenas

    I had the same issue a while back, and the solution was a real headache but still doable.  If anyone knows a better way to do this, I'm all ears.

    Basically, you have to use the entire HtmlCollection that gets returned and figure out which numbered index to reference for your data.  What I did was enable the Show Row Number option of the subform, hide it using CSS, then relatively reference that element based on whichever event fired the JavaScript in question.  Once I had that element, I could parse the value and get the row number.  Now, take the row number as the index of the HtmlCollection array and you may hopefully find the current element, but I remember running into a few issues with hidden templated rows which threw off the HtmlCollection object, so I needed to refine my CSS query to only grab visible elements matching very specific criteria.

    Another option is to relatively reference the element in question based on the event fired by the JavaScript, but my case was a bit different and there were dynamic fields preventing me from referencing it directly, hence the HtmlCollection array solution.

    I apologize that I couldn't find a way to add indents from the clipboard on these forums, so this may be a bit hard to read.  Also, I only took portions of my code, so it may have a missing function or there may be unused functions floating around below.

    window.AwmCode = window.AwmCode || {
    callbackObject: {},
    specialElements: []
    }
    AwmCode.CreateCustomPropertiesForElement = AwmCode.CreateCustomPropertiesForElement || function(elemOrId, propertyList) {
    var element;

    // Convert elemOrId into a single element.
    if (typeof elemOrId === "string") {
    element = document.getElementById(elemOrId);
    if (element === null)
    return null;
    }
    else if (typeof elemOrId === "object" && elemOrId.nodeType === 1)
    element = elemOrId;
    else
    return null;

    // Create the object.
    var obj = {
    node: element,
    properties: {}
    };
    obj.CanFireJsEvent = function(eventName) {
    if (obj.properties.hasOwnProperty(eventName) && obj.properties[eventName] === "locked")
    return false;
    return true;
    };
    for (var key in propertyList) {
    obj.properties[key] = propertyList[key];
    }

    AwmCode.specialElements.push(obj);
    };
    AwmCode.FireEventsOnElements = AwmCode.FireEventsOnElements || function() {
    // For now, only fires the "change" event.
    var thisElemId,
    elementList = AwmCode.UnwrapElements(arguments),
    isInDatabase,
    elemLen = elementList.length,
    databaseLen = AwmCode.specialElements.length;

    for (var elemIndex = 0; elemIndex < elemLen; elemIndex++) {
    thisElemId = elementList[elemIndex].id;
    isInDatabase = false;
    for (var databaseIndex = 0; databaseIndex < databaseLen; databaseIndex++) {
    if (AwmCode.specialElements[databaseIndex].node.id === thisElemId) {
    if (AwmCode.specialElements[databaseIndex].node.CanFireJsEvent("change"))
    elementList[elemIndex].dispatchEvent(new Event("change"));
    isInDatabase = true;
    break;
    }
    }
    if (isInDatabase === false)
    elementList[elemIndex].dispatchEvent(new Event("change"));
    }
    };

    //----------------------------------------------------------
    // USABLE FUNCTIONS
    //----------------------------------------------------------

    function CopyValueOfDropdownToAnotherField(input, cssClassOfThisField, cssClassOfNewField, options) {
    // USAGE
    /*
    * Create a repeating subform.
    * Create a dropdown list inside the subform which has a lookup to a name/value pair.
    * Create a text box which will hold the value associated with the dropdown selection.
    * Edit the text box and set the Field CSS to some unique string. Copy this class name into the final step.
    * Edit the dropdown list and go to the appearance tab. In the Field Css, type a unique CSS class that you won't find anywhere else on the page.
    * Now go to the advanced tab. Edit the JavaScript Event to "Change", and paste the following line into the JavaScript Function while editing the value in quotes to the CSS class from above:
    * CopyValueOfDropdownToAnotherField([this], "InputDropdownForFirstLookupField36", "PutDropdownValueIntoThisCascadingLookupField4", "Fire)
    * If you want the dropdown text instead of the hidden value, add a boolean true as the last parameter.
    */

    // OPTIONS
    /*
    * getSelectionText: bool. Grabs the dropdown selection text instead of the dropdown value.
    */

    // MAIN CODE

    try {
    var dropdownNode = input[0];
    } catch(e) {
    console.log("Don't set up CopyValueOfDropdownToAnotherField() from a rule; set it up from the field's Advanced tab using JavaScript options.");
    return;
    }

    var elementNumber = WhichNumberElementIsThisNode(dropdownNode, cssClassOfThisField);
    if (elementNumber > -1) {
    try {
    var fieldValue = GetValueOfSelectedItemFromDropdown(dropdownNode);
    SetFieldValueToDropdownValue(fieldValue, elementNumber);
    }
    catch(e) {
    console.log("Error attempting to find element in custom JavaScript.");
    }
    }

    // FUNCTION DEFINITIONS

    function GetValueOfSelectedItemFromDropdown(dropdownElement) {
    var optionNum = dropdownElement.selectedIndex;
    if (options) {
    if (options.getSelectionText)
    return dropdownElement.children[optionNum].text;
    }
    return dropdownElement.children[optionNum].value;
    }
    function SetFieldValueToDropdownValue(fieldValue, elementNumber) {
    var fieldToHoldValue = document.getElementsByClassName(cssClassOfNewField)[elementNumber];
    fieldToHoldValue.value = fieldValue;
    AwmCode.FireEventsOnElements(fieldToHoldValue);
    }
    function WhichNumberElementIsThisNode(currentElement, cssClass) {
    /*
    var elementsWithThisClass = document.getElementsByClassName(cssClass);
    var len = elementsWithThisClass.length-1;
    var counter = 0;
    // AgilePoint makes many template duplicates of certain repeatable fields, then hides the templates. We need to get the index of this element compared to other visible elements.
    for (var index = 0; index < len; index++) {
    if (IsVisible(elementsWithThisClass[index]))
    counter++;
    if (elementsWithThisClass[index] === currentElement)
    return counter;
    }
    return -1;
    */
    var elementsWithThisClass = document.getElementsByClassName(cssClass);
    for (var index = elementsWithThisClass.length-1; index > -1; index--)
    if (elementsWithThisClass[index] === currentElement)
    return index;
    return -1;
    }
    }
    function FireChangeEventOnClickableObjectInteraction(internalNameFieldList) {
    // USAGE
    /*
    * Include this script in the form load rules. For "When", leave a null condition, and for "Then", use an Execute Method block and select AppendTextToClickableObjects in the dropdown.
    * Next to the dropdown, use a comma-delimited list of quoted internal names surrounded by brackets. For example: ["DomesticOrInternational", "DirectorEmail"]
    */

    // MAIN CODE

    document.addEventListener("click", function(event) {
    var fireOnTheseElements;
    var a = HasClass(event.target, "addSubFormRow");
    var b = HasClass(event.target, "popActionButtons") && (HasClass(event.target, "next") || HasClass(event.target, "previous"));
    var c = HasClass(event.target, "formSectionProgressBarCell");
    fireOnTheseElements = a || b || c;

    if (fireOnTheseElements) {
    setTimeout( function() {
    for (var index = internalNameFieldList.length - 1; index > -1; index--) {
    eFormHelper.getField({fieldId: internalNameFieldList[index]}, function(result) {
    result.data[0].dispatchEvent(new Event("change"));
    });
    }
    }, 0);
    }
    });
    }

    //----------------------------------------------------------
    // HELPER FUNCTIONS
    //----------------------------------------------------------

    function GetFieldValue(fieldInternalName) {
    eFormHelper.getFieldValue({fieldId: fieldInternalName}, function(result) {
    window.SuperLongGlobalVariableNameWithFieldValue = result.data;
    });
    return window.SuperLongGlobalVariableNameWithFieldValue;
    }
    function HasClass(element, classToCheck) {
    return element.className.split(" ").indexOf(classToCheck) > -1;
    }
    function InsertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
    }
    function IsVisible(element) {
    return element.is(function(elem) {
    return elem.offsetWidth > 0 || elem.offsetHeight > 0;
    });
    }
    function SetFieldValue(fieldInternalName, dataObject) {
    eFormHelper.setFieldValue({fieldId: fieldInternalName, value: dataObject.data});
    eFormHelper.getField({fieldId: fieldInternalName}, function(result) {result.data[0].dispatchEvent(new Event("change"))}); //Trigger the native AgilePoint event listeners.
    }
    0
    Comment actions Permalink
  • Avatar
    Lucas Drege

    hi Scott, I will be sending you a sample for your reference.  

     

    0
    Comment actions Permalink

Please sign in to leave a comment.