Adding an image form element to an add/edit form
I found a way to do it without requiring a class attached to the field. I mean there is a class attached to the form element but not as a renderer.
The column should be defined as this:
<field name="image">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataType" xsi:type="string">string</item>
<item name="source" xsi:type="string">[entity]</item>
<item name="label" xsi:type="string" translate="true">Image</item>
<item name="visible" xsi:type="boolean">true</item>
<item name="formElement" xsi:type="string">fileUploader</item>
<item name="elementTmpl" xsi:type="string">ui/form/element/uploader/uploader</item>
<item name="previewTmpl" xsi:type="string">[Namespace]_[Module]/image-preview</item>
<item name="required" xsi:type="boolean">false</item>
<item name="uploaderConfig" xsi:type="array">
<item name="url" xsi:type="url" path="[namespace_module]/[entity]_image/upload"/>
</item>
</item>
</argument>
</field>
I also needed to create the preview template file referenced by [Namespace]_[Module]/image-preview
.
That is app/code/[Namespace]/[Module]/view/adminhtml/web/template/image-preview.html
that looks like this:
<div class="file-uploader-summary">
<div class="file-uploader-preview">
<a attr="href: $parent.getFilePreview($file)" target="_blank">
<img
class="preview-image"
tabindex="0"
event="load: $parent.onPreviewLoad.bind($parent)"
attr="
src: $parent.getFilePreview($file),
alt: $file.name">
</a>
<div class="actions">
<button
type="button"
class="action-remove"
data-role="delete-button"
attr="title: $t('Delete image')"
click="$parent.removeFile.bind($parent, $file)">
<span translate="'Delete image'"/>
</button>
</div>
</div>
<div class="file-uploader-filename" text="$file.name"/>
<div class="file-uploader-meta">
<text args="$file.previewWidth"/>x<text args="$file.previewHeight"/>
</div>
</div>
This code will generate a field like this:
After uploading an image (real time) it looks like this:
The url
item inside the uploaderConfig
is the url where the image is posted when uploaded. So I needed to create this also:
namespace [Namespace]\[Module]\Controller\Adminhtml\[Entity]\Image;
use Magento\Framework\Controller\ResultFactory;
/**
* Class Upload
*/
class Upload extends \Magento\Backend\App\Action
{
/**
* Image uploader
*
* @var \[Namespace]\[Module]\Model\ImageUploader
*/
protected $imageUploader;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \[Namespace]\[Module]\Model\ImageUploader $imageUploader
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\[Namespace]\[Module]\Model\ImageUploader $imageUploader
) {
parent::__construct($context);
$this->imageUploader = $imageUploader;
}
/**
* Check admin permissions for this controller
*
* @return boolean
*/
protected function _isAllowed()
{
return $this->_authorization->isAllowed('[Namespace]_[Module]::[entity]');
}
/**
* Upload file controller action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
try {
$result = $this->imageUploader->saveFileToTmpDir('image');
$result['cookie'] = [
'name' => $this->_getSession()->getName(),
'value' => $this->_getSession()->getSessionId(),
'lifetime' => $this->_getSession()->getCookieLifetime(),
'path' => $this->_getSession()->getCookiePath(),
'domain' => $this->_getSession()->getCookieDomain(),
];
} catch (\Exception $e) {
$result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()];
}
return $this->resultFactory->create(ResultFactory::TYPE_JSON)->setData($result);
}
}
This class uses an instance of [Namespace]\[Module]\Model\ImageUploader
that is similar to \Magento\Catalog\Model\ImageUploader
.
This seams to work. I still have troubles saving the image in the db but that's a totally different issue.
I used as inspiration the image
field for the category entity
Yes, the class you should extend is \Magento\Ui\Component\Form\Element\AbstractElement
.
This class implements the ElementInterface
which itself extends the UiComponentInterface
you're referring to.
On top of that, if you check the components declared under Magento\Ui\Component\Form\Element
you can see that they all extend that class.
The reason I would choose this class is because the (This is actually an instance of render
method of \Magento\Backend\Block\Widget\Form\Renderer\Element
only accepts such class type:Magento\Framework\Data\Form\Element\AbstractElement
that is accepted, not \Magento\Ui\Component\Form\Element\AbstractElement
)