You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AEM 6.1 Classic UI转Touch UI Multifield JS定制问题排查

Fixing Multifield Storage & Required Validation Issues in AEM Touch UI Dialog Conversion

Let's tackle your two key issues with converting the Classic UI multifield to Touch UI, using your existing JS code as a starting point.

1. Multifield Not Storing/Loading Values

Looking at your code, there are a few critical gaps in how you're handling data collection and population. Here's what's wrong and how to fix it:

Key Problems in Your Original Code:

  • Incorrect Indexing: The counter variable in collectDataFromFields starts at 0, but you used (counter + 1) for array indexing—this creates mismatches when loading stored data later.
  • Field Traversal Bug: The selector $(multifield).children().children(cmf.CFFW) doesn't reliably target inputs inside each multifield item, leading to missed fields.
  • Data Mapping Mismatch: The field naming pattern used during storage (fieldSetName +"/" + name+"["+counter+"]") didn't align with the pattern used to load data (./"+mName+"/" + this.key), so values couldn't be mapped correctly.

Fixed Multifield Handling Code:

(function ($, $document) { "use strict"; 
    var _ = window._, Class = window.Class;

    ACS.TouchUI.MultiCompositeMultiField = new Class({
        toString: 'AARP Composite Multifield Store as Multi Value Field',
        extend: ACS.TouchUI.Widget,
        MULTI_VALUE_STORE: "MULTI_STORE",
        isMulti: false,
        CFFW: ".coral-Form-fieldwrapper", // Explicitly define field wrapper selector
        DATA_ACS_COMMONS_NESTED: "data-acs-commons-nested", // Define missing constant

        getMultiFieldNames: function(){
            var cmf = this, mNames = {}, mName, $multifield, $template;
            $(".js-coral-Multifield-input-template").each(function (i, template) {
                $template = $(template);
                $multifield = $($template.html());
                if(!cmf.isMultiValueStore($multifield.data(cmf.DATA_ACS_COMMONS_NESTED))){
                    return;
                }
                cmf.isMulti = true; // Fixed `this` reference to class instance
                mName = $multifield.data("name").substring(2);
                mNames[mName] = $template.closest(".coral-Multifield");
            });
            return mNames;
        },

        buildMultiField: function(data, $multifield, mName){
            var cmf = this;
            if(_.isEmpty(mName) || _.isEmpty(data)){
                return;
            }
            var baseFieldName = "./" + mName + "/";
            var dataLength = Array.isArray(data) ? data.length : 1;
            var currentItems = $multifield.find("li.coral-Multifield-input").length;

            // Add enough multifield items to match stored data length
            while(currentItems < dataLength){
                $multifield.find(".js-coral-Multifield-add").click();
                currentItems++;
            }

            // Populate each field with stored values
            _.each(data, function(item, index){
                if(_.isObject(item)){
                    _.each(item, function(value, key){
                        if(key === "jcr:primaryType") return;
                        var $field = $multifield.find("[name='"+baseFieldName + key +"']").eq(index);
                        if(!_.isEmpty($field)){
                            cmf.setWidgetValue($field, value);
                        }
                    });
                }
            });
        },

        addDataInFields: function () {
            var cmf = this, mNames = cmf.getMultiFieldNames(), $form = $("form.cq-dialog");
            if(_.isEmpty(mNames)) return;
            
            var actionUrl = $form.attr("action") + ".infinity.json";
            $.ajax(actionUrl).done(function(data){
                _.each(mNames, function($multifield, mName){
                    cmf.buildMultiField(data[mName], $multifield, mName);
                });
                $document.trigger("touchui-composite-multifield-nodestore-ready", mNames);
            });
        },

        getMultiValueStoreMultifields: function(){
            return $("[" + this.DATA_ACS_COMMONS_NESTED + "='" + this.MULTI_VALUE_STORE + "']");
        },

        isMultiValueStore: function(name){
            return (name === this.MULTI_VALUE_STORE);
        },

        collectDataFromFields: function(){
            if (!this.isMulti) return;
            var $multifields = this.getMultiValueStoreMultifields();
            if(_.isEmpty($multifields)) return;
            
            var $form = $("form.cq-dialog"), cmf = this;
            $multifields.each(function(mfIndex, multifield){
                var $mfItems = $(multifield).find("li.coral-Multifield-input");
                $mfItems.each(function(itemIndex, item){
                    $(item).find(cmf.CFFW).find("[name]").each(function(j, field){
                        var $field = $(field);
                        cmf.fillValue($form, $(multifield).data("name"), $field, itemIndex);
                    });
                });
            });
        },

        fillValue: function($form, fieldSetName, $field, itemIndex){
            var name = $field.attr("name"), value;
            if (!name) return;
            
            if (name.indexOf("./") === 0) {
                name = name.substring(2);
            }
            
            // Get value based on field type
            if (this.isCheckbox($field)) {
                value = $field.prop("checked") ? $field.val() : "";
            } else if ($field.is("select")) {
                value = $field.val() || "";
            } else {
                value = $field.val() || "";
            }
            
            // Add hidden field with correct indexed name
            $('<input />').attr({
                'type': 'hidden',
                'name': fieldSetName + "/" + name + "[" + itemIndex + "]",
                'value': value
            }).appendTo($form);
            
            // Hide original field instead of removing it (preserves validation)
            $field.hide();
        },

        isCheckbox: function($field){
            return $field.is("[type='checkbox']");
        }
    });

    $document.ready(function () {
        var compositeMultiField = new ACS.TouchUI.MultiCompositeMultiField();
        
        $document.on("dialog-ready", function(){
            compositeMultiField.addDataInFields();
        });

        // Handle submit with validation check
        $document.on("click", ".cq-dialog-submit", function(e){
            var $form = $("form.cq-dialog");
            // Trigger Coral UI's native validation
            var isValid = $form[0].checkValidity();
            if(!isValid){
                e.preventDefault();
                // Highlight invalid fields and focus first error
                $form.addClass("coral-Form--invalid");
                $form.find(":invalid").first().focus();
                return;
            }
            // Collect data only if form is valid
            compositeMultiField.collectDataFromFields();
        });
    });
}(jQuery, jQuery(document)));

Key Multifield Fixes:

  • Switched to zero-based indexing to match AEM's array storage pattern
  • Improved field traversal to reliably target inputs in each multifield item
  • Changed field removal to hiding, so validation can still access the original fields
  • Aligned data loading and storage naming patterns for correct value mapping

2. Required Field Validation Not Working

The core issue here was that your original code removed input fields before validation ran, so Touch UI's Coral validation couldn't check the required Article Heading field. Here's how we fixed it:

Validation Fixes:

  • Preserve Original Fields: Instead of removing fields in fillValue, we hide them—this keeps them in the DOM so validation can check their required status.
  • Explicit Validation Check: Added a check in the submit handler using $form[0].checkValidity() to trigger Coral's native validation. If the form is invalid, we prevent submission and highlight errors.
  • Conditional Data Collection: Only run collectDataFromFields after the form passes validation.

Additional Tips

  • Ensure your Touch UI dialog's Article Heading field has the required="{Boolean}true" attribute set in its XML definition
  • Use browser dev tools to check for JS errors—this will catch missing constants or selector issues quickly
  • Verify that your base ACS.TouchUI.Widget class defines DATA_ACS_COMMONS_NESTED (it should map to data-acs-commons-nested)

内容的提问来源于stack exchange,提问作者nikitha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:09:32