I have a basic CLI structure environment created. I have a component to display messages/alerts Ie: Login Failed etc…
Since this component is going to be reused thro
You Should hide it inside your alert component using the created life cycle to hide it like this:
`
created: {
setTimeout(() => this.message = [], 1000)
}
Sorry for bumping this thread, but I think that is the best way to achieve this:
Example: Imagine we have the following data that is coming from server response
data() {
return {
serverMessages: [],
}
},
methods: {
onSubmit() {
this.$axios.$get('/myroute')
.then(res => {
this.serverMessages.push(res)
this.$emit('flush-message')
})
.catch(err => {
this.serverMessages.push(err.data)
this.$emit('flush-message')
});
},
mounted() {
let timer
this.$on('flush-message', message => {
clearTimeout(timer)
timer = setTimeout(() => {
this.serverMessages = []
}, 3000)
})
}
This way you can clear the timeout so it doesn't glitch if you have multiple messages for example. You can also integrate similar code to a separate controller.
document.querySelector('.alert').style.display = 'none';
Don't do this. You should not be manipulating the DOM in methods, only in prescribed places like directives and lifecycle hooks. Outside of them, Vue expects to have control of the DOM.
You can control inline styles using your viewmodel. You can also do conditional rendering with v-if. The Vue approach is for you to manipulate your model and have Vue make the DOM reflect it.
I've adapted your code into a runnable snippet below.
Since you put the hideAlert method in the component, I put the associated v-if
there. The test is whether message
(the prop) has a value, so closing is a matter of having the parent clear the message. This is a standard communication function handled with the .sync modifier.
The close button calls the hideAlert
method, and I also put a watcher in so that any time a new message is set, it waits 5 seconds and calls hideAlert
.
The Alert component is self-contained; it does not matter how its prop gets its value, whether the parent gets it from a router component, for example, it only matters whether it has a value or not.
const Alert = {
template: '#alert-template',
props: ['message'],
methods: {
hideAlert() {
// Tell the parent to clear the message
this.$emit('update:message', '');
}
},
watch: {
message(newValue) {
// Close after 5 seconds
if (newValue) {
setTimeout(this.hideAlert, 5000);
}
}
}
};
new Vue({
el: '#app',
data() {
return {
alert: ''
}
},
components: {
'alert': Alert
},
mounted() {
// If alert has a value, it will display. If not, not.
setTimeout(() => {
this.alert = 'Now you have a message';
}, 500);
}
});
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
<alert v-bind:message.sync="alert"></alert>
</div>
<template id="alert-template">
<div v-if="message" class="alert">
<p class="text-brand m-20">
<button class="btn btn-small btn-brand" v-on:click="hideAlert()">Close</button>
{{message}}
</p>
</div>
</template>
Firstly, it's important to understand the Vue and the v-if
directive. v-if
evaluates it's expression and if it evaluates to true
, then Vue will render that element in the DOM, but if not, the element will not be included in the DOM.
This leads to the error message that you're seeing, since when there is no alert to show, there is no element in the DOM matching document.querySelector('.alert')
.
Additionally, it would be better if you have all hiding/showing code in the one component. For example, if you want it in the parent component, your hideAlert()
method should be:
methods: {
hideAlert() {
this.alert = null
}
}
And your alert button component would be:
<button class="btn btn-small btn-brand" v-on:click="$emit('hide')">Close</button>
And where you have the <alert>
tag in the parent would become:
<alert v-if="alert" v-bind:message="alert" @hide="hideAlert"></alert>