How to set parent component data from child component in vuejs - javascript

Below is the parent component and child component. I am trying to access tabs_value data in the parent component from the child component but it is returning as undefined.
this.$parent.tabs_value returns as undefined when I try to access it inside the run method in the child component.
Please help me find where I am going wrong? Below is the code
Parent Component
<template>
<div>
<v-layout row wrap>
<v-flex xs12 sm12 lg12>
<div>
<v-card>
<v-tabs v-model="tabs_value"
color="black"
centered
show-arrows
>
<v-toolbar-title>Custom</v-toolbar-title>
<v-spacer></v-spacer>
<v-tab href="#build">Build</v-tab>
<v-tab href="#run">Run</v-tab>
</v-tabs>
<v-tabs-items v-model="tabs_value">
<v-tab-item value="#build" id="build">
<Build ref="build_reports" />
</v-tab-item>
<v-tab-item value="#run" id="run">
<Run :reports="reports" ref="run_reports" />
</v-tab-item>
</v-tabs-items>
</v-card>
</div>
</v-flex>
</v-layout>
</div>
</template>
<script>
import Build from 'views/build.vue'
import Run from 'views/run.vue'
import URLs from 'views/routes'
export default {
components: {
Build,
Run
},
data: function() {
return {
tabs_value: 'build',
isLoaded: true,
reports: []
}
},
created() {
this.fetch();
},
methods: {
fetch() {
this.$axios.get(URLs.REPORTS_URL)
.then(response => {
this.reports = response.data
});
}
}
};
</script>
Child Component run.vue
<template>
<div>
<v-layout row wrap>
<v-flex xs12 sm12 lg12>
<div>
<v-card>
<div>
<v-data-table
:headers="headers"
:items="reports"
hide-default-footer
:mobile-breakpoint="0">
<template slot="item" slot-scope="props">
<tr>
<td>{{props.item.name}}</td>
<td>
<div>
<v-tooltip attach left>
<template v-slot:activator="{ on, attrs }">
<a v-bind="attrs" v-on="on"
class="" href='javascript:void(0);'
#click="run(props.item)"><i small slot="activator" dark color="primary" class="fas fa-play"></i></a>
</template>
<span>Run</span>
</v-tooltip>
</div>
</td>
</tr>
</template>
<template slot="no-data" >
<v-alert id='no-data' :value="true" color="error" icon="warning">
No Reports Yet
</v-alert>
</template>
</v-data-table>
</div>
</v-card>
</div>
</v-flex>
</v-layout>
</div>
</template>
<script>
import URLs from 'views/routes'
export default {
props: ['reports'],
data: function() {
return {
headers: [
{ text: 'Name', value: 'name', sortable: false },
{ text: 'Actions', sortable: false }
],
}
},
methods: {
run(report) {
debugger
// this.$parent.tabs_value returns as undefined
}
}
}
</script>

