Perfect use of Dropzone with Laravel and AngularJS

# Introduction:

This article covers the complete steps to understand and use of Dropzone JS, Dropzone is really flexible and specifically improves file uploading feature for end user, It enables drag and drop option while uploading a file, so it is always good to implement drop zone JS into the application when we require to handle file upload.

Recently I have realsed a tutorial on Laravel File AngularJS upload, if you want to learn handling file upload in Laravel framework and AngulaJS only then you can read previous tutorial before going to handle this tutorial.

In this tutorial we be developing a file upload demo with Laravel 5.6 and AngularJS and our demo will have following list of features:

  • Upload multiple files by dragging and dropping on dropzone box
  • Store records for each uploaded file into the database
  • List all uploaded files
  • Delete exiting file from directory as well a associated record from the database

By reading this tutorial you will learn use of dropzone library with AngularJS and Laravel framework. 

So let’s begin:

# Project Setup

In this step we are going to create new Laravel project and setup database settings along with creating new database (will be using MySQL database).

If you already have your  existing project, you can skip this step and start from the next step onwards to implement drag and drop feature into your project.

Create new project using Laravel installer or Composer:

$ laravel new laravel-dropzonejs-file-upload

OR

$ composer create-project --prefer-dist laravel/laravel laravel-dropzonejs-file-upload

Database Settings:

Open up your project into your code editor, in my case I am using visual studio code.

Next go ahead and open .env file and update add database name, the database username and password.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-dropzone
DB_USERNAME=root
DB_PASSWORD=

Create new database

Open MySQL and create new database as you have mentioned in the above step.

# Create Database Migration and Eloquent Model

We are going to need a database migration file to create new table into the database and a eloquent model file to handle database operations.

To generate both files use following artisan command 

$ php artisan make:model File -m

You should have create_files_table and File.php files generated 

Open up File.php file into code editor from app/ directory and add guarded property as all Eloquent models protect against mass-assignment.

File.php:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class File extends Model
{
    protected $guarded = [];
}

