Implementing file uploads

In the card modal window, once a user clicks the Attachment button, the native file manager window will show up. Once the user selects a file, the frontend will send that file to the server and receive the saved file's information.

There are two parts to implementing this file upload feature. One is that we need to build a consistent UI of the Attachment button. By default, the UI of the element <input type="file" /> looks different in Chrome, Firefox, Safari, and IE, which is the only HTML element that will trigger the opening of the native file manager window. The second one is to send the file. Within <form>, we can add the file input to send the selected file when the form is submitted. However, in our card modal window, we need to send the file immediately after it is selected. Users won't need to click another button to do that.

To implement this file upload feature, we will use the library, jQuery File Upload (https://github.com/blueimp/jQuery-File-Upload), and we will create a standalone component, called Uploader.vue, to isolate the library from the other parts of the application.

The following is how the <template> part of Uploader.vue looks:

<template>
<div class="fileinput-button">
<font-awesome-icon :icon="icon" class="icon" v-if="icon"/> {{ label }}
<input :id="id" type="file" name="file" multiple>
</div>
</template>

As you can see, we wrap the file input inside a .fileinput-button element with an icon and the label. The multiple attribute indicates that users will be able to select multiple files in the native file manager window.

jQuery File Upload will make this file input invisible by applying the following style to it:

.fileinput-button input {
position: absolute;
top: 0;
right: 0;
margin: 0;
opacity: 0;
font-size: 200px !important;
direction: ltr;
cursor: pointer;
}

In this way, the user will only see the button's icon and label, but will still be able to click the button to open the native file browser.

The following is the <script> part of Uploader.vue:

<script>
...
export default {
name: 'Uploader',
props: ['id', 'url', 'icon', 'label'],
watch: {
url () {
if (!this.url) {
return
}

$('#' + this.id).fileupload({
url: this.url,
dataType: 'json',
add: (e, data) => {
this.$emit('uploading', data.files[0])
data.submit()
},
fail: (e, data) => {
this.$emit('failed',
data._response.jqXHR.responseJSON.message)
},
done: (e, data) => {
this.$emit('uploaded', data.result)
},
progress: (e, data) => {
let progress = parseInt(data.loaded / data.total * 100, 10)
this.$emit('progress', progress)
}
})
}
}
}
</script>

This Uploader component has four properties, id, which is used to specify the ID attribute of the file input, url, which is used to specify the server's endpoint that will receive the uploaded file, icon, which is used to display an icon in the button, as well as label, which is used to specify the label of the Upload button.

We add a watcher to the url property. Its value is different for each card. For upload card attachment, the value of the url property will be in this format: /api/cards/{cardId}/attachments. As you can see in the url watcher method, we initiate the jQuery File Upload plugin for the file input. The add, fail, done, and progress methods are the event listeners that will be invoked when a file is added to the upload queue, when an upload has failed, when an upload is done, and when an upload is in progress, respectively, and once an event listener is invoked, we will publish a corresponding event to the client of the Uploader component, which is the CardModal in this case.

The following is how the Uploader component looks in CardModal.vue:

<template>
<div class="modal" id="cardModal">
...
<uploader
id="cardAttachment"
:url="attachmentUploadUrl"
icon="paperclip"
label="Attachment"
@uploading="onUploadingAttachment"
@progress="onUploadingProgressUpdated"
@failed="onAttachmentUploadFailed"
@uploaded="onAttachmentUploaded"/>
...
</div>
</template>

For the id property, we use a static ID, and the url property is bound to the attachmentUploadUrl, which is a computed property. We also listen to the four events that Uploader emits, as in the following:

<script>
...
export default {
name: 'CardModal',
...
data () {
return {
...
uploadingCount: 0
...
}
},
computed: {
...
attachmentUploadUrl () {
return this.card.id ? '/api/cards/' + this.card.id + '/attachments' : ''
}
},
...
methods: {
...
onUploadingAttachment () {
this.uploadingCount++
},
onUploadingProgressUpdated (progress) {
console.log('Uploading progress: ' + progress + '%')
},
onAttachmentUploadFailed (error) {
this.uploadingCount--
notify.error(error)
},
onAttachmentUploaded (attachment) {
this.uploadingCount--
this.attachments.push(attachment)
...
},
...
}
}
</script>

As you can see, we use a counter, uploadingCount, to track if there is any file being uploaded. Inside the onUploadingAttachment method, we increase it every time the uploading event is triggered, and we decrease it accordingly inside the onAttachmentUploadFailed method and the onAttachmentUploaded method, and once a file is uploaded successfully, we will add it into the attachments array so that it will be shown in the attachments list.

For other parts of the CardModal.vue, such as adding comments, and listing activities, you can find the details in the commit record.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.27.171