Vue.js Changing props
Referencing the code in your fiddle
Somehow, you should decide on one place for the state to live, not two. I don't know whether it's more appropriate to have it just in the Alert
or just in it's parent for your use case, but you should pick one.
How to decide where state lives
Does the parent or any sibling component depend on the state?
- Yes: Then it should be in the parent (or in some external state management)
- No: Then it's easier to have it in the state of the component itself
- Kinda both: See below
In some rare cases, you may want a combination. Perhaps you want to give both parent and child the ability to hide the child. Then you should have state in both parent and child (so you don't have to edit the child's props inside child).
For example, child can be visible if: visible && state_visible
, where visible
comes from props and reflects a value in the parent's state, and state_visible
is from the child's state.
I'm not sure if this is the behavour that you want, but here is a snippet. I would kinda assume you actually want to just call the toggleAlert
of the parent component when you click on the child.
var Alert = Vue.component('alert', {
template: `
<div class="alert" v-if="visible && state_visible">
Alert<br>
<span v-on:click="close">Close me</span>
</div>`,
props: {
visible: {
required: true,
type: Boolean,
default: false
}
},
data: function() {
return {
state_visible: true
};
},
methods: {
close() {
console.log('Clock this');
this.state_visible = false;
}
}
});
var demo = new Vue({
el: '#demo',
components: {
'alert': Alert
},
data: {
hasAlerts: false
},
methods: {
toggleAlert() {
this.hasAlerts = !this.hasAlerts
}
}
})
.alert {
background-color: #ff0000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo" v-cloak>
<alert :visible="hasAlerts"></alert>
<span v-on:click="toggleAlert">Toggle alerts</span>
</div>
According to the Vue.js component doc:
When the parent property updates, it will flow down to the child, but not the other way around. So, how do we communicate back to the parent when something happens? This is where Vue’s custom event system comes in.
Use $emit('my-event)
from the child to send an event to the parent. Receive the event on the child declaration inside the parent with v-on:my-event
(or @my-event
).
Working example:
// child
Vue.component('child', {
template: '<div><p>Child</p> <button @click="hide">Hide</button></div>',
methods: {
hide () {
this.$emit('child-hide-event')
}
},
})
// parent
new Vue({
el: '#app',
data: {
childVisible: true
},
methods: {
childHide () {
this.childVisible = false
},
childShow () {
this.childVisible = true
}
}
})
.box {
border: solid 1px grey;
padding: 16px;
}
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app" class="box">
<p>Parent | childVisible: {{ childVisible }}</p>
<button @click="childHide">Hide</button>
<button @click="childShow">Show</button>
<p> </p>
<child @child-hide-event="childHide" v-if="childVisible" class="box"></child>
</div>
After a read of your latest comments it seems that you are concerned about having the logic to show/hide the alerts on the parent. Therefore I would suggest the following:
parent
# template
<alert :alert-visible="alertVisible"></alert>
# script
data () {
alertVisible: false,
...
},
...
Then on the child alert you would $watch the value of the prop and move all logic into the alert:
child (alert)
# script
data: {
visible: false,
...
},
methods: {
hide () {
this.visible = false
},
show () {
this.visible = true
},
...
},
props: [
'alertVisible',
],
watch: {
alertVisible () {
if (this.alertVisible && !this.visible) this.show()
else if (!this.alertVisible && this.visible) this.hide()
},
...
},
...
If the prop is only useful for this child component, give the child a prop
like initialVisible
, and a data
like mutableVisible
, and in the created
hook (which is called when the component's data structure is assembled), simply this.mutableVisible = this.initialVisible
.
If the prop is shared by other children of the parent component, you'll need to make it the parent's data
to make it available for all children. Then in the child, this.$emit('visibleChanged', currentVisible)
to notify the parent to change visible
. In parent's template, use <ThatChild ... :visibleChanged="setVisible" ...>
. Take a look at the guide: https://v2.vuejs.org/v2/guide/components.html