I am trying to get file upload working in a ui-component admin form. The file input field is coming from a htmlContent block. This is all sitting in a <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> element.
Now the issue is that the form element is not actually being rendered as html, as such I cannot add/apply enctype="multipart/form-data" to this form and $_FILES remains empty upon submitting.
Attempts to change the uiComponent to include form element and/or enctype definition failed as it doesn't allow usage of such elements/attributes in the ui-component file directly (collapsible.xhtml). How to move forward with this?
Code example:
Form definition
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
[...]
<item name="template" xsi:type="string">templates/form/collapsible</item>
<item name="enctype" xsi:type="string">multipart/form-data</item>
</argument>
<settings>
[...]
</settings>
<dataSource name="XXX">
[...]
</dataSource>
[...]
<fieldset name="XXX" sortOrder="20">
<settings>
<collapsible>false</collapsible>
<opened>true</opened>
<label translate="true">Products</label>
</settings>
<htmlContent name="custom_field">
<block name="html_content_input_field" class="XXXX">
<arguments>
<argument name="template" xsi:type="string">
XXX.phtml
</argument>
</arguments>
</block>
</htmlContent>
</fieldset>
</form>
XXX.phtml - Simplified as the file is nested in an array-like structure set up with Knockout
<div>
<input type="file" name="nameforfile" data-form-part="the-correct-name"/>
</div>
All textual data is send - even the fake file name of the file but not the file itself. I don't need instant upload and/or visible files - Solely the data of the file actually being send to the server.
Raw POST data looks like:
------WebKitFormBoundaryvmSygS0ldVua30Aw
Content-Disposition: form-data; name="author_id"
1
[...] etc
-
just follow basic Magento code standard for file upload. developer.adobe.com/commerce/frontend-core/ui-components/…Suraj Tupe– Suraj Tupe2023年11月28日 18:48:58 +00:00Commented Nov 28, 2023 at 18:48
-
It should come from a html file, not a ui-component as it's nested in a htmlContentvandijkstef– vandijkstef2023年12月01日 13:55:24 +00:00Commented Dec 1, 2023 at 13:55
-
Can you please share your code so that we can better understand the problem?Msquare– Msquare2023年12月05日 15:35:10 +00:00Commented Dec 5, 2023 at 15:35
-
@msquare Edited/added minimum code examplevandijkstef– vandijkstef2023年12月13日 09:16:16 +00:00Commented Dec 13, 2023 at 9:16
2 Answers 2
If you need a file uploader in a UI component, you can use their default component, which provides a way to upload a file with data and save it in your controller. When you create a form using UI component, it does not create form elements; that's why you cannot get file data into the controller. If you want file data, your input must be inside a form tag, and the enctype attribute value must be multipart/form-data, which is not available in a UI component.
To implement a file uploader with UI components, consider using a combination of UI components and a traditional form for handling file uploads. This way, you can achieve the desired functionality while still working within the constraints of UI components.
File Upload into UI component: https://magento.stackexchange.com/a/186781/82670
And if you don't have an option for it, you need to add an AJAX call, as described in the above answer link. In UI components, a similar approach is used—when you upload anything, it triggers an AJAX controller that stores the file in a temporary location. You can follow a similar approach here. Below is a small example of how to retrieve file data in a UI form
Form UI Component
<fieldset name="test_uploader">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="label" xsi:type="string" translate="true">Test Image Uploader</item>
</item>
</argument>
<container name="test_uploader_container" >
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="sortOrder" xsi:type="number">1</item>
</item>
</argument>
<htmlContent name="html_content">
<argument name="block" xsi:type="object">VendorName\ModuleName\Block\Adminhtml\CustomData</argument>
</htmlContent>
</container>
</fieldset>
app/code/VendorName/ModuleName/Block/Adminhtml
CustomData.php
<?php
namespace VendorName\ModuleName\Block\Adminhtml;
use Magento\Framework\View\Asset\Repository;
class CustomData extends \Magento\Backend\Block\Widget\Form\Generic
{
protected $_systemStore;
protected $_countryFactory;
protected $_assetRepo;
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\Data\FormFactory $formFactory,
\Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig,
\Magento\Framework\View\Asset\Repository $assetRepo,
\Magento\Directory\Model\Config\Source\Country $countryFactory,
\Magento\Store\Model\System\Store $systemStore,
array $data = []
) {
$this->_systemStore = $systemStore;
$this->_assetRepo = $assetRepo;
$this->_countryFactory = $countryFactory;
$this->_wysiwygConfig = $wysiwygConfig;
parent::__construct($context, $registry, $formFactory, $data);
}
/**
* Prepare form.
*
* @return $this
*/
protected function _prepareForm()
{
// $model = $this->_coreRegistry->registry('row_data');
$form = $this->_formFactory->create(
['data' => [
'id' => 'custom_form',
'enctype' => 'multipart/form-data',
'action' => $this->getData('action'),
'method' => 'post',
'data-form-part' => "custom_form",
]
]
);
$form->setHtmlIdPrefix('storelocation_');
$fieldset = $form->addFieldset(
'base_fieldset',
['legend' => __('General Information'), 'class' => 'fieldset-wide']
);
$importdata_script = $fieldset->addField(
'importdata',
'file',
array(
'label' => 'Upload File',
'name' => 'importdata',
'data-form-part' => "custom_form",
)
);
$importdata_script->setAfterElementHtml("
<span id='sample-file-span' >
<script type=\"text/javascript\">
document.getElementById('storelocation_importdata').onchange = function () {
var fileInputData = document.getElementById('storelocation_importdata').files[0];
console.log(fileInputData);
};
</script>"
);
// $form->setValues($model->getData());
$form->setUseContainer(true);
$this->setForm($form);
return parent::_prepareForm();
}
}
You can display file data in the console when selected using file input and implement AJAX to save it to a temporary path, similar to what the UI component does for file uploading.
OUTPUT enter image description here
-
1Thank you for your complete response. Sorry I'm not working this fulltime, thus responding slow. So it is a hard constraint in UI-Components. Bad design imo, but okay. It's sad as the images should be attached to an array-like entity which may not exist in DB yet, why I tried so hard to have it in one POST. Many thanks again!vandijkstef– vandijkstef2023年12月20日 13:13:49 +00:00Commented Dec 20, 2023 at 13:13
For File Handling In Magento ui_component Form Custom Template File. How we can add enctype="multipart/form-data"
There is not need to add enctype="multipart/form-data".
Just add htmlContent tag template's file input field in attribute, called form and set value ui_component handle name.
For Example:
This is my ui_component file name: learn_hello_form.xml
This is path: app/code/Learn/Hello/view/adminhtml/ui_component/learn_hello_form.xml
<?xml version="1.0" ?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
***
</argument>
<settings>
***
</settings>
<htmlContent name="html_content">
<settings>
<additionalClasses>
<class name="html-content-class">true</class>
</additionalClasses>
</settings>
<block name="html_content_block" class="Learn\Hello\Block\Adminhtml\Test">
<arguments>
<argument name="template" xsi:type="string">Learn_Hello::test.phtml</argument>
</arguments>
</block>
</htmlContent>
This is template file input tag:
<div class="admin__field-control">
<input type="file" name="attachment" id="image" class="admin__control-file" form="learn_hello_form"/>
</div>
Explore related questions
See similar questions with these tags.