I am new to Vue and I am so far enjoying the Single File Components.
Prior to making what I really want to make, I figured I would try some small things to see if I grasp the concept.
So I set off to make a component for opening an XMLHttpRequest, with a progress bar.
<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 occured."
})
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>
and in index.html
<div id="request" style="width:50%;" >
<http-request :url="'./<some-file>'"/>
</div>
with JS
var progress = new Vue({
el: '#request',
components: { httpRequest }
})
and this works fairly nicely...
However, there are a few small bugs that for the life of me I can not figure out:
- I would like to define a function
onSuccess
that I pass to the propsuccess
, but this throws an error from Vue - the computed properties for
statusTooltip
does not get updated - trying to set
automaticCloseQ
results in the default value no matter how I try to bind
e.g.
var onSuccess = function() {console.log('here')}
<http-request :url="'./<some-file>'" :success="onSuccess" :automaticCloseQ="true"/>
what am I missing?
Hope this helps.
I would like to define a function onSuccess that I pass to the prop success, but this throws an error from Vue
You are defining
onSuccess
outside of Vue. It should be defined in Vue'smethods
the computed properties for statusTooltip does not get updated
In Javascript, an object is passed by reference.
xhr
always reference the same object. That's why the computed value won't update. See https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats . 1 way to solve it is to have another reactive data calledxhrStatus
and update this status manually in the xhr's event listeners.trying to set automaticCloseQ results in the default value no matter how I try to bind
(I dunno what this means...)