Ext JS 4: Drag&Drop upload

Ext JS 4: Drag&Drop upload

HTML 5 provides a lot of really cool features that can easily extend our app functionality. Some of them are drag and drop or multipart upload. Of course I didn’t mention it without reason. Combining them will give us powerful mechanism for uploading files without popup window.

Get necessary files

For purpose of this post we will use source code that is available here. It’s a simple boilerplate for ExtJS 4 app (generated via Sencha Cmd) and JSON server written on ZF.

We will also need a user extension written by me which you can get from our github.

Setup user extension

Files which are important for us are stored under src/ux folder. Copy them to the public/app folder in boilerplate directory and after that add this line:

Ext.Loader.setPath({
    'Ext.ux' : '/app/ux'
});

to public/app.js above line :

Ext.application({
    name: 'MyApp',

    models: [
    ],

Now, extensions should be visible for our app.

Build proper view and configure extension

Next we need to prepare placeholder for drag & drop functionality. Firstly open public/app/view/Viewport.js, scroll to the top of the script and put class ‘Ext.ux.container.Upload’ into ‘requires‘ property.
Downloaded boilerplate already has initialised border layout (here more about it), so find

............
        {
            region: 'center',
            xtype: 'tabpanel',
            layout: 'fit',
            items: [
            ]
        }
............

change ‘xtype‘ from ‘tabpanel‘ to ‘panel‘ and add below code to ‘items‘  array

    {
        xtype: 'uploadbox',
        url: 'api/json/1.0/jsonrpc.php'
    }

save and refresh browser window to see what happen. Yep, it’s works, of course currently only on client side.

Notice

D&D plugin extends container, so can be easily integrated into every component like panel or window.

Ok, open application/Example/Example.php file and after class name declaration add

    /**
     * @param $params
     * @return array
     */
    public function upload($params)
    {
        // this is the place where you should insert your code
        // uploaded file is available under $_FILES array
        // if you want to inform frontend that something goes wrong
        // change true to false under success key in returned array
        return array('success' => true);
    }

Look to Viewport.js again, find line url: ‘api/json/1.0/jsonrpc.php’ and put

    directMethod: 'Example.Example.upload'

after it.
It’s not necessary step if url point directly to proper script. In our case we use JSON server, so we need to tell router which method should be used to process data.

Some configurable options exist:
pattern: property(default): description

  • autoUpload(false): if true file will be uploaded automatically after drop
  • acceptedTypes({}): if empty all dropped files will be accepted, to make some restriction add proper mime types to object
    example: {‘image/png’ : true}
  • params({}): put here additional parameters to send it with files
  • timeout(60 * 1000): number of ms after request will be aborted
  • maxFileSize(500000000): maximum file size in bytes

All displayed messages can be customised. Below some explanation:
pattern: property(param1, ): description

  • emptyDragZoneMsg: displayed in drop zone
  • dragZoneOverMsg(number of dragged files) : text displayed in drop zone when you drag files above it
  • progressMsg(files to process): displayed on bottom of component, inform how many files still need to be processed
  • notAcceptedMsg(file name): displayed in tooltip when dropped file not meet the requirements
  • timeoutMsg(file name): displayed in tooltip when timeout for request has been exceeded

Here you can find some useful information about Ext.String.format which is used to insert parameters into string.

Prevent from opening file in browser

Default behaviour when you dropped file on element is to open it in the browser. To prevent from that add following code to public/app.js

    launch: function () {
        window.ondragenter = function (e) {
            e.dataTransfer.dropEffect = 'none';
            e.preventDefault();
            return false;
        };

        window.ondragover = function (e) {
            e.preventDefault();
            return false;
        };

        window.ondrop = function (e) {
            return false;
        };

        window.ondragleave = function (e) {
            return false;
        };
    }

after line

    autoCreateViewport: true

Dealing with upload events

To have some more control on our upload process we can use existing events. There are plenty of them for each class.

  • Ext.ux.container.Upload
    It’s regular container component so you can use every events available natively. Additionaly you have “ddinit” which is fired when component is ready to use.
    pattern: event(param1,): description

    • ddinit(Ext.ux.upload.DD): As described above fired when component is ready to use
  • Ext.ux.upload.DD
    • dragover(drop zone, number of dragging files, data transfer for event, event): Fired when dragged element is over drop zone.
    • dragout(drop zone): Fired when dragged files left drop zone
    • drop(drop zone, dropped files): Fired when files were dropped.
  • Ext.ux.upload.transport.Xhr
    • notaccepted(file): Fired when file was not accepted.
    • progresschange(event, item): Firing during upload process
    • failure(response, event, item): Fired when error occurred.
    • success(response, event, item): Fired when file was uploaded with success.
    • beforeupload(form data, record): Fires when upload process start.
    • afterupload(status, event, record): Fired whatever file was uploaded or error occurred.
    • timeout(event, record): Fired after request timeout.

Example of use

                {
                    xtype: 'uploadbox',
                    url: 'api/json/1.0/jsonrpc.php',
                    directMethod: 'Example.Example.upload',
                    listeners : {
                        ddinit : function (upload) {
                            upload.getTransport().on(/* event name */'success', function (/* params */){
                                // do whatever you want
                            });
                        }
                    }
                }
Comments:
Johnny
Reply
16/10/2014 at 1:02 AM

Hi,

I got error to integrate this.

“Uncaught TypeError: Cannot read property ‘down’ of undefined” at /app/view/Viewport.js
grid = el.up(‘layout’).down(‘#filegrid’);

I add id/itemId for the west panel still doesn’t work

If i bypass it, there are only grid header (west) and upload tilte (center) without the upload zone

Is there any comment for it?

Albert
Reply
24/11/2014 at 3:20 PM

Hi,

I am trying to configure this to upload a zip file without any success. Tried application/zip, application/x-zip, application/x-zip-compressed, application/octet-stream on Chrome 38, IE11 and Firefox 34.

What about be the issue?

Regards,
Albert

Sebastian
Reply
25/11/2014 at 7:50 PM

You should add ‘application/zip’ key with value set to ‘true’ to acceptedTypes object. Here is the gist for it: https://gist.github.com/mkalafior/1801135a846227df0ed8.
And for the future, if you want to check the mime type of the dropped file, add console.log to the ux/upload/transport/Abstract.js file at line 252.

Barry
Reply
01/12/2014 at 2:27 AM

Hi Sebastian,

I seem to be getting the same error as Albert. I could not configure to accept zip files. After printing some logs, I realize that whenever I upload a zip file, the ‘type’ property is not being populated. I’ve attempted to try many other different file types, like text, html files, js files, images and pdfs, the ‘type’ property is being populated.

Sebastian
Reply
01/12/2014 at 8:19 AM

Barry,
what is your platform (os, browser, browser version) ? To be honest I never had similar problem, so it’s hard for me to say anything in this topic. Maybe if you will send me an example zip file which cause this I will be able to find the reason.

Barry To reply on comment
Reply
01/12/2014 at 8:31 AM

Sebastian,

I am currenlty using Google Chrome v39.0.2171.71 m (64-bit). I performed on numerous zip files, including ones I obtained from the internet (IcoMoon) and various that I zipped it myself.. but still unsuccessful. I will send to your email a zip file I obtained from IcoMoon. If you could check and see if its working on your side..

Sebastian To reply on comment
Reply
01/12/2014 at 12:30 PM

It works on my laptop, will try later to check this also on some windows virtual machines (currently I’m on OS X).

Leave a Reply

Please fill all required fields


Drag circle to the rectangle on the right!

Send