I'm trying to pass value from a select field to v-model but the code only works in a v-text-field.
<script>
export default {
name: 'FormSelect',
props: {
modelValue: {
type: [String, Number],
default: '',
},
label: {
type: String,
default: '',
},
items: {
type: Array,
},
},
model: {
prop: 'modelValue',
event: 'change',
},
};
</script>
<template>
<div>
<v-select
:label="label"
v-bind:value="modelValue"
#input.native="$emit('change', $event.target.value)"
:items="items"
></v-select>
</div>
</template>
<template>
<div class="form">
<v-flex xs10 sm8 md6 lg5>
<v-card>
<FormTitle />
<ModalFormMessage />
<v-form ref="form" class="d-flex flex-column">
<FormSelect
v-model="vulnerabilities.vulnerability"
label="Vulnerability"
:items="items.vulnerability"
/>
<FormInputs
v-model="vulnerabilities.evidence"
label="Eevidence"
type="file"
/>
<FormInputs
v-model="vulnerabilities.solution"
label="Solution"
type="text"
/>
<FormBtns />
</v-form>
</v-card>
</v-flex>
</div>
</template>
In the select field is not returning the selected value, how can I make the value I select be passed to the v-model in the child component too?
You should emit an event with name input :
#input="$emit('input', $event.target.value)"
I solved it as follows
methods: {
changeSelect: function () {
this.$emit('input', this.modelValue);
},
},
<template>
<div>
<v-select
:label="label"
#input="changeSelect()"
v-model="modelValue"
:items="items"
></v-select>
<p>{{ modelValue }}</p>
</div>
</template>
Related
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>
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
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
I'm new to vue.js and vuetify and currently trying to validate the input in a v-edit-dialog inside a v-datatable. The validation seems to work, however the save button does not get disabled and the user-input gets saved even when it's invalid. Is there a way to let the button be disabled or to prevent the saving of invalid data?
My datatable:
<v-data-table dense :headers="headers" :items="sitesTable">
<template v-slot:top>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<v-card>
<v-toolbar flat>
<v-toolbar-title v-model="title" class="primary--text">{{ title }} Site</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon #click="close">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<v-container>
<FormTemplateSites v-bind:title="title" #dialog="!dialog"></FormTemplateSites>
</v-container>
</v-card-text>
</v-card>
</v-dialog>
</template>
<template v-slot:item.name="props">
<v-edit-dialog :return-value.sync="props.item.name" large persistent #save="saveName(props.item.name, props.item)">
<div>{{ props.item.name }}</div>
<template v-slot:input>
<div class="mt-4 title">Update Name</div>
</template>
<template v-slot:input>
<v-text-field v-model="props.item.name" :rules="required" label="Edit" single-line counter autofocus></v-text-field>
</template>
</v-edit-dialog>
</template>
<template v-slot:item.field="props">
<v-edit-dialog :return-value.sync="props.item.field" large persistent #save="saveField(props.item.field, props.item)">
<div>{{ props.item.field }}</div>
<template v-slot:input>
<div class="mt-4 title">Update Field</div>
</template>
<template v-slot:input>
<v-text-field v-model="props.item.field" :rules="rules_vectors" label="Edit" single-line counter autofocus></v-text-field>
</template>
</v-edit-dialog>
</template>
<template v-slot:item.position="props">
<v-edit-dialog :return-value.sync="props.item.position" large persistent #save="savePosition(props.item.position.x, props.item.position.y, props.item)">
<div>{{ props.item.position }}</div>
<template v-slot:input>
<div class="mt-4 title">Update Position</div>
</template>
<template v-slot:input>
<v-text-field v-model="props.item.position" label="Edit" single-line autofocus :rules="rules_vectors"></v-text-field>
</template>
</v-edit-dialog>
</template>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editSite(item)">mdi-pencil</v-icon>
<v-icon small #click="deleteItem(item)">mdi-delete</v-icon>
</template>
</v-data-table>
My component:
data: () => ({
title: '',
dialog: false,
required: [(v) => !!v || 'Required', ],
rules_vectors: [
(v) => !!v || 'Required',
(v) => {
try {
v = JSON.parse(v)
for (let item of v) {
if (!isNaN(item)) {
console.log(item)
} else {
return 'Invalid vector.'
}
}
return v.length === 2 || 'Invalid vector.'
} catch (error) {
console.log(error)
return 'Invalid vector.'
}
},
],
pagination: {},
headers: [{
text: 'Name',
align: 'start',
sortable: false,
value: 'name',
},
{
text: 'ID',
align: 'start',
sortable: false,
value: 'id',
},
{
text: 'Position',
sortable: false,
value: 'position',
},
{
text: 'Field',
sortable: false,
value: 'field',
},
{
text: 'Actions',
value: 'actions',
sortable: false,
},
],
}),
I had the same question
Is there a way to let the button be disabled or to prevent the saving of invalid data?
I found a way to de-couple the item value passed from the editted value and "prevent the saving of invalid data".
By using a seperate data field editName and linking the v-text-field to this, you can set the value on open and check and set it on save, without the item value becoming polluted by invalid values.
So
the v-model for the v-text-field is v-model="editName"
the v-edit-dialog has #open which sets the edit value - #open="editName = props.item.name"
you check the this.editName on save in your saveName(props.item)
as in
<v-data-table dense :headers="headers" :items="sitesTable">
<template v-slot:item.name="props">
<v-edit-dialog
large
persistent
#save="saveName(props.item)"
#open="editName = props.item.name"
>
<div>{{ props.item.name }}</div>
<template v-slot:input>
<div class="mt-4 title">Update Name</div>
</template>
<template v-slot:input>
<v-text-field
v-model="editName"
:rules="required"
label="Edit"
single-line
counter
autofocus
></v-text-field>
</template>
</v-edit-dialog>
</template>
</v-data-table>
on Save
saveName(item) {
if (this.validate(this.editName) {
item.name = this.editName
...
}
}
Edit. I removed the :return-value.sync="props.item.name" attribute on v-data-table as it seemed to be over-riding the setting of item.name in the saveName() function
I ran into the same thing and with some searching it turns out v-edit-dialog is going to be removed in v3 ... which doesn't help us now, but I did come up with a nimble solution that might work for you too.
<v-form v-model="isFormValid">
<v-data-table
:headers="headers"
:items="items"
item-key="ID"
>
<template v-slot:item.column="{ item }">
<v-text-field
v-model="itemData"
:rules="[validationRulesHere]"
required
></v-text-field>
</template>
</v-data-table>
<v-btn :disabled="!isFormValid">Save</v-btn>
</v-form>
I wrapped the whole table in a form and used it to overall tell me if everything in the table was correct.
i am trying to implement vue draggable and it almost seems to work except for when i try to implement it on a button. It gives me an error message whenever i try to move the button.
Here is an example : https://codepen.io/anon/pen/xoQRMV?editors=1111
<div id="app">
<v-app id="inspire">
<v-container>
<v-layout justify-center>
<v-flex>
<draggable v-model="myArray" :options="options" handle=".handle">
<div v-for="element in myArray" :key="element.id" class="title
mb-3">{{element.name}}
<v-icon color="red" class="handle mt-0">drag_handle</v-icon>
</div>
<v-btn class="ml-0">Button</v-btn>
<v-icon color="red" class="handle">drag_handle</v-icon>
</draggable>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
new Vue({
el: '#app',
data() {
return {
myArray: [
{name: 'Text1!!!!', id: 0},
{name: 'Text2!!!!', id: 1},
],
options: {
handle: '.handle'
}
}
}
})
Any help is appreciated.
It would have to work from a single array I think, e.g.
https://codepen.io/anon/pen/agQVvm?editors=1111
<div id="app">
<v-app id="inspire">
<v-container>
<v-layout justify-center>
<v-flex>
<draggable :list="combinedArray" :options="options" handle=".handle">
<div v-for="element in combinedArray" :key="element.id" class="title mb-3">
<div v-if="element.type !== 'button'" class="title mb-3">
{{ element.name }}
<v-icon color="red" class="handle mt-0">drag_handle</v-icon>
</div>
<div v-else>
<v-btn>{{ element.name }}</v-btn>
<v-icon color="red" class="handle mt-0">drag_handle</v-icon>
</div>
</div>
</draggable>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
new Vue({
el: '#app',
created () {
this.combinedArray = [...this.myArray, ...this.buttonsArray]
},
data () {
return {
myArray: [
{ name: 'Text1!!!!', id: 0 },
{ name: 'Text2!!!!', id: 1 }
],
buttonsArray: [
{ name: 'Button1', id: 2, type: 'button' },
{ name: 'Button2', id: 3, type: 'button' }
],
combinedArray: [],
options: {
handle: '.handle'
}
}
}
})
I was able to implement the drag on buttons by creating their own array:-
<draggable class="list-group" :list="buttonArray" :options="options"
handle=".handle" group="drags">
<div v-for="item in buttonArray" :key="item.id">
<v-btn class="ml-0">{{item.name}}</v-btn>
<v-icon color="red" class="handle">drag_handle</v-icon>
</div>
</draggable>
buttonArray: [
{name: 'Button1', id: 2},
{name:'Button2', id:3}
],
The updated pen:- https://codepen.io/anon/pen/xoQRMV?editors=1111
However it creates an issue where i am not able to replace the text with the button. :(