Update reactive object made from prop after prop changes - vue.js

I have the following component:
<template>
<vote-buttons :score="commentRef.score"
#update-score="updateScore">
</vote-buttons>
</template>
<script>
props: {
comment: {type: Object}
},
setup(props) {
const commentRef = ref(props.comment);
const updateScore = (value) => {
commentRef.value.score = value;
}
}
</script>
Problem is when parent component loops again..
<comment v-for="comment in comments" :comment="comment">
</comment>
then prop has new data, but commentRef is not updated. How to retrigger reactive object creation after prop changes? Thanks

There are some issues in the code:
you should add a key in the parent component: (Here guessing you have an unique id in comment object)
<Comment
v-for="comment in comments"
:comment="comment"
:key="comment.id"></Comment>
Define comments as a reactive object, child no need to ref for the props, below give a similar sample but not exactly the same with your code:
Parent component:
<template>
<Comment v-for="comment in comments" :comment="comment" :key="comment.id"></Comment>
<button #click="addCmt">addCmt</button>
</template>
<script>
import Comment from "./components/Comment";
import {reactive} from "vue";
export default {
name: "Params",
setup() {
const comments = reactive([{id: 1, name: 'a'}, {id: 6, name: 'c'}]);
function addCmt() {
comments.unshift({id: comments.length + 10, name: 'k'});
}
return {
comments,
addCmt
}
},
components: {Comment}
}
</script>
Comment component:
<template>
<div>comments {{ comment.id }}</div>
</template>
<script>
export default {
name: "Comment",
props: ['comment'],
setup(props, ctx) {
const comment = props.comment;
return {comment};
}
}
</script>

Related

<b-form-input> bind value does not update on the second time

