How to pass custom props to component in Vue from function? - vue.js

I want to pass isReadonly boolean value from first component to second.
And it does not work.
Edited after cafertayyar answer.
Method isReadonly moved from methods to computed.
First component:
<template>
<PreliminaryInformationUsageCode :is-readonly="isReadonly" />
</template>
<script>
import PreliminaryInformationUsageCode from './form/PreliminaryInformationUsageCode.vue'
export default {
name: 'FormPage',
computed: {
form() {
return this.$store.getters['form/form']
},
isReadonly: function() {
//return true
return false
}
},
components: {
PreliminaryInformationUsageCode,
},
}
</script>
Second component:
<template>
<v-select
v-model="usageCodesSelected"
:items="usageCodes"
item-text="name"
item-value="code"
label="Label"
multiple
hint="Hint"
persistent-hint
v-bind:readonly="isReadonly"
>
<template v-slot:selection="{ item, index }">
<v-chip v-if="index === 0">
<span>{{ item.name }}</span>
</v-chip>
<span
v-if="index === 1"
class="grey--text text-caption"
>
(+{{ usageCodesSelected.length - 1 }} дополнительно)
</span>
</template>
</v-select>
</template>
<script>
export default {
name: 'PreliminaryInformationUsageCode',
props: {
isReadonly: {
Boolean
},
},
data: function() {
return {
usageCodesSelected: [
],
usageCodes: [
],
}
},
}
</script>

Use this:
<PreliminaryInformationUsageCode :is-readonly="isReadonly"/>
and instead of using isReadonly function, define a computed like:
computed: {
isReadonly() {
return this.form.status.seq != 10;
}
}

Related

using vuetify overlay in datatable

So I have a datatable of images that I want to expand in an overlay tag on click.
To do that, I created an array for each column of the table and I mapped it to its corresponding image.
Here's the code :
<template>
<v-app>
<app-navbar />
<v-main>
<div class="text-center">
<h3>
test {{ $route.params.name }}, {{ $route.query.status }},{{
$route.query.tag
}}
</h3>
</div>
<v-data-table
:headers="headers"
:items="imagesref"
:items-per-page="5"
class="elevation-1"
>
<template v-slot:[`item.index`]="{ index }">
{{index+1}}
</template>
<template v-slot:[`item.ref`]="{ index }">
<v-img :src="imagesref[index]" max-width="750" max-height="750" #click="expref[index] = !expref[index]"/>
<v-overlay :value="expref[index]"><v-img :src="imagesref[index]" max-width="1300" max-height="900" #click="expref[index] = !expref[index]"/> </v-overlay>
</template>
<template v-slot:[`item.test`]="{ index }">
<v-img :src="imagestest[index]" max-width="750" max-height="750" #click="exptest[index] = !exptest[index]"/>
<v-overlay :value="exptest[index]"><v-img :src="imagestest[index]" max-width="1300" max-height="900" #click="exptest[index] = !exptest[index]"/> </v-overlay>
</template>
<template v-slot:[`item.res`]="{ index }">
<v-img :src="imagesresult[index]" max-width="750" max-height="750" #click="expres[index] = !expres[index]"/>
<v-overlay :value="expres[index]"><v-img :src="imagesresult[index]" max-width="1300" max-height="900" #click="expres[index] = !expres[index]"/> </v-overlay>
</template>
<template #[`item.Scrubber`]="{ index }">
<nuxt-link :to="{ path: 'scrubber', query: { imageref: imagesref[index],imagetest:imagestest[index],imageres:imagesresult[index] }}">Show Scrubber</nuxt-link>
</template>
</v-data-table>
</v-main>
</v-app>
</template>
<script>
import appNavbar from "../../../components/appNavbar.vue"
import axios from "axios"
export default {
components: { appNavbar },
name: "App",
data() {
return {
expref:[],
exptest:[],
expres:[],
items: [],
imagesref: [],
imagestest: [],
imagesresult: [],
headers: [
{ text: 'index',value: 'index',sortable:false},
{ text: 'Imagesref', value: 'ref',sortable:false },
{ text: 'Imagestest', value: 'test',sortable:false },
{ text: 'Imagesresult', value: 'res',sortable:false },
{ text: 'Scrubber', value: 'Scrubber',sortable:false },
]
}
},
async created() {
try {
const res = await axios.get(`http://localhost:3004/tests`, {
params: { name: this.$route.params.name },
})
this.items = res.data
this.imagesref = res.data[0].refimages
this.imagestest = res.data[0].testimages
this.imagesresult = res.data[0].resultimages
for (let i of this.imagesref){
this.expref.push(false);
this.exptest.push(false);
this.expres.push(false);
}
} catch (error) {
console.log(error)
}
}
}
</script>
<style scoped>
</style>
When I tested it, after I click on the image the corresponding variable in the array changes its value to true but the overlay is not getting displayed but somehow when I change the value manually on devtools it works.Does someone have any idea what's going on and how can i make it work ?

