Magento 2.1 – UI Component Image Upload Form

image-uploadmagento-2.1uicomponent

I am trying to add an image field in my module. I know its been asked before, I tried many provided solutions but can't get it to work. Here's the error I am getting:

While editing old entity:

/pub/static/adminhtml/Magento/backend/en_US/Magento_Ui/js/form/element/file-uploader.js:69
Uncaught TypeError: value.map is not a function

While creating new entity, after selecting the uploadable file, I get this on console:

admin/namespace_module/entity_image/upload/key/dce7aaf794cabba38bc88c52a3317988b859ee4987303d9954ab92c91f9f9049?isAjax=true
500 (Internal Server Error)

On checking the url above:

Exception #0 (BadMethodCallException): Missing required argument
$baseTmpPath of Magento\Catalog\Model\ImageUploader.

My form.xml

<field name="image">
    <argument name="class" xsi:type="string">Namespace\Module\Model\Entity\DataProvider</argument>
    <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>

My Upload Class:

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 \Magento\Catalog\Model\ImageUploader $imageUploader
     */
    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Catalog\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);
    }
}

Is there something I am missing? Do I need to add/update anyother files?

Best Answer

The Catalog model image uploader is a bit abstract. It can be used to upload images/files basically anywhere. It does not have a path and a tmp path set for image uploading.
You can create a virtual type based on it and use that one.
You can do that by adding this in the di.xml file of your module.

<virtualType name="[entity]ImageUploader" type="Magento\Catalog\Model\ImageUploader"><!-- replace [entity] with your entity name -->
    <arguments>
        <argument name="baseTmpPath" xsi:type="string">path/to/upload/tmp</argument>
        <argument name="basePath" xsi:type="string">path/to/upload</argument>
        <argument name="allowedExtensions" xsi:type="array"><!-- you can add here file extension restrictions -->
            <item name="jpg" xsi:type="string">jpg</item>
            <item name="jpeg" xsi:type="string">jpeg</item>
            <item name="gif" xsi:type="string">gif</item>
            <item name="png" xsi:type="string">png</item>
        </argument>
    </arguments>
</virtualType>

and then you need to tell magento that your image upload controller uses this virtual type to upload the file. This virtual type instance is basically an instance of the catalog image uploader but with baseTmpPath, basePath and allowedExtensions set to certain values.

Add this also to the di.xml of your module

<type name="[Namespace]\[Module]\Controller\Adminhtml\[Entity]\Image\Upload">
    <arguments>
        <argument name="imageUploader" xsi:type="object">[entity]ImageUploader</argument><!-- again, replace [entity] with the name of your entity. Make sure it matches the name of the virtual type above. -->
    </arguments>
</type>
Related Topic