Magento 2:如何在管理员中上传动态添加的文件输入字段的文件

我的管理员中有一个表单,我可以在管理员中动态添加多个文件上传字段。
这个机制是用Knockout构建的。
但是当我提交表单时,文件没有上传(因为Magento'将表单信息转移到隐藏的表单而不是提交)。
如何在提交时将动态添加的文件上载字段考虑在内?

我在Knockout中创建了一个可以创建一组选项的组件。这些选项具有标签和图像。在水下,所有这些配置都保存为JSON字符串,并且此JSON字符串存储在数据库中。除图像外,这种方法效果很好。

在我的表单中添加一个虚拟上传字段会将Magento的“隐藏”表单转换为enctype="multipart/form-data",但它不会包含我动态添加的文件上传字段。

第一步:我在UI组件的XML中添加了一个自定义字段:

<field name="configuration">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <!-- Change the component: -->
            <item name="component" xsi:type="string">Vendor_Module/js/form/element/configurator</item>
            <!--main template for form field that renders elementTmpl as a child template-->
            <item name="template" xsi:type="string">ui/form/field</item>
            <!-- Set the template :-->
            <item name="elementTmpl" xsi:type="string">Vendor_Module/form/element/configurator</item>
            <item name="dataType" xsi:type="string">text</item>
            <item name="label" xsi:type="string" translate="true">Configuration</item>
            <item name="formElement" xsi:type="string">textarea</item>
            <item name="source" xsi:type="string">form</item>
            <item name="sortOrder" xsi:type="number">30</item>
            <item name="dataScope" xsi:type="string">configuration</item>
            <item name="validation" xsi:type="array">
                <item name="required-entry" xsi:type="boolean">true</item>
            </item>
        </item>
    </argument>
</field>

html呈现的输入字段和添加新的选项提供了一个按钮-template看起来是这样的:

<textarea class="admin__control-textarea" data-bind="
    value: value,
    valueUpdate: valueUpdate,
    hasFocus: focused,
    attr: {
        name: inputName,
        cols: cols,
        rows: rows,
        'aria-describedby': noticeId,
        placeholder: placeholder,
        id: uid,
        disabled: disabled
    }"
/>

... some knockout template code ... 

<div class="form-configurator__options">
    <label data-bind="i18n: 'Options'"></label>
    <ul data-bind="foreach:options">
        <li>
            <input type="text" data-bind="value:value"/>
            <input type="text" data-bind="value:image"/>
            <input type="file" data-bind="attr:{name:'image_' + $parentContext.$parentContext.$index() + '_' + $parentContext.$index() + '_' + $index()}"/>
        </li>
    </ul>
    <a href="#" class="form-configurator__button" data-bind="i18n: 'Add New Value', click: $parents[1].addOption.bind($parents[1])"></a>
</div>

... some knockout template code ... 

name根据它的范围输入字段的动态构建,但它永远是这样image_1_0_0image_1_2_3options是可观察到的数组,addOption增加了一个新选项,此阵:

addOption: function(column) {
  column.options.push({
    value: this.getSubscribedObservable(),
    image: this.getSubscribedObservable()
  });
},

getSubscribedObservable()是一个返回相同的方法,ko.observable()但是为它订阅一个更新value()我的UIElement 的observable 的事件,有效地用JSON字符串替换textarea中的文本(如上所述)。

现在,因为所有内容都被转移到了最初的textarea(名称configuration),所以一切都运行良好。但是文件上传字段当然不能像这样处理。

所以,我想要做的唯一的事情就是确保这些文件被发布到服务器上,这样我就可以处理文件上传从我的控制器内(我可以根据自己的动态名称(用正确的选择配合他们image_0_0_1image_0_0_2等等)那么这里的问题是:如何将这些文件发布到我的服务器上?

当我提交表单时,Magento会快速创建一个隐藏的表单,但我认为它只会查看在我的UI组件中声明的字段,而不是使用JavaScript动态添加的字段。那我该怎么做呢?

或者甚至是一个更基本的例子,它将文件上传排除在等式之外:“如果表单添加了UI组件中没有的额外文本输入字段,那么如何将其提交给我的控制器?”

我已经想出了如何为默认表单字段(例如文本输入)执行此操作。通过向相关data-form-part元素添加-attribute,它将添加到'hidden'表单:

<input type="text"
       data-form-part="form_form_form"
       data-bind="attr:{name:'image_' + $parentContext.$parentContext.$index() + '_' + $parentContext.$index() + '_' + $index()}"/>

但这不适用于输入file字段,因为它们只会被转换为文本字段并获取假值,但不会上传文件(例如:) "C:\fakepath\some-image.png"

解决办法是:表单的内容被复制到隐藏的表单,并且只复制UI组件的XML文件中声明的字段的值。可以使用data-form-part-attribute 添加其他字段。

我们需要做的第一件事是弄清楚,在代码中这些值被复制到这个隐藏的形式。好吧,事实证明这发生在lib/mage/utils/misc.js::submit()

submit: function (options, attrs) {
    var form        = document.createElement('form'),
        data        = this.serialize(options.data),
        attributes  = _.extend({}, defaultAttributes, attrs || {}),
        field;

    if (!attributes.action) {
        attributes.action = options.url;
    }

    data['form_key'] = window.FORM_KEY;

    _.each(attributes, function (value, name) {
        form.setAttribute(name, value);
    });

    _.each(data, function (value, name) {
        field = document.createElement('input');

        field.setAttribute('name', name);
        field.setAttribute('type', 'hidden');

        field.value = value;

        form.appendChild(field);
    });

    document.body.appendChild(form);

    form.submit();
},

如您所见,传递给此函数的所有属性都将转换为新形式的隐藏输入元素。但众所周知,如果你想提交文件,你需要type="file",而不是hidden。所以我们需要覆盖这个功能。

首先,我们需要查看声明的位置。经过一些回溯后,我注意到submit()-method主要是从mageUtils-component中使用的。

mageUtils你可能会问这是什么?嗯,这是主题模块设置的requirejs地图。它加载lib/mage/utils/main.js了所有utils的占位符:

define(function (require) {
    'use strict';

    var utils = {},
        _ = require('underscore');

    return _.extend(
        utils,
        require('./arrays'),
        require('./compare'),
        require('./misc'),
        require('./objects'),
        require('./strings'),
        require('./template')
    );
});

所以让我们覆盖这个文件。我通过requirejs-config.js在我的模块中添加一个文件view/adminhtml夹来完成此操作:

var config = {
    "map": {
        "*": {
            "mageUtils": "Vendor_Module/js/mage/utils/main"
        }
    }
};

这是我修改过的main.js

/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
define(function (require) {
    'use strict';

    var utils = {},
        _ = require('underscore');

    return _.extend(
        utils,
        require('mage/utils/arrays'),
        require('mage/utils/compare'),
        require('mage/utils/misc'),
        require('mage/utils/objects'),
        require('mage/utils/strings'),
        require('mage/utils/template'),
        // Overrule submit-method, so it includes file uploads:
        {
            /**
             * Serializes and sends data via POST request.
             *
             * @param {Object} options - Options object that consists of
             *      a 'url' and 'data' properties.
             * @param {Object} attrs - Attributes that will be added to virtual form.
             */
            submit: function (options, attrs) {
                var defaultAttributes = {
                    method: 'post',
                    enctype: 'multipart/form-data'
                };

                var form        = document.createElement('form'),
                    data        = this.serialize(options.data),
                    attributes  = _.extend({}, defaultAttributes, attrs || {}),
                    field;

                if (!attributes.action) {
                    attributes.action = options.url;
                }

                data['form_key'] = window.FORM_KEY;

                _.each(attributes, function (value, name) {
                    form.setAttribute(name, value);
                });

                var fileUploadField;

                _.each(data, function (value, name) {
                    field = document.createElement('input');

                    // Check if this is a file upload field:
                    if (fileUploadField = document.querySelector('input[type="file"][name="' + name + '"]')) {
                        // field = fileUploadField.clone();
                        form.appendChild(fileUploadField);
                    } else {
                        field.setAttribute('name', name);
                        field.setAttribute('type', 'hidden');
                        field.value = value;
                        form.appendChild(field);
                    }
                });

                document.body.appendChild(form);

                form.submit();
            }
        }
    );
});

正如你所看到的,我包含所有默认的东西,就像原版一样main.js,但我添加了一个额外的选项,只包含一个新的submit()方法,有效地覆盖了一个misc.js。我做的唯一的补充是检查是否有一个具有相同名称的文件上传字段。如果是这样,请移动(不克隆,因为这不起安全作用)输入字段到新表单,否则回退到您的默认行为。

花了一段时间,但它完成了工作。现在,我可以使用动态添加的上传字段将文件上传提交到服务器

相关文章

0 0 投票数
文章评分
订阅评论
提醒
0 评论
内联反馈
查看所有评论