Greetings! Here is something I wish to document for future in case any poor soul comes upon this issue.
I’m going to be as brief as I can. The assumptions I’m making are that you already know about Vue, Vuex, and Vuetify.
Let’s begin:
A standard v-alert component looks like this:
<v-alert type="success" dismissible v-model="alert">
This is a success alert that is closable.
</v-alert>
It is bound to the alert value stored in local component state.
<script>
export default {
data: () => ({
alert: false
})
}
</script>
So that when we want to show the alert it is bound to that value. All that is needed is to change that value and the alert will show up, awesome.
Vuetify, has a sweet prop for their components which allows you to pass a dismissible option to auto create an icon button to dismiss the alert. Pretty sweet huh! You bet!
Here’s the problem though, if you are using Vuex to store your state and control when you want it show how would one do so with this component? Theres no way to fire off mutation commits or actions dispatchers when bound to model because it relies on local state.
So here is the solution, we use the computed state and the little-known set and get options to interact with out store.
So with the alert above bound to a v-model="alert" we need to remove it from local state, the data, and place it into a computed property.
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState('logs', ['isError', 'errorObj', 'errorMsg']),
// need to setup a two-way bind here with store state since the 'dismissible'
// prop does not have vuex bindings, so when the click event fires the event
// payload will not reach the store.
alert: {
get () {
return this.$store.state.logs.isError
},
set (v) {
if (!v) { this.$store.commit('logs/setError') }
}
}
}
}
</script>
And now we have a computed value which gets it value from the store and on a change fires off the mutation commit.
The rest of my component looks like this.
<template>
<v-container>
<v-alert dismissible color="error" v-model="alert" icon="warning">
<v-card flat dark color="error">
<v-card-title class="title">Oops! {{ errorMsg }}</v-card-title>
<!-- <v-card-title class="title">Oops! {{ errorObj.response.status }}: {{ errorObj.response.statusText }}</v-card-title> -->
<v-card-text>
<p>Unable to perform <code>{{ errorObj.request.toString() }}</code></p>
<p>
Occured when performing <code>{{ errorObj.config.method }}</code> request
with <code>{{ errorObj.config.data }}</code>
to <code>{{ errorObj.config.url}}</code>
</p>
</v-card-text>
</v-card>
</v-alert>
</v-container>
</template>
Store. When I commit a XHR request to my api If there is an error I retrieve the error object and stick it into my store to deal with.
state: {
...
isError: false,
errorObj: '',
errorMsg: '',
...
},
mutations: {
...
setError: (state) => { state.isError = !state.isError },
setErrorMsg: (state, payload) => { state.errorMsg = payload },
setErrorObj: (state, payload) => { state.errorObj = {...payload} },
...
},
actions: {
voidLog: async ({commit}, logId) => {
commit('setLoading')
try {
await logService.void_log(logId)
} catch (err) {
commit('setError')
commit('setErrorMsg', err.toString())
commit('setErrorObj', err)
}
commit('setLoading')
}
}
To help get my point across here is a short video of my the project I was working on.