Prop not getting updated in child component on value change - vuejs2

In my App component (parent) I have:
<div class="bg-dark text-white text-center p-3 content">
<div class="form-group">
<input class="form-control" v-model="dogBreed" />
</div>
<my-child-comp greeting="Hello from parent" v-bind:dog-breed="dogBreed" />
</div>
...
data() {
return {
dogBreed: "Pit Bull"
}
},
And in the child view:
<template>
<div class="bg-primary text-white text-center m-2 p-3 content">
<h3>Dog: {{ dog }}</h3>
</div>
</template>
...
props: ["greeting", "dogBreed"],
data() {
return {
dog: this.dogBreed,
}
},
I do get the initial value for dogBreed from the parent component inside of the child component, but when I change the value of the input field the same change is not reflected, why?

The prop in the parent component is not named properly, it should be the exact name of the props which is dogBreed you used in the child
component
<div class="bg-dark text-white text-center p-3 content">
<div class="form-group">
<input class="form-control" v-model="dogBreed" />
</div>
<my-child-comp greeting="Hello from parent" v-bind:dogBreed="dogBreed" />
</div>
...
data() {
return {
dogBreed: "Pit Bull"
}
},
Your child component can remain the same but use computed instead of data. Having the same name will not cause any computational
problems and using computed instead of data should fix up the dynamic
nature of your code.
<template>
<div class="bg-primary text-white text-center m-2 p-3 content">
<h3>Dog: {{ dog }}</h3>
</div>
</template>
...
props: ["greeting", "dogBreed"],
computed: {
dog(){
return this.dogBreed
}
},
If however you are keen on using dog-breed then you must rename the prop in you child component to dog-breed before binding it in your parent component.

Related

Pass component as prop in object param

I have the following Card Grid set up in Vue3:
<template>
<div class="max-w-5xl mx-auto">
<div class="ml-20 pr-72">
<h2 class="text-4xl pb-8">{{ title }}</h2>
<p class="font-normal text-base pb-20">{{ description }}</p>
</div>
<div class="grid grid-cols-2 gap-5">
<div
v-for="card in cards"
:key="card.title"
class="rounded-xl py-6 px-5 bg-cover bg-center flex flex-col justify-between items-start h-60"
:style="{ backgroundImage: `url(${card.imageUrl})` }"
>
<div
class="rounded-full p-4 bg-gradient-to-r from-prospire-blue to-prospire-light-blue"
>
{{ card.icon }}
</div>
<p class="text-white font-light text-2xl">{{ card.title }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
title: string;
description: string;
cards: any[];
}>();
</script>
<style scoped></style>
Which i call using:
<CardGrid
:title="$t('aboutUs.cardGrid.title')"
:description="$t('aboutUs.cardGrid.description')"
:cards="[
{
title: $t('aboutUs.cardGrid.cards[0].title'),
imageUrl:
'https://images.pexels.com/photos/5595573/pexels-photo-5595573.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
icon: <BeakerIcon />,
},
]"
/>
This however gives me a BeakerIcon' refers to a value, but is being used as a type here. Did you mean 'typeof BeakerIcon'? error.. Is it possible to pass a 3rd party component (icon in this case), to a component in a way?
One option is to pass card.icon as a string, such as 'BeakerIcon' and do
<div class="rounded-full p-4 bg-gradient-to-r from-prospire-blue to-prospire-light-blue">
<component :is="card.icon" />
</div>
...granted the icon you pass has been registered as a component.
It also work without registering the component globally.
Here is an example on sfc.vuejs.org passing the imported components in the props.

Nested Scoped Slots Variable Collisions VueJS