I am currently implementing a component that update parent's year[] array when year to / year[1] value is lower than year from / year[0] with <b-input> (Bootstrap Vue Library).
The year to stop updating after the second time.
Code example are as below.
Code equivalent in jsfiddle can be found here.
Parent.vue
<template>
<child :year="year" #update="update">
</template>
<script>
// Import child component here
export default {
name: 'Parent',
components: {
Child,
},
data: () => ({
year: [100, null],
}),
methods: {
update(newYear) {
this.year = newYear;
},
},
</script>
Child.vue
<template>
<div>
From <b-input :value="year[0]" />
To <b-input :value="year[1]" #change="update" />
</div>
</template>
<script>
export default {
name: 'Child',
props: {
year: {
type: Array,
required: true,
}
},
methods: {
update(yearToVal) {
const [yearFrom] = this.year;
let newYear = [...this.year];
if (yearToVal < yearFrom) {
/* Both of this update end up the same */
// newYear[1] = yearFrom;
this.$set(newYear, 1 , yearFrom);
}
this.$emit('update', newYear);
},
},
};
</script>
I had used Vue Dev Tools to check and the child is emitting data correctly to the parent.
The issue happen on the vModalValue and localValue of the <b-input> are not updating on the second time.
What am I doing wrongly or is it a Bootstrap Vue library problem?
Hiws's answer indicate that the this problem does not only happen on <b-form-input> but ordinary <input> with Vue as well.
This happen due to Vue not able to react to changes since the update is happening on child, hence when year to is lower than year from, parent will not detect any changes on the second time as the array pass to Parent.vue will always be [100,100].
The solution will be using watcher on Parent.vue's array to detect the changes, hence both eg: [100, 1] -> [100,100] are both reflected on Parent.vue and most importantly, force the component to re-render.
Without force re-rendering, [100,1] or [100,2]... will always be treated as [100,100], the same value, and Vue will not react or even update to them.
Jsfiddle equivalent solution can be found here
Code sample below:
Parent.vue
<template>
<child :year="year" #update="update">
</template>
<script>
// Import child component here
export default {
name: 'Parent',
components: {
Child,
},
data: () => ({
year: [100, null],
yearKey: 0,
}),
watch: {
year: {
handler(val) {
if (val[1] < val[0]) {
let newYear = [...val];
newYear[1] = val[0];
this.year = newYear;
// key update reference: https://michaelnthiessen.com/force-re-render/
this.yearKey += 1;
}
}
}
},
methods: {
update(newYear) {
this.year = newYear;
},
},
</script>
Child.vue
<template>
<div>
From <b-input :value="year[0]" />
To <b-input :value="year[1]" #change="update" />
</div>
</template>
<script>
export default {
name: 'Child',
props: {
year: {
type: Array,
required: true,
}
},
methods: {
update(yearToVal) {
const [yearFrom] = this.year;
let newYear = [...this.year];
newYear[1] = yearToVal;
this.$emit('update', newYear);
},
},
};
</script>

Vue why props is undefined on component

I try to transfer data to my component using props
If I type the name in the parent, I see that the button is working properly, ie the problem is with the component.
Where am I wrong?
Thanks in advance
Parent:
HTML
<div><button #click="changeName" :name="name">Change my name</button></div>
Script:
import UserDetail from "./UserDetail.vue";
export default {
components: {
UserDetail,
UserEdit,
},
data() {
return {
name: "Eden",
};
},
methods: {
changeName() {
this.name = "EdenM";
},
},
};
Component
<template>
<div>
<p>{{ name }}</p>
</div>
</template>
<script>
export default {
props: ["name"],
};
</script>
Ooooooh!! I add the prop an wrong place!
Here is my ans:
<user-detail :name="name"></user-detail>

Vue pass data from compontent 1 to a method in component 2

I have a parent component and a childcomponent.
In my parent component I call a simple childcomponent-method to save an email to the email variable. But the variable email does not change.
My Parentcomponent:
import ChildComponent from "./ChildComponent";
export default {
components: {ChildComponent},
methods: {
openDocument(d) {
ChildComponent.methods.saveEmail('new#example.com');
}
}
My Childcomponent:
<template>
<div>
Email: {{ email }}
</div>
</template>
<script>
export default {
data: function () {
return {
email: ''
}
},
methods: {
saveEmail(email) {
this.email = email; // this does NOT change my email variable
}
}
}
</script>
Why my email variable does not change? How can I change this variable?
In vue it is not work like that. You have to use Probs:
Parent :
<template>
<div class="container">
<child-component :email ="email"></child-component> // NEW HERE
</div>
</template>
<script>
import ChildComponent from "./ChildComponent";
module.exports = {
data: function () {
return {
email:''
}
},
methods: {
openDocument(d) {
this.email = "example#gmil.com"
}
},
}
</script>
Child component:
<template>
<div class="container">
<h1>Profile Form Component</h1>
</div>
</template>
<script>
module.exports = {
module.exports = {
props: ['email'], //NEW HERE
created: function () {
console.log(this.email) //prints out an empty string
}
}
</script>
ATTENTION
As you I added 2 comment NEW HERE in the code , these 2 lines are really important for what you wanna do.
The code that I giving you is an example (not a complete answer) , Probs is the solution of what you asked for.
Hope it Helps <3.
The ChildComponent variable only holds the recipe for creating components of this type - but it does not hold your actual component. Your actual component lives inside your template - you have to add a ref attribute to it (e.g. <custom-component ref="custom" ... />) and then reference it like this.$refs.custom.saveEmail()

Vue template does not update value (composition api)

I have a functional component:
export default defineComponent({
name: 'MovieOverview',
components: {
ExpandedMovieInformation,
},
setup() {
let toggleValue = false;
const toggleExpandedMovieInformation = (moviex: Movie) => {
toggleValue = !toggleValue;
console.log(toggleValue)
};
return {
toggleValue,
toggleExpandedMovieInformation,
};
},
});
<template>
<div>
<button v-on:click='toggleExpandedMovieInformation'>click</button>
{{ toggleValue }}
</div>
</template>
When I click the button the console.log does log the change, but the toggleValue in the template stays the same value: false.
Right now the toggleValue variable has no reactivity. You should use ref() or reactive() in order to make it reactive so the view re-renders every time changes are made into that property.
So you should do something like this:
import { ref } from 'vue'
export default defineComponent({
name: 'MovieOverview',
components: {
ExpandedMovieInformation,
},
setup() {
let toggleValue = ref(false);
const toggleExpandedMovieInformation = (moviex: Movie) => {
// now you'll have to access its value through the `value` property
toggleValue.value = !toggleValue.value;
console.log(toggleValue.value)
};
return {
toggleValue,
toggleExpandedMovieInformation,
};
},
});
<template>
<div>
<button v-on:click='toggleExpandedMovieInformation'>click</button>
<!-- You DON'T need to change toggleValue to toggleValue.value in the template -->
{{ toggleValue }}
</div>
</template>
Check the docs for more info about ref and reactive.

How to bind a prop value using a function and pass it to another component

Is it possible to bind a prop to a function?
In my example below I’m trying to get a value from a function in the main App.vue and pass it as a prop to the child component customComponent.
e.g. (this example doesn’t work)
App.vue
import customComponent from ‘./custom-component.vue'
<template>
<custom-component
v-bind:myValue="geMyValue()"
></custom-component>
</template>
<script>
export default {
name: "Item",
methods: {
getMyValue: function() {
return 1+3;
}
}
}
</script>
customComponent.vue
<template>
<h3 class="some-custom-layout">custom component</h3>
<input type="button" #click="sendMyValue()" />
</template>
<script>
export default {
name: “custom",
props: ['myValue']
methods: {
sendMyValue: function() {
console.log(this.myValue);
}
}
}
</script>
It's possible, but probably it would be better to use computed properties, if you are going to return value:
<template>
<custom-component
v-bind:myValue="myValue"
></custom-component>
</template>
<script>
export default {
name: "Item",
computed: {
myValue: function() {
return 1+3;
}
}
}
</script>
https://v2.vuejs.org/v2/guide/computed.html