How to pass props to input value in Vuejs

I have a parent component as a Cart. Here I defined quantity and I want to pass this quantity to the child component's input value which is Counter. So here how I am passing it and here is my parent component, Cart:
<Counter quantity="item.quantity"/>
And here is my child component, Counter:
<template>
<div id="counter">
<button class="minus" #click="countDown"><i :class="quantity == 0 ? 'disabled' : ''" class="fas fa-minus"></i></button>
<div class="count-number"><input class="counter-content" type="number" v-model="quantity"></div>
<button class="plus" #click="countUp"><i class="fas fa-plus"></i></button>
</div>
</template>
<script>
export default {
props: {
quantity: Number
},
methods: {
countUp() {
this.quantity++;
},
countDown() {
if(this.quantity > 0) {
this.quantity--;
}
},
}
}
</script>
I am quite new in Vue, so maybe I am doing something wrong when I pass the props. So could you help me about this?
Try (with the : colon sign)
<Counter :quantity="item.quantity"/>
Before you were just passing the string "item.quanity"
I see you're modifying your prop directly:
countUp() {
this.quantity++;
},
countDown() {
if(this.quantity > 0) {
this.quantity--;
}
},
This is not how you do it in Vue. You need to use two way binding.
countUp() {
this.$emit('input', this.quantity+1)
}
countDown() {
this.$emit('input', this.quantity-1)
}
and in your parent component:
<Counter :quantity="item.quantity" #input="(payload) => {item.quantity = payload}"/>
By the way, the Vue styleguide recommends to use multi-word component names: https://v2.vuejs.org/v2/style-guide/#Multi-word-component-names-essential (Cart = bad, MyCart = good)
We cannot change the value that we get from props, so I created a variable and put props there when mounting
Try it
<Counter :quantity="item.quantity"/>
and
<template>
<div id="counter">
<button class="minus" #click="countDown"><i :class="sum == 0 ? 'disabled' : ''" class="fas fa-minus"></i></button>
<div class="count-number"><input class="counter-content" type="number" v-model="sum"></div>
<button class="plus" #click="countUp"><i class="fas fa-plus"></i></button>
</div>
</template>
<script>
export default {
props: {
quantity: Number
},
data: () => ({
sum: 0
}),
mounted() {
this.sum = this.quantity;
},
methods: {
countUp() {
this.sum++;
},
countDown() {
if(this.sum > 0) {
this.sum--;
}
},
}
}
</script>

VueJS props issue something missing

I have a tabbed view and I need to pass a value from a select field to the tabs but I don't get props right. This is the parent:
<template>
<div>
<b-form-group id="memberListing" label-for="memberListing" class="mr-sm-2">
<b-form-select
v-model="memberSelection"
:title="memberSelection"
#change="getUserID"
aria-describedby="memberListing"
>
<option disabled value="" selected>Mitglied auswählen</option>
<option
v-for="member in memberList"
v-bind:key="member"
:value="member.cb_userid"
lazy
>
{{member.user_name}}
</option>
</b-form-select>
</b-form-group>
<Biometrics :title="memberSelection"></Biometrics>
<b-card title="Card Title" no-body>
<b-card-header header-tag="nav">
<b-nav card-header tabs>
<b-nav-item to="/users/profile" exact exact-active-class="active"
>Profil</b-nav-item
>
<b-nav-item
to="/users/trainingsplans"
exact
exact-active-class="active"
>Trainingspläne</b-nav-item
>
<b-nav-item to="/users/biometrics" exact exact-active-class="active"
>Biometrie</b-nav-item
>
</b-nav>
</b-card-header>
<b-card-body>
<router-view></router-view>
</b-card-body>
</b-card>
</div>
</template>
<script>
import axios from 'axios'
import Biometrics from "#/components/users/biometrics.vue";
export default {
data: () => {
return {
profileList: 'http://localhost:8000/userProfiles/profileList',
memberList: [],
memberSelection: null,
}
},
props: {
},
components: {
Biometrics
},
async mounted() {
try {
let memberListData = await axios.get(this.profileList)
this.memberList = memberListData.data
console.log(this.memberList)
} catch (e) {
this.errors.push(e)
}
},
methods: {
getUserID: function () {
// this.childMemberSelect = this.memberSelection
// console.log(this.childMemberSelect)
},
},
}
</script>
What am I missing? I'm trying for hours now but I cant get the value to show up in the specified tab when I select the value.
this is the child :
<pre>
<template>
<div>
{{title}}
</div>
</template>
<script>
export default {
created () {
console.log(this.title)
},
methods: {},
props: {
title: Number,
},
data() {
return {
}
},
components:{},
}
</script>
</pre>