you can use component events i.e $emit.
Below is example which will tell you how to use $emit. (https://vuejs.org/guide/components/events.html#emitting-and-listening-to-events)
Parent Component
<template>
<ChildComponent #updateTabsValue="updateTabsValue"/>
</template>
<script>
export default {
data(){
return {
tabsValue: 'tabs',
};
},
methods:{
updateTabsValue(val){
this.tabsValue = val;
}
},
}
</script>
Child Component
<template>
<button #click="$emit('updateTabsValue','newVal')"/>
</template>

Related

vue.runtime.esm.js?TypeError: Cannot read properties of undefined

I create this component from the vuetify documentation.
https://github.com/vuetifyjs/vuetify/blob/master/packages/docs/src/examples/v-card/prop-outlined.vue
<template>
<v-card class="mx-auto" max-width="344" outlined>
<v-list-item three-line>
<v-list-item-content>
<div class="text-overline mb-4">OVERLINE</div>
<v-list-item-title class="text-h5 mb-1"> {{ person.name }} </v-list-item-title>
<v-list-item-subtitle> {{ person.role }} </v-list-item-subtitle>
</v-list-item-content>
<v-list-item-avatar tile size="80" color="grey"></v-list-item-avatar>
</v-list-item>
<v-card-actions>
<v-btn outlined rounded text> Message </v-btn>
</v-card-actions>
</v-card>
</template>
<script>
export default {
name: 'Person',
props: {
person: Object
}
}
</script>
I import them like so... was intended to use it in a loop 5 times.
<template>
<div class="teams">
<h1 class="subtitle-1 grey--text">Teams</h1>
<v-container class="my-5">
<v-card class="mx-12 my-12">
<v-row>
<v-flex xs12 sm6 md4 lg3 v-for="person in team" :key="person.name">
<Person :name="person" :role="person" />
</v-flex>
</v-row>
<v-divider></v-divider>
</v-card>
</v-container>
</div>
</template>
<script>
import Person from '#/components/Person.vue'
export default {
name: 'Team',
components: {
Person
},
data() {
return {
team: [
{ name: 'The Net Ninja', role: 'Web developer' },
{ name: 'Ryu', role: 'Graphic designer' },
{ name: 'Chun Li', role: 'Web developer' },
{ name: 'Gouken', role: 'Social media maverick' },
{ name: 'Yoshi', role: 'Sales guru' }
]
}
}
}
</script>
However, it is not compiling... I kept getting
vue.runtime.esm.js?2b0e:1897 TypeError: Cannot read properties of undefined (reading 'name')
What did I forget to do ??
If I comment out the
<Person :name="person" :role="person" />
Result
{{ person.name }} seems accessible...
I think it has something to do with the data being rendered after the html. You can probably solve this by:
Adding v-if on the component; only render the component if data exists. You can also add it on v-flex component but as far as I know it's a bad practice because it may disturb the flow.
<v-flex xs12 sm6 md4 lg3 v-if="person" v-for="person in team" :key="person.name">
<Person />
</v-flex>
or alternatively:
<Person v-if="person" />
Add a default value on Person component props
// Object with a default value
person: {
type: Object,
default: function () {
return { name: '' }
}
}
More about props: https://v2.vuejs.org/v2/guide/components-props.html

VueJs - I don't understand how I could pass a Boolean value to a chid component

I know that the subject was certainly treated somewhere, but I really don't understand how I could pass a variable to a child component.
What I'm trying to do, is passing 'displayTogglingMenu' in the parent component to 'toggle' in the child.
Parent component
data(){
return {
selectedItem: 1,
displayTogglingMenu: false,
items: [
{ text: 'Home', name: 'home', icon: 'mdi-home' },
{ text: 'Courses', name: 'courses_index', icon: 'mdi-school' },
{ text: 'Enrolments', name: 'enrolments_index', icon: 'mdi-format-list-bulleted' },
{ text: 'Lecturers', name: 'lecturers_index', icon: 'mdi-account-tie' },
],
}
},
Child Component
data(){
return {
toggle: valueOfParentComponent,
alertMessage: '',
loadTable: true,
courses: [],
expendable: [],
...
},
Here is the component I'm trying to hide, as asked by Tim:
<div class="table-container">
<b class="circle"></b>
<v-app>
<v-content>
<v-card>
<v-card-title>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
class="search"
></v-text-field>
<v-spacer></v-spacer>
<router-link :to="{ name: 'new_course'}" class="newItem">New course</router-link>
</v-card-title>
<v-data-table
:headers="courseHeaders"
:items="courses"
:items-per-page="10"
:loading="loadTable"
loading-text="Loading... Please wait"
:search="search"
:single-expand="singleExpand"
:expanded.sync="expanded"
item-key="id"
show-expand
class="elevation-1"
>
<template v-slot:item.title="{ item }">
<transition-group name="list" tag="p">
<span class=" list-item" v-bind:key="item">{{item.title}}</span>
</transition-group>
</template>
<template v-slot:item.actions="{ item }">
<transition-group name="list" tag="p">
<span class=" list-item" v-bind:key="item">
<router-link :to="{ name: 'edit_course', params: { id: item.id } }" class="edit-btn" title="Edit">
<v-icon med>mdi-pencil</v-icon>
</router-link>
<v-btn v-on:click="deleteCourse(item.id)" class="del-btn " title="Delete" >
<v-icon med>mdi-delete</v-icon>
</v-btn>
</span>
</transition-group>
</template>
<template v-slot:expanded-item="{ headers, item }" >
<td :colspan="headers.length" class="item-description" >
<h4 >Course description:</h4>
<p >{{ item.description }}</p>
</td>
</template>
</v-data-table>
</v-card>
</v-content>
</v-app>
<b class="circle2"></b>
</div>
Parent
<template>
<Component :toggle="displayTogglingMenu" />
</template>
<script lang='ts'>
import Component from 'Componenet.vue'
import { defineComponent } from 'vue';
export default defineComponent({
components: {Component},
data() {
return {
displayTogglingMenu: false
}
}
});
</script>
Child
<template>
<div v-show="toggle"></div>
</template>
<script lang='ts'>
import { defineComponent } from 'vue';
export default defineComponent({
props: ['toggle']
});
</script>
The value of displayTogglingMenu in parent will be availible as toggle in the child
You might need to get the value as this.$props.toggle depending on the context
You could pass data from parent to child components using properties. Here are the docs
Eg.
<child-componenet :prop-name="parent value that could also be reactive"></child-component>
Props are reactive so your child-component will react to any change on its properties, you could use the property in a v-if or v-show

Vue warning and dialog only appears once

Having vue ui, creating a basic new project with Babel and Lint, I installed deps vuetify, vuetify-loader, and vue-bootstrap. All I want is a simple 'open dialog' button that open a dialog defined in a separate component (file). The dialog shows, without problems/warnings, but when I close it (either by clicking elsewhere or on one of the buttons, I get a warning about "Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders." Clicking the button again has no longer effect. Although the "JAAA" is shown in the console. The code:
HelloWorld.vue
<template>
<div class="hello">
<v-btn #click="openDialog" class="btn btn-info">Open dialog</v-btn>
<Dialog :showDialog="showDialog"></Dialog>
</div>
</template>
<script>
import Dialog from "./Dialog";
export default {
name: 'HelloWorld',
components: {
Dialog
},
props: {
msg: String
},
data() {
return {
showDialog: false
}
},
methods: {
openDialog() {
this.showDialog = true
window.console.log('JAAA')
}
}
}
</script>
Dialog.vue
<template>
<div>
<v-dialog v-model="showDialog" width="500">
<v-card>
<v-card-title class="headline grey lighten-2" primary-title>
Remark
</v-card-title>
<v-card-text>
Remark: <input type="text">
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<div class="flex-grow-1"></div>
<v-btn color="primary" text #click="hideDialog">
Done
</v-btn>
<v-btn color="primary" text #click="hideDialog">
Cancel
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
name: "Dialog",
props: ['showDialog'],
methods: {
hideDialog() {
this.showDialog = false;
}
}
}
</script>
Mutating the value in child will not reflect to parent, the props data flows at the time of child component created hook and while closing you are try to mutate it in child level and the state is not shared with the parent. From the next time onwards its just calls updated hook in dialog box
Make these changes to the Dialog.vue component
<template>
<div>
<v-dialog v-model="displayDialog" width="500">
<v-card>
<v-card-title class="headline grey lighten-2" primary-title>
Remark
</v-card-title>
<v-card-text>
Remark: <input type="text">
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<div class="flex-grow-1"></div>
<v-btn color="primary" text #click="hideDialog">
Done
</v-btn>
<v-btn color="primary" text #click="hideDialog">
Cancel
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
name: "Dialog",
props: {
showDialog: {
type: Boolean,
}
},
data() {
return {
displayDialog: false,
};
},
methods: {
hideDialog() {
this.displayDialog = false;
}
},
watch: {
showDialog(val) {
this.displayDialog = val;
}
}
}
</script>
You should not change the values of props directly in a component as the change will not be reflected in your parent.
You can instead convert your dialog component to use a v-model instead combined with a computed property in your child to emit changes to your parent so that it knows the value has been updated.
HelloWorld.vue
<template>
<div class="hello">
<v-btn #click="openDialog" class="btn btn-info">Open dialog</v-btn>
<Dialog v-model="showDialog"></Dialog>
</div>
</template>
<script>
import Dialog from "./Dialog";
export default {
name: 'HelloWorld',
components: {
Dialog
},
props: {
msg: String
},
data() {
return {
showDialog: false
}
},
methods: {
openDialog() {
this.showDialog = true
window.console.log('JAAA')
}
}
}
</script>
Dialog.vue
<template>
<div>
<v-dialog v-model="displayDialog" width="500">
<v-card>
<v-card-title class="headline grey lighten-2" primary-title>
Remark
</v-card-title>
<v-card-text>
Remark: <input type="text">
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<div class="flex-grow-1"></div>
<v-btn color="primary" text #click="hideDialog">
Done
</v-btn>
<v-btn color="primary" text #click="hideDialog">
Cancel
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
name: "Dialog",
props: {
value: {
type: Boolean,
default: false
}
},
computed: {
displayDialog: {
get() {
// returns the value of your prop
return this.value
},
set(newValue) {
// v-model listens to the input event, so emitting `input` with a value
// will update the model with that value
this.$emit('input', newValue)
}
};
},
Methods: {
hideDialog() {
this.displayDialog = false;
}
}
}
</script>
This worked for me =>
To close the dialog all you have to do is emit an event to the parent component and change the value of dialog property(ie close dialog from parent not from child)
<div class="hello">
<Dialog v-model="showDialog" #closeDialog="showDialog=false"> <Dialog>
</div>
dialog component =>
<v-dialog v-model="displayDialog" width="500">
.....
<v-card-actions>
<div class="flex-grow-1"></div>
<v-btn color="primary" text #click="$emit('closeDialog')">
Done
</v-btn>
<v-btn color="primary" text #click="$emit('closeDialog')">
Cancel
</v-btn>
</v-card-actions>
......
</v-dialog>
export default {
name: "Dialog",
props: {
showDialog: {
type: Boolean,
}
},
data() {
return {
displayDialog:this.showDialog,
};
},

Vue.js dialog/modal closes on parent component

I am trying to open my CanvasPreview Component in another component but it fails,
first, it quickly shows the dialog/modal afterward it gets hidden again if I open the Vue Dev tool
the showCanvasPreview is set to false if I manually edit it in my console to true the modal gets shown.
So I guess that it gets set to false again, but I can't see why.
This is the dialog/modal component:
<template>
<v-dialog
v-model="show"
>
<v-card>
<v-card-actions>
<v-container grid-list-md text-xs-center>
<v-layout row wrap>
</v-layout>
</v-container>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import CanvasPreviewSourceUpload from './CanvasPreviewSourceUpload';
export default {
components: {
'canvas-preview-source-upload': CanvasPreviewSourceUpload
},
props: {
imgSrc: String,
visible: Boolean
},
computed: {
show: {
get () {
return this.visible;
},
set (visible) {
if (!visible) {
this.$emit('closePreview');
}
}
}
},
}
</script>
And in my parent component I call the preview component like this:
<template>
<div>
//... some more html
<div id="canvas-body">
<canvas id="pdf-render"></canvas>
<canvas id="selectCanvas"
#mousedown="markElementOnMouseDown"
#mousemove="updatePreview"
#mouseup="markElementOnMouseUp">
</canvas>
</div>
<canvas-preview
:imgSrc="this.targetImage.src"
:visible="showCanvasPreview"
#closePreview="showCanvasPreview=false">
</canvas-preview>
</div>
</template>
<script>
import CanvasPreview from '#/js/components/CanvasPreview';
export default {
components: {
'canvas-preview': CanvasPreview
},
props: {
'name': String
},
data: () => ({
showCanvasPreview: false,
...
}),
methods: {
markElementOnMouseUp (event) {
this.isDragging = false;
this.targetImage.src = this.clipCanvas.toDataURL();
this.targetImage.style.display = 'block';
this.showCanvasPreview = true;
console.log("mouseup: " + this.showCanvasPreview);
},
}
</script>
Try this one
<v-dialog
v-model="show"
>
<v-card>
<v-card-actions>
<v-container grid-list-md text-xs-center>
<v-layout row wrap>
<canvas-preview-source-upload
:imgSrc="imgSrc"
#close.stop="show=false">
</canvas-preview-source-upload>
</v-layout>
</v-container>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import CanvasPreviewSourceUpload from './CanvasPreviewSourceUpload';
export default {
components: {
'canvas-preview-source-upload': CanvasPreviewSourceUpload
},
data: ()=> ({
show: false
}),
props: {
imgSrc: String,
visible: Boolean
},
watch: {
show(isShow){
if (!isShow) {
this.$emit('closePreview');
}
}
visible(isVisible) {
this.show = isVisible;
}
}
}
</script>```
Something like this should allow you to open a v-dialog from a separate component..
If you supply a CodePen or CodeSandbox with your code in it, we would be able to better assist you.
[CodePen mirror]
const dialog = {
template: "#dialog",
props: {
value: {
type: Boolean,
required: true
},
},
computed: {
show: {
get() {
return this.value;
},
set(value) {
this.$emit("input", value);
}
}
},
};
const dialogWrapper = {
template: "#dialogWrapper",
components: {
appDialog: dialog,
},
data() {
return {
isShown: false,
}
}
}
new Vue({
el: "#app",
components: {
dialogWrapper
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.6/dist/vuetify.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.6/dist/vuetify.min.css" rel="stylesheet" />
<div id="app">
<v-app>
<v-content>
<dialog-wrapper/>
</v-content>
</v-app>
</div>
<script type="text/x-template" id="dialog">
<v-dialog v-model="show">
<v-card>
<v-card-actions pa-0>
<v-spacer/>
<v-btn dark small color="red" #click="show = false">Close</v-btn>
<v-spacer/>
</v-card-actions>
<v-card-title class="justify-center">
<h2>
Hello from the child dialog
</h2>
</v-card-title>
</v-card>
</v-dialog>
</script>
<script type="text/x-template" id="dialogWrapper">
<div>
<h1 class="text-xs-center">I am the wrapper/parent</h1>
<v-container>
<v-layout justify-center>
<v-btn color="primary" dark #click.stop="isShown = true">
Open Dialog
</v-btn>
</v-layout>
</v-container>
<app-dialog v-model="isShown"></app-dialog>
</div>
</script>

How to remove vuetify card on clicking a button

I have added a vuetify card and a button on it. I want that when the button is clicked, the card disappears. How can I do that?
Below is how my component looks like. I want to add a method to do so but don't know what the method will be.
<template>
<div class="notifications">
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-card flat color="green">
<v-card-title primary-title>
<div>
<h3 class="headline">Neu Benutzer angelegt</h3>
<div> {{ card_text }} </div>
</div>
</v-card-title>
<v-card-actions>
<div class="close"><v-btn #click="removeMessage(2)">Ok</v-btn></div>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</div>
</template>
<script>
export default {
data () {
return {
card_text: 'Success!'
}
},
methods: {
removeMessage(seconds) {
},
},
};
</script>
You can hide it with v-if and a boolean flag:
<template>
<div class="notifications" v-if="show">
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-card flat color="green">
<v-card-title primary-title>
<div>
<h3 class="headline">Neu Benutzer angelegt</h3>
<div> {{ card_text }} </div>
</div>
</v-card-title>
<v-card-actions>
<div class="close"><v-btn #click="removeMessage(2)">Ok</v-btn></div>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</div>
</template>
<script>
export default {
data () {
return {
card_text: 'Success!',
show:true;
}
},
methods: {
removeMessage(seconds) {
setTimeout(()=> this.show = false, seconds * 1000);
},
},
};
</script>
You can also use v-show and make something like this: CodePen

Categories

Resources