As a newcomer to Vue, I've been enjoying working with Single File Components. Before diving into my main project idea, I decided to experiment with some small components to get a better grasp of the concept.
One such experiment involved creating a component for handling XMLHttpRequests with a progress bar:
https://i.stack.imgur.com/QG15q.png
<template >
<div v-if="showQ">
<div class="text-muted">
<span>{{humanReadableLead}}</span>
<span :class="'text-'+color">{{humanReadableMessage}}</span>
<span>{{humanReadableEnd}}</span>
</div>
<div class="progress">
<div
class="progress-bar progress-bar-striped progress-bar-animated"
:class="'bg-'+color"
role="progressbar"
:style="{width: String(percent)+'%'}"
:aria-valuenow="percent"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
<div class="text-right text-muted form-text text-small">
<span class="float-left">{{xhrMessage}}</span>
<span
class="badge"
:class="'badge-'+color"
data-toggle="tooltip"
data-placement="right"
:title="readyStateTooltip"
>
{{xhr.readyState}}
</span>
<span
class="badge"
:class="'badge-'+color"
data-toggle="tooltip"
data-placement="right"
:title="statusTooltip"
>
{{xhr.status}}
</span>
<span
v-if="transferComplete"
@click="goodbye"
class="badge badge-secondary"
data-toggle="tooltip"
data-placement="right"
title="Dismiss progress bar"
>
×
</span>
</div>
</div>
</template>
<script>
import {httpStatusCodes, httpReadyStateCodes} from './http-responses';
export default {
props: {
method: {
type: String,
default: "GET",
validator: function(value) {
return ["GET", "POST", "PUT", "DELETE"].includes(value)
}
},
url: {
type: String,
required: true
},
async: {
type: Boolean,
default: true
},
success: {
type: Function,
default: function() {
console.log(this.xhr.response)
}
},
readystatechange: {
type: Function,
default: function(event) {
}
},
automaticCloseQ: {
type: Boolean,
default: false
}
},
data: function() {
return {
xhr: new XMLHttpRequest(),
httpStatusCodes:httpStatusCodes,
httpReadyStateCodes:httpReadyStateCodes,
color: "primary",
percent: 0,
humanReadableLead: "",
humanReadableMessage: "",
humanReadableEnd: "",
xhrMessage: "",
showQ: true,
completeQ: false
}
},
computed: {
readyStateTooltip: function() {
var rs = this.xhr.readyState,
rsc = httpReadyStateCodes[rs]
return `Ready state ${rs}: ${rsc}`
},
statusTooltip: function() {
var s = this.xhr.status
// s = s == 0 ? 218 : s
var sc = httpStatusCodes[s]
return `Status ${s}: ${sc}`
},
transferComplete: function() {
return this.completeQ
}
},
methods: {
open: function() {
this.xhr.open(this.method, this.url, this.async)
},
send: function() {
this.xhr.send()
},
goodbye: function() {
this.showQ = false
}
},
created: function() {
var that = this
that.open()
that.xhr.addEventListener("error", function(event) {
that.color = "danger"
that.xhrMessage = "An error has occurred."
})
this.xhr.addEventListener("progress", function(event) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total * 100;
that.percent = percentComplete
} else {
that.percent = 100
that.xhrMessage = "Unable to compute progress information since the total size is unknown."
}
})
that.xhr.addEventListener("abort", function(event) {
that.color = "danger"
that.xhrMessage = "The transfer has been canceled by the user."
});
that.xhr.addEventListener("load", function(event) {
that.color = "success"
that.xhrMessage = "Transfer complete."
that.completeQ = true
if (that.automaticCloseQ) { that.showQ = false }
that.success()
})
that.xhr.addEventListener("readystatechange", function(event) {
that.readystatechange(event)
})
that.send()
}
}
</script>
<style scoped>
</style>
In your index.html:
<div id="request" style="width:50%;" >
<http-request :url="'./<some-file>'"/>
</div>
Using JS:
var progress = new Vue({
el: '#request',
components: { httpRequest }
})
This setup works quite well, but there are a few minor bugs that have me stumped:
- I attempted to define a function
onSuccess
to pass as thesuccess
prop, but it resulted in an error from Vue - The computed property for
statusTooltip
does not update as expected - Setting
automaticCloseQ
always defaults to its initial value, regardless of how I try to bind it
For example:
var onSuccess = function() {console.log('here')}
<http-request :url="'./<some-file>'" :success="onSuccess" :automaticCloseQ="true"/>
What could I be missing here?