Next update migration file and add new fields as showing below 

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateFilesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */    public function up()
    {
        Schema::create('files', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('size');
            $table->string('extention');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */    public function down()
    {
        Schema::dropIfExists('files');
    }
}

Now migrate the database to create files table using command given below:

$ php artisan migrate

We are all set about the database setup in next step let’s focus on angularjs and dropzone installation.

# AngularJS, Dropzone installation and Setup into Laravel:

To handle this step you should have node package manager installed into your system so that you can install required dependencies and compile your front end code with Laravel mix.

To install npm you can simply install Node.Js from nodejs.org, npm comes along the nodejs.

okay so now I assume that you have npm installed and ready to use.

Note –  if you haven’t installed node dependencies for your Laravel project then use following command to install them first.

$ npm install

Wait until your get all the dependencies installed, you should get message showing below when the installation process is complete:

added 1260 packages from 716 contributors and audited 8994 packages in 49.728s
found 0 vulnerabilities

Next install angularjs

$ npm install angular --save-dev

Now install dropzone using following command

$ npm install dropzone --save

Open bootstrap.js file from /resources/assets/js/ and replace it with the script given below:

/resources/assets/js/bootstrap.js:


window._ = require('lodash');
window.Popper = require('popper.js').default;

try {
    window.$ = window.jQuery = require('jquery');

    require('bootstrap');
} catch (e) {} 

// Import AngularJS and Dropzone JS 
import 'angular';
import dropzone from 'dropzone';

// Declare Dropzone to use globaly into the project
window.dropzone = dropzone;

If you notice above code, you will see that we have imported angular as well as dropzone, also take a note we are declaring dropzone globally so that we can use anywhere int the project using window object.

Next open up app.scss file from /resources/assets/sass/  directory and a line to import dropzone css file. as showing below script

/resources/assets/sass/app.scss:

// Fonts
@import url('https://fonts.googleapis.com/css?family=Nunito');

// Variables
@import 'variables';

// Bootstrap
@import '~bootstrap/scss/bootstrap';

// Dropzone CSS
@import '~dropzone/dist/dropzone.css';

.navbar-laravel {
  background-color: #fff;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
}

Now are are good with angularjs and dropzone installation, next will have to use into into our JS, but before going to do that, first let’s design our front end view.

# Design Frontend View

In this step we will design a view, the view will have a dropzone box along with list of uploaded files.

Go ahed and open welcome.blade.php view from /resources/views/ directory. You can use different file if you are using existing project don’t get confuse.

Replace view following script, If notice the script basically we are setting up the page structure along with AngularJS module and controller setup.

Don’t forget to note the window.laravel declaration here, this is the important variable we are going to use this token while handling http/xhr requests from front end. without this the project will not work.

/resources/views/welcome.blade.php:

<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel AngularJS file Upload with dropzone</title>

         <link rel="stylesheet" href="css/app.css">

        <script>
            window.Laravel = <?php echo json_encode([
                    'csrfToken' => csrf_token(),
            ]); ?>
        </script>
    </head>
    <body ng-app="App">

        <div ng-controller="FileUploadController" ng-clock>
           // Rest of the further script will be here 
        </div> 

        <script src="js/app.js"></script>
    </body>
</html>

Next add following script into the same above page and it should go into the ng-controller section:

 <div class="container">
               <div class="row">
                   <div class="col">
                        <br>
                        <h1>Laravel AngularJS file Upload with dropzone</h1>
                        <hr>

                        {{--  Dropzone  --}}
                        <div class="dropzone" mydropzone options="options" callbacks="callbacks" methods="methods"></div>
                        
                        <br>
                        <h2>All Uploaded Files</h2>

                        {{--  File Listing   --}}
                        <div class="table-responsive-sm">
                            <table class="table table-bordered">
                                <thead>
                                <tr>
                                    <th scope="col">No</th>
                                    <th scope="col">Image</th>
                                    <th scope="col">Name</th>
                                    <th scope="col">Remove</th>
                                </tr>
                                </thead>
                                <tbody>
                                    <tr ng-repeat="file in allFiles" ng-if="allFiles.length > 0">
                                        <td>@{{ $index + 1 }}</td>
                                        <td><img height="100" ng-src="/images/@{{ file.id }}.@{{ file.extention }}"></td>
                                        <td>
                                            @{{ file.name }}
                                        </td>    
                                        <td>
                                            <button ng-click="deleteFile(file, $index)" class="btn btn-outline-danger btn-sm">X</button>
                                        </td>
                                    </tr>
                                    <tr ng-if="allFiles.length == 0">
                                        <td colspan="5">
                                            Files not found!
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                   </div>
               </div>
           </div>

If read above script carefully you will have we have number of things written up there, the page title, dropzone implementation along with custom directive and also lopping files into the html table with different columns.

# AngularJS Controller and Custom Directive for dropzone

This specifically important step, in this step we are going define our AngularJS controller along with the a directive fro dropzone, the directive will ensure the few things such as render dropzone, bind events and methods.

Go ahead and open app.js file from /resources/assets/js/ folder and replace with the following script 

/resources/assets/js/app.js:

import './bootstrap';

var app = angular.module('App', [], ['$httpProvider', function ($httpProvider) {
    $httpProvider.defaults.headers.post['X-CSRF-TOKEN'] = Laravel.csrfToken;
}]);

app.controller('FileUploadController', ['$scope', '$http', function ($scope, $http) {


    // Store all the files 
    $scope.allFiles = [];

    // load files from the server 
    $scope.loadAllFiles = function()
    {
        $http.get('/all-files').then(function success(e) {
            $scope.allFiles = e.data.files;
        });
    };
    $scope.loadAllFiles();// 


    // dropzone configuration, the url and headers part is important make sure to focus on this
    $scope.options = {
        url: '/upload-file',
        maxFilesize: 100,
        acceptedFiles: 'image/jpeg, images/jpg, image/png',
        headers: { 'X-CSRF-TOKEN': Laravel.csrfToken}
    };

    $scope.callbacks = {
        'addedfile': function (file) {
            // can add more action if needed when user add new file to upload
        },
        'success': function (file, xhr) {
            // file got successfully uploaded
            $scope.allFiles.push(xhr.file);
        },
        'removedfile': function(file){
            // Dropzone remove file event
        },
        uploadprogress: function (file, progress, bytesSent) {
            // Display file uploading progress
        }
    };

    // Remove file from server 
    $scope.deleteFile = function(file, index)
    {
        $http.post('/delete-file', {id: file.id}).then(function success(e) {
            $scope.allFiles.splice(index, 1);
        });
    };
    
}]);

app.directive('mydropzone', function () {
    return {
        restrict: 'AE',
        replace: true,
        scope: {
            options: '=?',
            methods: '=?',
            callbacks: '=?',
        },
        link: function (scope, el) {
            Dropzone.autoDiscover = false;
            var drop = new Dropzone(el[0], scope.options);

            scope.methods = scope.methods || {};

            scope.methods.getDropzone = function () {
                return drop;
            };

            scope.methods.getAllFiles = function () {
                return drop.files;
            };

            var controlMethods = [
                'removeFile', 'removeAllFiles', 'processQueue',
                'getAcceptedFiles', 'getRejectedFiles', 'getQueuedFiles', 'getUploadingFiles',
                'disable', 'enable', 'confirm', 'createThumbnailFromUrl'
            ];

            angular.forEach(controlMethods, function (methodName) {
                scope.methods[methodName] = function () {
                    drop[methodName].apply(drop, arguments);
                    if (!scope.$$phase && !scope.$root.$$phase) scope.$apply();
                };
            });

            if (scope.callbacks) {
                var callbackMethods = [
                    'drop', 'dragstart', 'dragend',
                    'dragenter', 'dragover', 'dragleave', 'addedfile', 'removedfile',
                    'thumbnail', 'error', 'processing', 'uploadprogress',
                    'sending', 'success', 'complete', 'canceled', 'maxfilesreached',
                    'maxfilesexceeded', 'processingmultiple', 'sendingmultiple', 'successmultiple',
                    'completemultiple', 'canceledmultiple', 'totaluploadprogress', 'reset', 'queuecomplete'
                ];
                angular.forEach(callbackMethods, function (method) {
                    var callback = (scope.callbacks[method] || angular.noop);
                    drop.on(method, function () {
                        callback.apply(null, arguments);
                        if (!scope.$$phase && !scope.$root.$$phase) scope.$apply();
                    });
                });
            }
        }
    };
});

If you notice above script from the first line to last you will notice few important things going on here, details points are here:

  • Import bootstrap.js file where we have added our external library files such as bootstrap, jquery, proper.js, angular and dropzone.
  • AngularJS module declaration with http provider configuration to have new field added to the header called X-CSRF-TOKEN
  • AngularJS Controller definition  along with dropzone configuration and methods to load files from server as well as delete the file from the server
  • Custom directive for dropzone called mydropzone – this important one read it carefully to understand the declaring, rendering and other configuration part such as events binding methods binding.

Recommended – Incredible Laravel 5 Angular JS File Upload

# Laravel Routes and Controller Setup

As we see we have complete all the front end part of the demo, now it’s time look at the backend where we are going to handle different request coming from the front end such as upload and store the file, list out the files and delete existing file.

To this let’s generate new controller and required define methods:

$ php artisan make:controller FileUploadsController

Open up FileUploadsController.php file and add following definiation:

<?php
/**!
 * Laravel AngularJS file Upload using dropzone
 * @author Yogesh Koli <yogesh@itechempires.com>
 * https://www.itechempires.com
 */
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\File;

class FileUploadsController extends Controller
{
    protected $file_upload_path;

    public function __construct()
    {
        $this->file_upload_path = public_path('/images');
    }

    /**
     * List all the files
     *
     * @return void
     */    public function list()
    {
        $files = File::all();

        if (request()->wantsJson()) {
            return response()->json(['files' => $files], 200);
        }

        return response(['files' => $files], 200);
    }

    /**
     * Upload the file
     *
     * @return void
     */    public function upload()
    {
        $this->validate(request(), [
            'file' => 'required|file'
        ]);

        $request_file = request()->file('file');

        $size = $request_file->getClientSize();

        $extention = strtolower($request_file->getClientOriginalExtension());

        $name = $request_file->getClientOriginalName();

        $file = File::create([
            'name' => $name,
            'size' => $size,
            'extention' => $extention,
        ]);

        $request_file->move($this->file_upload_path, $file->id . '.' . $extention);

        return response()->json(['file' => $file], 200);
    }

    /**
     * Delete Given file
     *
     * @return void
     */    public function deleteFile()
    {
        $this->validate(request(), [
            'id' => 'required'
        ]);

        $file = File::find(request('id'));

        unlink($this->file_upload_path . '/' . $file->id . '.' . $file->extention);

        $file->delete();

        return response()->json(['message' => 'Given file has been deleted'], 200);
    }
}

Now will needs to define routes to align income requests, open web.php file and add following new routes:

<?php

Route::get('/', function () {
    return view('welcome');
});

Route::get('/all-files', 'FileUploadsController@list');
Route::post('/upload-file', 'FileUploadsController@upload');
Route::post('/delete-file', 'FileUploadsController@deleteFile');

# Compile Assets using Laravel Mix:

We are almost done with the development part, now we just have compile the assets (the css and JS part) and test the project.

If you remember at the start of this tutorial I told your about npm and compile the assets with laravel mix.

Okay so you don’t have do much things you just needs to run following command and your done.

$ npm run dev

If you success message like this:

 DONE  Compiled successfully in 6202ms                                                                                                                  17:24:35

       Asset     Size  Chunks                    Chunk Names
  /js/app.js  2.52 MB       0  [emitted]  [big]  /js/app
/css/app.css   206 kB       0  [emitted]         /js/app

Then you can go ahead and test the demo into the browser, you should see output as showing into the screen below:

Dropzone AngularJS and Laravel File upload Demo

Try uploading single or multiple files and then try deleting files as well see the result.

All done, let me know if you get any issues using comment box below and keep sharing I Tech Empires tutorials to your friends on social media.

Yogesh Koli

Software engineer & Blogger live in India, has 8+ years of experience working with the Front-end and Back-end Web Application Development.

Recent Posts

Complete guide of using Laravel 6 Eloquent Subquery Enhancements

Learn How to use laravel frameworks new improved feature called Eloquent Subquery and get example…

1 year ago

3 Useful examples of using Array Map function in PHP – Best Practices

Learn how to use php array map function with easy and essential tutorial to modify…

1 year ago

Working with PHP Array Filter Function – Best Practices

Learn how to use php array filter function with easy and essential tutorial to filter…

1 year ago

How to add Access Modifiers with Constructor Parameters in TypeScript

Want to know how to refactor your Typescript class, Learn here utilising Typescript of the…

1 year ago

What is Access Modifiers and how to use Access Modifiers in TypeScript ?

What is Access Modifiers in typescript, how to use Access Modifiers, when to use them,…

1 year ago

Top 10 Super Useful Packages to Improve Laravel applications in 2019

This tutorial provide ultimate list of package those are top 10 on packagist and super…

1 year ago