Vue.js + Flask + Flask-SQLAlchemy: Part 2— Vue.js + plugins

TH Zhao
5 min readJun 9, 2019

Now, we need to learn not to reinvent wheels and there are many great projects out there existing solving the problems we also have..

In this chapter, I will show how to give our Vue.js frontend some Google Materials Design feeling with Vuetify. And then I also show you a neat component Vue2-dropzone that allows drag and drop file upload.

Installation of Vuetify:

  1. use vue ui and install vuetify
  2. In backend/main.js add following:
import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css' Vue.use(Vuetify)

3. In backend/public/index.html:

<head>
<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons' rel="stylesheet">
</head>

Usage of vuetify:a

There are many layouts preset:

We can modify frontend/src/App.vue as well:

Here we use the vuetify to change our overall framework with v-navigation-drawer and v-bottom-nav.

  • First, everything has to be inside <v-app> ... </v-app>
  • Then v-navigation-drawer are the menu that appears from the side
  • Then the <v-content>...</v-content> is where we put <router-view/>
  • Lastly the <v-bottom-nav> is just as the name indicates. But this is what gives us the ‘whatsapp’ feeling at the bottom

Few notes:

  1. Now we want to change the router-view by click different menu in the v-navigation-drawer and <v-bottom-nav> . To achieve this, the previous <router-link to=”/”> is too limiting. Instead, we use this.$router.push({name:name_of_route}) . The name_of_route is the same asfrontend/router.js . I create a wrapper function change_view(name) as I find this is the only way to work when click button with @click="change_view()"
  2. changing datadrawer: True can allow expanded drawer when booting up.
  3. We are using data current_view to set the default view and memorise the current_view so that v-bottom-nav is correctly highlighted.

Update the style of frontend/src/components/todo_rest.vue

  • Good tutorial https://chunkbytes.com/2019/01/learn-vuetify-grid-system/
  • To format our framework, vuetify uses grid that can have total of 12 divisions per row. https://vuetifyjs.com/en/framework/grid
  • The hierarchy is <v-container> that contains everything grid-list is used to add gutter.
  • v-layout refers to a row, it can accept other text alignment tags like align-center justify-center.
  • v-flex refers to the element inside this row, we can then choose the size and alignment with <v-flex xs8> , the xs or md mean the size of the screen, so we can have different ratios on different screens.
  • Somehow after installing vuetify, the normal <button> will stop working, we have to replace it with <v-btn>
  • We also add a cute little v-progress-circular progress bar to track how much tasks we have done. And this is the only place where we need to change script a bit to calculate the ratio.

Drag-and-drop upload is arguably the best method to upload files, but unfortunately, many websites haven’t implemented this yet.

We can do this easily today using Vue with a component vue2-dropzone or just Dropzone I suppose.

  1. Download the vue2-dropzone in vue ui
  2. create 2 files: frontend/src/component/upload_download.vue and I am lazy to usefrontend/src/views/About.vue , but we should really make a new view.
  3. upload_download.vue
  4. As we are not going to use it in many pages, so I won’t import the dropzone globally in main.js.

frontend/src/components/upload_download.vue

The basic examples to use vue2-dropzone can be found here:

  1. The most important thing is the data dropzoneOptions , in which url will be your API address to accept file upload via POST.
  2. There are also many events that we can use to call with other methods. In this case, I use @vdropzone-removed-file=cancel_upload for the situation that our user uploaded and decide to remove it. Then we can
  3. There are also some methods that we can programmatically control the dropzone, like .removeAllFiles(), which we can assign to a button.
  4. We also want to demonstrate you can host and download a file from the backend server. The implementation of the backend server is right below. We can show the image via <img src="http://localhost:5000/file"/> and we can use our customised method download_file() to download it.

But really the important things are happening in the backend API server to handle the upload.

  1. First we create a new sheet to store filepath of the uploaded file.
  2. In our config.py file, we added one line:

UPLOAD_FOLDER=r'saved_files\\'

Now we can use app.config[‘UPLOAD_FOLDER’] to represent this path.

3. Regarding the safety of the file that can be uploaded, see more at http://flask.pocoo.org/docs/1.0/patterns/fileuploads/

4. Here we incorporate another database sheet FileEntry to record the name of the file and filepath. This allows user to update their file in the future, or delete the file on our filesystem.

5. Flask has a handy request.files() , but if we use this and no file has been supplied, it will return 409 error. Instead, if we use request.files.get('file') , this will just return None. This applies to all the request sub-methods. See more at: https://scotch.io/bar-talk/processing-incoming-request-data-in-flask

By calling request.args.get('language'), our application will continue to run if the language key doesn't exist in the URL. In that case, the result of the method will be None. If we use request.args['language'], the app will return a 400 error if language key doesn't exist in the URL.

6. Then we just use file.save() onto our filesystem. Here os.path.join is more platform agnostic, so we can move from Windows to Mac to Linux without a problem.

7. As the frontend can capture several different events, one will be @vdropzone-removed-file=cancel_upload . The frontend method will send a POST to the same API, but this time as a json data. Our backend will read the json data with request.get_json() .

8. Lastly, to allow frontend access backend filesystem, flask has a nice and safe send_from_directory(folder, filename) .

Now, it is a bit tricky to send the filename to backend I realise. This discussion has greatly helped: https://stackoverflow.com/questions/41543951/how-to-change-downloading-name-in-flask

We can send the filename via response.headers[‘x-suggested-filename’] , but our frontend cannot access to this header due to the CORS. This can be solved by using flask-CORS module, and with CORS(app, expose_headers=[“x-suggested-filename”]) .

Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell a browser to let a web application running at one origin (domain) have permission to access selected resources from a server at a different origin.

Now in our frontend, we can obtain the filename simply with var filename = response.headers[‘x-suggested-filename’]

  • We can also directly

--

--