Currently I'm using Laravel 5.2 with Elixir 5 and Vue 2.0.
I'm stuck on how to access and imported component's data from the element view using something like v-if.
Here's my setup:
resources\views\vuetest.blade.php
<div id="app">
<modal v-if="showModal"></modal>
<button>Show Modal</button>
</div>
resources\assets\vuejs\app.js
import Vue from 'vue';
import Modal from './components/modals/show-modal.vue';
Vue.component('modal', Modal);
if (document.getElementById("app")){
var app = new Vue({
el: '#app',
data: {
},
methods: {
},
computed: {
},
components: {
'modal': Modal
}
});
window.vue = app;
}
resources\assets\vuejs\components\modals\show-modal.vue
<template>
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Modal Header</h4>
</div>
<div class="modal-body">
<p>Some text in the modal.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
showModal: false
}
},
components: {
},
}
</script>
Now if I just use this without v-if then it works and pulls in the template correctly.
However if I use what I had in my example it gives me an error:
app.js:967 [Vue warn]: Property or method "showModal" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
(found in <Root>)
If I add data in main app.js it can access it but not in imported child components. I'd rather not make the main app.js messy and better to use imports and keep components in organized subfolders. How can I access these child component data properly?
If you want to change the state of the child component, you can set a ref
on the component and access the properties directly.
In this example, a ref
is set on each modal component.
<bootstrap-modal ref="modal1">
Then, to change a data value in the referenced component, simply set it like you would any other javascript value.
<button type="button" class="btn btn-primary" @click="$refs.modal1.visible = true">Show Modal One</button>
console.clear()
const BootstrapModal = {
template: "#modal-template",
data() {
return {
visible: false
}
},
watch: {
visible(show) {
if (show) $(this.$el).modal('show')
else $(this.$el).modal('hide')
}
},
mounted() {
// listen for the close event
$(this.$el).on("hidden.bs.modal", () => {
this.visible = false
})
}
}
new Vue({
el: "#app",
components: {
BootstrapModal
}
})
<script src="https://unpkg.com/vue@2.4.2"></script>
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<div id="app">
<button type="button" class="btn btn-primary" @click="$refs.modal1.visible = true">Show Modal One</button>
<bootstrap-modal ref="modal1">
<span slot="title">Modal One</span>
<p slot="body">Modal One Content</p>
</bootstrap-modal>
<button type="button" class="btn btn-success" @click="$refs.modal2.visible = true">Show Modal Two</button>
<bootstrap-modal ref="modal2">
<span slot="title">Modal Two</span>
<p slot="body">Modal Two Content</p>
</bootstrap-modal>
</div>
<template id="modal-template">
<div class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><slot name="title">Modal Title</slot></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<slot name="body"><p>Modal Body</p></slot>
</div>
<div class="modal-footer" v-if="$slots.footer">
<slot name="footer"></slot>
</div>
</div>
</div>
</div>
</template>
</div>
You need to declare the var showModal
on your main app.
data: {
showModal: false
},
You also need to toggle that variable everytime they click the button, so add this to your button:
<button @click="showModal = !showModal">Show Modal</button>
Also i suggest to wrap your modal inside template with a div. Since vuejs required this.
<template>
<div>
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Modal Header</h4>
</div>
<div class="modal-body">
<p>Some text in the modal.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</template>
Short answer: you are trying to access property of child component within the scope of parent component (or, the Root instance in your case). You should declare showModal
property on the Parent component instead.
var app = new Vue({
el: '#app',
components: {Modal},
data: {
showModal: false
},
});
and then you would have the button changing the state of your property: <button @click="showModal = true"></button>
The tricky part is close out the modal - because you can't reach your button anymore when the modal is active. You would have to change the state of your parent component from your child component. You can use custom events in this case, like so:
In the show-modal.vue
<div class="modal-footer">
<button type="button" class="btn btn-default" @click="close">
Close
</button>
</div>
...
<script>
export default {
methods: {
close() {
this.$emit('close');
}
}
}
And now, all we have to do is to listen on that custom event we just fired in the main template, like so;
<modal v-if="showModal" @close="showModal = false"></modal>
Not sure if best way but I figured it out with the help from this: VueJS access child component's data from parent
What I did was in my child component I put the following:
<template>
<div>
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Modal Header</h4>
</div>
<div class="modal-body">
<p>Some text in the modal.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
showModal: false
}
},
components: {
},
}
</script>
Then in my vuetest.blade.php I put this:
<div id="app">
<modal v-if="modalData.showModal"></modal>
<button>Show Modal</button>
</div>
Then lastly in my app.js I did this:
import Vue from 'vue';
import Modal from './components/modals/show-modal.vue';
if (document.getElementById("app")){
var app = new Vue({
el: '#app',
data: {
modalData: Modal.data()
},
methods: {
},
computed: {
},
components: {
'modal': Modal
}
});
window.vue = app;
}
I didn't complete the code for the show modal button obviously but at least using this way I can now access child data. I would just need to define this for every component which should account for as many vars it contains inside the data.
Also thanks Carlos for suggestion of wrapping it into a div.