I've created a simple scoped slot component that I need to nest, but I'm struggling to figure out how I can avoid naming collisions.
Vue Component nested-fields
<script>
export default {
props: [
"entityName",
"items"
],
data: function() {
return {
formItems: this.items
}
},
methods: {
addItem: function() {
this.items.push({})
},
removeItem: function(index) {
if (this.items[index].id) {
this.$set(this.items[index], '_destroy', true);
} else {
this.items.splice(index, 1);
}
}
}
}
</script>
<template>
<div class="nested-fields">
<div v-show="item._destroy !== true || typeof item._destroy == 'undefined'" class="card nested-fields__field-set mb-2" v-for="(item, index) in formItems" :key="index">
<div class="card-header d-flex justify-content-between">
<span>Add {{entityName}}</span> <span class="fa fa-times" #click="removeItem(index)"></span>
</div>
<div class="card-body">
<slot name='item-fields' v-bind="{item, index}"></slot>
</div>
</div>
<button class="btn btn-primary btn-xs mb-2" type="button" #click="addItem()">Add {{entityName}}</button>
</div>
</template>
HTML
<nested-fields entity-name="Rotap Analysis" :items="[]">
<template #item-fields="{item, index}">
<div class="form-group col-sm-4">
<label>
Amount (g)
<input type="number" min="0" v-model="item.amount_grams" :name="'setup[input_material_attributes][rotap_analysis_attributes]['+index+'][amount_grams]'" class="form-control">
</label>
</div>
<div class="form-group col-sm-4">
</div>
<div class="form-group col-sm-4">
</div>
<nested-fields entity-name="Sieve" :items="[]">
<template #item-fields="{item2, index2}">
<label>
Sieve Size (US Mesh)
<input type="number" min="0" v-model="item2.size_mesh" :name="'setup[input_material_attributes][rotap_analysis_attributes]['+index+'][rotap_sieves]['+index2+'][size_mesh]'" class="form-control">
</label>
</template>
</nested-fields>
</template>
</nested-fields>
I need to rename the variables in the nested template shown here:
<nested-fields entity-name="Sieve" :items="item.rotap_sieves || []">
<!-- this line --><template #item-fields="{item2, index2}">
So I can use them here:
<input type="number" min="0" v-model="item2.size_mesh" :name="'setup[input_material_attributes][rotap_analysis_attributes]['+index+'][rotap_sieves]['+index2+'][size_mesh]'" class="form-control">
... BUT it does not let me rename the destructuring as I have it from "item" and "index" to "item2" and "index2".
For what it's worth, I'm attempting to replace Cocoon rails gem for nesting forms in my Rails app, though that shouldn't really matter.
Question - How can I rename the variables in nested scoped slots to avoid variable collisions?
I figured out it's a simple destructuring syntax solution:
<template #item-fields="{item: item2, index: index2}">
Works perfectly as expected!

Vue is not rendering on right tags component

The way i am defining my props;
<template>
<div>
<vinput id="login_name" label="Phone number"></vinput>
<div class="flex flex-col">
<label for="email-phone">School slug</label>
<input type="text" id="email-phone" class="border py-2 rounded">
</div>
</div>
</template>
<script>
import vinput from '../../components/Input.vue'
export default {
components:{
vinput
}
}
</script>
The way i am passing the props
<vinput id="login_name" label="Phone number"></vinput>
Please why is the browser rendering it this way:
<div class="mt-4">
<div class="flex flex-col" id="login_name" label="Phone number">
<label></label>
<input type="tex" class="border py-2 rounded">
</div>
<div class="flex flex-col"><label for="email-phone">School slug</label><input type="text"
id="email-phone" class="border py-2 rounded"></div></div>
I was expecting interpolation in the label and input not in the div
====Edited====
vinput component
<template>
<div class="flex flex-col">
<label :for="id">{{ label }}</label>
<input :type="type ? type : 'tex'"
:class="classes ? classes :''" class="border py-2 rounded"/>
</div>
</template>
<script>
export default {
data(){
return {
props:['label', 'id','type', 'classes']
}
}
}
</script>
Move your props:['label', 'id','type', 'classes'] away from data.
Those are totally different things.
<script>
export default {
props: ['label', 'id','type', 'classes'],
data(){
return {};
},
};
</script>

How to send & receive object as a props in Vue.js

I have a Product object and I am taking it from API call. So in parent component I want to send this object into the child object which is SoldTickets component.
So here is my parent component:
<template>
<section id="cart-page" class="row">
<div v-for="item in cart.attributes.items" :key="item.id">
<div class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
<div class="title">WARENKORB</div>
<SoldTickets :item = item />
</div>
</div>
</section>
</template>
And my child:
<template>
<div id="sold-tickets">
<div class="card">
<div class="sold-tickets-actions properties">
<div class="sold-tickets-inner">
<div class="ticket-details">
<div class="ticket-prop">
<div class="ticket-name">{{ item.product_name }}</div>
<div class="ticket-type">{{ item.variation_name }}</div>
</div>
</div>
<DeleteButton #click.prevent="removeProductFromCart(item.id)" />
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
data: {
item: Object,
}
},
}
</script>
So I am quite new in Vue so I am not really confident with props. COuld you please help me with this.
You mostly have it right, but there are a couple of errors:
Your props aren't declared correctly in the child component, you shouldn't have the "data" in there. All props you want to declare go directly under the "props" key of the component declaration:
export default {
props: {
item: Object,
},
}
You're also missing quotes around the attribute value in the parent component, so that's invalid HTML:
<SoldTickets :item = item />
should be
<SoldTickets :item = "item" />
Just wrap the bound value with "" or '' like :
<SoldTickets :item="item" />

