12

I'm building a CRUD module for Magento 2 using ui components for admin list and form and one of my entities has an image field.
But I cannot make it work as it should.
Here is how it should work.
When in add mode or in edit mode with no uploaded image it should look like a simple file input.

When a file is uploaded it should show the image preview and a delete box below it.

I'm not looking for the exactly this design. It could look differently but have the same functionality.

In Magento 1 I was able to do this, just by creating my own block renderer

class {{Namespace}}_{{Module}}_Block_Adminhtml_{{Entity}}_Helper_Image extends Varien_Data_Form_Element_Image
{
    protected function _getUrl()
    {
        $url = false;
        if ($this->getValue()) {
            $url = Mage::helper('{{namespace}}_{{module}}/{{entity}}_image')->getImageBaseUrl().$this->getValue();
        }
        return $url;
    }
}

And adding this in my form block

    $fieldset->addType(
        'image',
        Mage::getConfig()->getBlockClassName('{{namespace}}_{{module}}/adminhtml_{{entity}}_helper_image')
    );

But I have no form block in Magento 2.
I know I can use a class name for a form field in the ui components file

    <field name="image" class="Class\Name\Here">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="dataType" xsi:type="string">text</item>
                <item name="label" xsi:type="string" translate="true">Resume</item>
                <item name="formElement" xsi:type="string">image</item>
                <item name="source" xsi:type="string">[entity]</item>
                <item name="dataScope" xsi:type="string">image</item>
            </item>
        </argument>
    </field>

Obviously I have to create this class, but what should I extend?
All I know is that I need to implement the interface Magento\Framework\View\Element\UiComponentInterface but I found nothing I can extend.
So my real question is: Can I extend some class to achieve the desired behavior? If not, how can I start creating this element renderer?

1
  • Hi @Marius , I tried to use your example to be able to add product images in my custom grid edit page, but got this error : Fatal error: Class 'Varien_Data_Form_Element_' not found in ...\lib\Varien\Data\Form\Abstract.php on line 146 Commented May 25, 2017 at 18:14

2 Answers 2

21

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

12
  • I can successfully upload image and save image name into database, then when I open record i just created, all fields other than image field are all showing as expected. When I change the image field to just a normal "text" field, then it will show. Do you have any idea about this?
    – Nero
    Commented Oct 19, 2016 at 14:50
  • 1
    @Nero. You need the image value in a certain json format. Here is an example on how you can transform it to the proper json
    – Marius
    Commented Oct 19, 2016 at 14:57
  • I don't want to upload image but i want to display image in admin Ui form.Actually i upload image from frontend form and want to display it in admin ui form.So please help me how to do Commented Jan 19, 2017 at 7:08
  • There is error in [Namespace][Module]\Controller\Adminhtml[Entity]\Image\upload.php on line number 61 Please check and update answer. Commented May 26, 2017 at 5:47
  • @PrincePatel What's the error message?
    – Marius
    Commented May 26, 2017 at 6:23
2

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 render method of \Magento\Backend\Block\Widget\Form\Renderer\Element only accepts such class type: (This is actually an instance of Magento\Framework\Data\Form\Element\AbstractElement that is accepted, not \Magento\Ui\Component\Form\Element\AbstractElement )

4
  • Any pointers on how my class should look like?
    – Marius
    Commented May 20, 2016 at 8:31
  • @Marius hmmm I'm not too sure, I'll try to find out Commented May 20, 2016 at 8:36
  • 1
    I don't think you need to do that yet. I think I found a solution without using a class in the ui component, but I need to test first.
    – Marius
    Commented May 20, 2016 at 8:41
  • @Marius hmmmm I think I was wrong, I reckon you should check that out: github.com/magento/magento2-samples/tree/master/… Commented May 20, 2016 at 9:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.