want to use vuetify snackbar as a global custom component in vuejs

i used snackbar to show success messages in vuejs. i want to make a global custom snackbar component.
<template>
<div name="snackbars">
<v-snackbar
v-model="snackbar"
:color="color"
:timeout="timeout"
:top="'top'"
>
{{ text }}
<template v-slot:action="{ attrs }">
<v-btn dark text v-bind="attrs" #click="snackbar = false">
Close
</v-btn>
</template>
</v-snackbar>
</div>
</template>
<script>
export default {
props: {
snackbar: {
type: Boolean,
required: true,
},
color: {
type: String,
required: false,
default: "success",
},
timeout: {
type: Number,
required: false,
default: 3000,
},
text: {
type: String,
required: true,
},
},
};
</script>
then i import this as a component in my every form like this.
<SnackBar :snackbar="snackbar" :color="color" :text="text" />
but my issue is i can't use snackbar as a prop in my child component. it shows me this error.
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "snackbar"
how can i fix this issue. can anyone help me?
I realize this is old, but thanks to google, I am going to add my solution.
I use this, because I don't see the point of using vuex for a snackbar. It's more work then needed.
Create a vue component named vtoast
<template>
<v-snackbar
:color="color"
:timeout="timer"
v-model="showSnackbar"
bottom
right
>
<v-icon left>{{icon}}</v-icon>{{message}}
</v-snackbar>
</template>
<script>
export default {
name: "vtoast",
data() {
return{
showSnackbar: false,
message: '',
color: 'success',
icon: 'mdi-check',
timer: 3000
}
},
methods:{
show(data) {
this.message = data.message || 'missing "message".'
this.color = data.color || 'success'
this.timer = data.timer || 3000
this.icon = data.icon || 'mdi-check'
this.showSnackbar = true
}
}
}
</script>
Somewhere in the root of your main app, add the following. (I usually put mine in App.vue)
<template>
...
<!-- toast -->
<vtoast ref="vtoast"/>
...
</template>
<script>
import vtoast from '#/your/vtoast/directory/vtoast'
export default{
name: 'App', //or whatever your root is
components:{
vtoast
},
mounted() {
this.$root.vtoast = this.$refs.vtoast
},
}
</script>
And access it like so...
this.$root.vtoast.show()
this.$root.vtoast.show({message: 'Ahoy there!'})
i found a way to fix my solution using vuex.
<template>
<div name="snackbars">
<v-snackbar v-model="show" :color="color" :timeout="timeout" :top="'top'">
{{ text }}
<template v-slot:action="{ attrs }">
<v-btn dark text v-bind="attrs" #click="show = false">
Close
</v-btn>
</template>
</v-snackbar>
</div>
</template>
<script>
export default {
created() {
this.$store.subscribe((mutation, state) => {
if (mutation.type === "snackbar/SHOW_MESSAGE") {
this.text = state.snackbar.text;
this.color = state.snackbar.color;
this.timeout = state.snackbar.timeout;
this.show = true;
}
});
},
data() {
return {
show: false,
color: "",
text: "",
timeout: 0,
};
},
};
</script>
in my vuex module i wrote like this
export default {
namespaced: true,
state: {
text: "",
color: "",
timeout: "",
},
mutations: {
SHOW_MESSAGE(state, payload) {
state.text = payload.text;
state.color = payload.color;
state.timeout = payload.timeout;
},
},
actions: {
showSnack({ commit }, payload) {
commit("SHOW_MESSAGE", payload);
},
},
};
then i import snackbar child component into my parent component and send data like this.
...mapActions("snackbar", ["showSnack"]),
saveDetails() {
this.showSnack({
text: "Successfully Saved!",
color: "success",
timeout: 3500,
});
}
Another solution is to use a computed value with getter and setter.
Using options api
<template>
<v-snackbar v-model="show" :color="color">
{{ message }}
<template v-slot:action="{ attrs }">
<v-btn text v-bind="attrs" #click="show = false">Close</v-btn>
</template>
</v-snackbar>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters({
message: 'snackbar/message',
color: 'snackbar/color'
}),
show: {
get() {
return this.$store.state.snackbar.show
},
set(v) {
this.$store.commit('snackbar/SET_SHOW', v)
}
}
}
}
</script>
Using composition api plugin
<template>
<v-snackbar v-model="show" :color="color">
{{ message }}
<template v-slot:action="{ attrs }">
<v-btn text v-bind="attrs" #click="show = false">Close</v-btn>
</template>
</v-snackbar>
</template>
<script>
import { defineComponent, computed } from '#vue/composition-api';
export default defineComponent({
setup(_props, { root }) {
const show = computed({
get: () => root.$store.state.snackbar.show,
set: (v) => root.$store.commit('snackbar/SET_SHOW', v),
});
const message = computed(() => root.$store.state.snackbar.message);
const color = computed(() => root.$store.state.snackbar.color);
return {
show,
message,
color,
};
},
});
</script>
A better implementation using composables here https://gist.github.com/wobsoriano/2f3f0480f24298e150be0c13f93bac20
You are having a prop and the same in data.
remove snackbar from data() as it is available from prop.
<script>
export default {
props: {
snackbar: {
type: Boolean,
required: true,
},
color: {
type: String,
required: false,
default: "success",
},
timeout: {
type: Number,
required: false,
default: 3000,
},
text: {
type: String,
required: true,
},
}
};
</script>
This is what I did with Options API with mere props and events;
Here is the Snackbar.vue component
<template>
<div class="text-center">
<v-snackbar
transition="true"
bottom
right
v-model="show"
:color="snackbar.color"
:timeout="snackbar.timeout"
class="snackbar-shadow"
>
<div class="d-flex align-start alert-notify">
<v-icon size="24" class="text-white mr-5">{{ snackbar.icon }}</v-icon>
<p class="mb-0">
<span class="font-size-root font-weight-600">{{
snackbar.title
}}</span>
<br />
{{ snackbar.message }}
</p>
</div>
<template v-slot:action="{ attrs }">
<v-btn
icon
elevation="0"
max-width="136"
:ripple="false"
height="43"
class="font-weight-600 text-capitalize py-3 px-6 rounded-sm"
color="rgba(255,255,255, .85)"
text
v-bind="attrs"
#click="show = false"
>
<v-icon size="13">fas fa-times</v-icon>
</v-btn>
</template>
</v-snackbar>
</div>
</template>
<script>
export default {
name: "snackbar",
props: {
snackbar: Object,
},
computed: {
show: {
get() {
return this.snackbar.visible;
},
set(value) {
this.$emit("closeSnackbar", value);
},
},
},
};
</script>
Here is the App.vue component
<template>
<!-- Snackbar -->
<snackbar :snackbar="snackbar" #closeSnackbar="SnackbarClose"></snackbar>
</template>
<script>
export default {
name: "app",
data() {
return {
snackbar: {
visible: false,
timeout: 2000,
color: "#11cdef",
title: "Hello",
message: null,
icon: "fas fa-bell",
},
};
},
created: { this.SnackbarShow(); }
methods: {
SnackbarShow() {
this.snackbar.visible = true;
this.snackbar.message = "Hola!👋 I'm a snackbar";
},
SnackbarClose() {
this.snackbar.visible = false;
},
},
};
</script>

Getting the data back from child to parent element in Vue

Before last updates I used a custom component to work with sliders. Works perfectly, but after upgrading I got a problem.
This is the wayI use my custom component:
<ta-slider2 v-model="stelling1"/>
This passes the value to the custom component and received the data back.
This is my custom component
<template>
<div class="mt-3">
<div><br><br></div>
<v-slider
prop="value"
v-model="interface"
:value="value"
:color="color"
always-dirty
min="-100"
max="100"
thumb-label="always"
>
<template v-slot:append>
<v-icon color="blue">add_circle_outline</v-icon>
</template>
<template v-slot:prepend>
<v-icon color="error">remove_circle_outline</v-icon>
</template>
</v-slider>
</div>
</template>
<script>
export default {
name: "ta-slider2",
props: {
value: null
},
data() {
return {
}
},
computed: {
interface: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
},
color() {
if (this.value < 0) return 'red'
if (this.value > 0) return 'blue'
return 'red'
},
},
}
</script>
<style scoped>
</style>
Worked perfect but now the line with v-model="interface" gives problems. Googled for hours but can not find a solution. Who can help me out?