VeeValidate with Yup: input type="number" value is converted to string on submit

I use VeeValidate and Yup for form validation and don't know why my input field with type="number" is converted to string on submit.
When I input 78 and submit the form the output of the console.log in the onSubmit(values) function is the following:
values: {
prioritaet: "78"
}
Am I doing something wrong here or is this the normal behavior of VeeValidate and Yup? I would like to have a number instead of a string after submit.
My code looks like this:
<template>
<div class="container m-3">
<div class="row bg-primary align-items-center py-2">
<div class="col">
<h3 class="text-light mb-0">Format {{ this.action }}</h3>
</div>
<div class="col-auto">
<h5 class="text-light mb-0">{{ this.formatTitle }}</h5>
</div>
</div>
<Form #submit="onSubmit" :validation-schema="formatSchema" v-slot="{ errors }" ref="formatForm">
<div class="row mt-4">
<div class="col">
<h5>Formatdaten</h5>
</div>
</div>
<div class="row mb-2">
<div class="col-4">
<label for="prioritaet-input">Priorität: </label>
</div>
<div class="col">
<Field type="number" name="prioritaet" id="prioritaet-input" class="w-100" />
</div>
</div>
<div class="row justify-content-end">
<div class="col-auto me-auto">
<button class="btn btn-outline-primary">Änderungen übernehmen</button>
</div>
<div class="col-auto">
<button class="btn btn-outline-primary">Abbrechen</button>
</div>
<div class="col-auto">
<button type="sumbit" class="btn btn-outline-primary">Speichern</button>
</div>
</div>
<div class="row mt-4">
<template v-if="Object.keys(errors).length">
<span class="text-danger" v-for="(message, field) in errors" :key="field">{{ message }}</span>
</template>
</div>
</Form>
</div>
</template>
<script>
import { Form, Field } from "vee-validate";
import deLocale from "../assets/yup-localization.js";
import * as Yup from "yup";
import { markRaw } from "vue";
import { mapActions, mapState } from "vuex";
Yup.setLocale(deLocale);
export default {
name: "FormatBearbeitungsSchirm",
props: ["material_id"],
data() {
let action = "neu";
let formatTitle = "Format neu";
let formatSchema = markRaw(Yup.object().shape({
prioritaet: Yup.number().min(1).max(100).integer().label("Priorität"),
}));
return { formatSchema, action, formatTitle };
},
created() {
},
components: {
Form,
Field,
},
methods: {
onSubmit(values) {
console.log("values", values);
},
},
};
</script>
It looks like there is currently no support for specifying the .number modifier on the internal field model value of <Field>, so the emitted form values would always contain a string for number-type fields.
One workaround is to convert the value in the template, updating <Form>'s values slot prop in <Field>'s update:modelValue event:
<Form #submit="onSubmit" v-slot="{ values }">
<Field 👆
type="number"
name="prioritaet" 👇
#update:modelValue="values.prioritaet = Number(values.prioritaet)"
/>
<button>Submit</button>
</Form>
demo
Another simple workaround is to convert the property inside onSubmit before using it:
export default {
onSubmit(values) {
values.prioritaet = Number(values.prioritaet)
// use values here...
}
}
You must use the .number modifier.
You can read about it here
If you want user input to be automatically typecast as a Number, you can add the number modifier to your v-model managed inputs:
const app = new Vue({
el: "#app",
data: () => ({
mynumber1: undefined,
mynumber2: undefined
}),
methods: {
submit() {
console.log(typeof this.mynumber1, this.mynumber1)
console.log(typeof this.mynumber2, this.mynumber2)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14/dist/vue.js"></script>
<div id="app">
<form>
<!-- number modifier -->
<input type="number" v-model.number="mynumber1" placeholder="Type here" />
<!-- no modifier -->
<input type="number" v-model="mynumber2" placeholder="Type here" />
<input type="button" #click="submit" value="submit" />
</form>
</div>