Vue resets child Component data on props update - vue.js

I have child component, that has some internal data, that should not be changed from outside. But when I update prop from parent component, this internal data is reset.
Basically in example below, when we change title from outside, value is set back to empty ''. How can I make value persistent with Child component props update?
Child.vue
<template>
<div class="child">
<h2>{{title}}</h2>
<input type="text" :value="value" v-on:change="$emit('change', $event.target.value)">
</div>
</template>
<script>
export default {
props: {
title: {
default: 'Just title'
}
},
data() {
return {
value: ''
}
}
}
</script>
Parent.vue
<template>
<div class="parent">
<Child :title="title" v-on:change="processTitle($event)"></Child>
</div>
</template>
<script>
import Child from './Child';
export default {
data() {
return {
title: 'Title from parent'
}
},
methods: {
processTitle(value) {
this.title = value.reverse();
}
}
}
</script>

You are not setting the value data attribute, :value=value means that "if value changes, the input value should pick up that change". But value doesn't change. Use v-model instead if you want to keep it simple.
Vue.component("Child", {
props: {
title: {
default: 'Just title'
}
},
data() {
return {
value: ''
}
},
template: `
<div class="child">
<h2>{{title}}</h2>
<input type="text" v-model="value" v-on:change="$emit('change', $event.target.value)">
</div>
`
})
new Vue({
el: "#app",
data() {
return {
title: 'Title from parent'
}
},
methods: {
processTitle(value) {
this.title = value.split("").reverse().join("");
}
},
template: `
<div class="parent">
<child :title="title" v-on:change="processTitle($event)"></child>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
EDIT
Also, if you want a continuous effect, don't use #change - use #input instead:
Vue.component("Child", {
props: {
title: {
default: 'Just title'
}
},
data() {
return {
value: ''
}
},
template: `
<div class="child">
<h2>{{title}}</h2>
<input type="text" v-model="value" v-on:input="$emit('change', $event.target.value)">
</div>
`
})
new Vue({
el: "#app",
data() {
return {
title: 'Title from parent'
}
},
methods: {
processTitle(value) {
this.title = value.split("").reverse().join("");
}
},
template: `
<div class="parent">
<child :title="title" v-on:change="processTitle($event)"></child>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Related

function not updating vue property

I have this component:
<template>
<div class="hello">
<div>
My prop: {{ myprop }}?
</div>
<div>
<button class="fas fa-lock-open lock" #click="changeText()">Click</button>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'StartPage',
props: {
myprop: {
type: String
}
},
model: {
prop: 'myprop',
event: 'click'
},
methods: {
changeText () {
this.$emit('click', 'sometext')
console.log('this.myprop', this.myprop)
}
}
})
</script>
Im using vue v3. Everytime I click on the button, I still see the text "My prop: ?" in the browser.
And in the console I can see: "this.myprop undefined" every time I click on the button.
What am I doing wrong?
As per my understanding, You are trying to update the prop text on click of button from the child component. If Yes, you can achieve it simply by emitting a new text and updating that in the parent component.
Live Demo :
const ShowPropText = {
template: `<div class="hello">
<div>
My prop: {{ myprop }}
</div>
<div>
<button class="fas fa-lock-open lock" #click="changeText()">Click</button>
</div>
</div>`,
props: ['myprop'],
methods: {
changeText() {
this.$emit('click-event', 'sometext')
}
}
}
const app = Vue.createApp({
components: {
'show-prop-text': ShowPropText
},
data() {
return {
text: 'This is default text'
}
},
methods: {
methodCall(e) {
this.text = e;
}
}
})
app.mount('#app')
<script src="https://cdn.jsdelivr.net/npm/vue#next"></script>
<div id="app">
<show-prop-text :myprop="text" #click-event="methodCall"></show-prop-text>
</div>

How can I select element Id with a function in Vue.js?

I want to use the Id of the element in which I use the function as a parameter. Example:
<div
class="col-2 column-center"
id="myId"
#mouseover="aFunction(id)"
>
methods: {
aFunction(id){
alert(id);
}
}
But it doesn't work. How can I do it?
In another way, you can make your id attribute bind with the data property, like this :
<template>
<div class="col-2 column-center" v-bind:id="id" #mouseover="aFunction(id)">
test
</div>
</template>
<script>
export default {
name: 'Test',
data() {
return {
id: 'myId',
};
},
methods: {
aFunction(id) {
alert(id);
},
},
};
</script>
You can pass the event to the method and then get the id from event.target.id.
https://jsfiddle.net/tr0f2jpw/
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
aFunction(event){
alert(event.target.id);
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div
class="col-2 column-center"
id="myId"
#mouseover="aFunction($event)"
>
some stuff
</div>
</div>

Watching properties in child component does not work

I have 2 components, a parent and a child. I want to modify the value of a prop in my parent component (by calling a method when a button is clicked) and send it to the child component. In the child component, I want to watch for changes in my prop, so that anytime it changes, it does something (for testing purposes, I tried to console.log() the prop).
Parent:
<template>
<div>
<h5>Your Feeds</h5>
<div id="feeds">
<div class="card" v-for="feed in feeds">
<div class="card-body" :id="feed['_id']" >
{{ feed['name'] }}
<button v-on:click="loadFeed(feed['_id'])">Button</button>
</div>
</div>
</div>
</div>
</template>
<script>
import GridComponent from "./GridComponent";
export default {
name: "FeedsListComponent",
data() {
return {
feeds: []
}
},
mounted() {
axios
.get("/getFeeds")
.then(response => (this.feeds = response.data))
.catch(error => console.log(error))
},
methods: {
loadFeed(id) {
this.feedId = id
}
},
components: {
GridComponent
}
}
</script>
Child:
<template>
<div id="grid">
<v-grid
theme="compact"
:source="rows"
:columns="columns"
></v-grid>
</div>
</template>
<script>
import VGrid from "#revolist/vue-datagrid";
export default {
name: "Grid",
props: ['feedId'],
data() {
return {
columns: [],
rows: [],
};
},
watch: {
feedId: function(val, oldVal) {
console.log(val)
console.log(oldVal)
console.log(this.feedId)
//here I want to send an ajax request with feedId to one of my controllers in order to get
//the data needed for rows and colums
}
},
components: {
VGrid,
},
};
</script>
I put together a sample that is working in order to help you diagnose why yours isn't working:
Parent.vue
<template>
<div class="parent">
<h3>Parent</h3>
<div class="row">
<div class="col-md-6">
<button class="btn btn-secondary" #click="incrementCounter">Change parent message</button>
</div>
</div>
<child :propMessage="message" />
</div>
</template>
<script>
import Child from '#/components/stackoverflow/watch-prop/Child'
export default {
components: {
Child
},
data() {
return {
counter: 0
}
},
computed: {
message() {
return 'Message' + this.counter;
}
},
methods: {
incrementCounter() {
this.counter++;
}
}
}
</script>
Child.vue
<template>
<div class="child">
<hr>
<div class="row">
<div class="col-md-6">
<label>Message in child from watched prop:</label>{{ dataMessage }}
</div>
</div>
</div>
</template>
<script>
export default {
props: {
propMessage: {
type: String,
required: true
}
},
data() {
return {
dataMessage: this.propMessage
}
},
watch: {
propMessage(newMessage) {
this.dataMessage = newMessage;
}
}
}
</script>
<style scoped>
label {
font-weight: bold;
margin-right: 0.5rem;
}
</style>

Vuejs passing function from parent to child

I have a beginner question about passing function from parent to child. In my example, I want to to use the child more times and sometimes it should to do someting else v-on:focus. How can i do that? There are options to pass it with prop but i don't know how and i think it's not good to do it ? Maybe with EventBus and if yes then how ? I want to know the right way how to do it in VueJs.
Here is the Parent Component:
import Child from "./child.js";
export default {
name: "app",
components: {
Child
},
template: `
<div>
<child></child>
<child></child>
<child></child>
</div>
`
};
And here is the child Component:
export default {
name: "test",
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="functionFromChild">
</div>
</div>
`,
methods: {
functionFromChild() {
//run the function from parent
}
}
};
You can pass the function as any other prop
import Child from "./child.js";
export default {
name: "app",
components: {
Child
},
methods: {
calledFromChild(id){
console.log(id)
}
},
template: `
<div>
<child :callback="calledFromChild" id="1"></child>
<child :callback="calledFromChild" id="2"></child>
<child :callback="calledFromChild" id="3"></child>
</div>
`
};
And then in the child
export default {
name: "test",
props: ["callback", "id"],
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="() => this.calledFromChild(this.id)">
</div>
</div>
`,
}
I'm also adding an id to the child so you know which child is making the call.
But this is not a good idea. You should use emit from your child to send an event, and listen to it from the parent.
In the child
export default {
name: "test",
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="handleFocus">
</div>
</div>
`,
methods: {
handleFocus() {
this.$emit('focusEvent')
}
}
};
And in the parent
<child #focusEvent="handleFocusFromChild"></child>
A working example here
This should work:
const Child = {
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="functionFromChild">
</div>
</div>
`,
props: {
functionFromParent: Function
},
methods: {
functionFromChild: function() {
this.functionFromParent();
}
},
data() {
return {
message: 'Oh hai from the component'
}
}
}
const App = {
template: `
<div>
<h1>Quick test</h1>
<p>{{ message }}</p>
<Child :functionFromParent="functionOnParent"/>
<Child :functionFromParent="functionOnParent"/>
<Child :functionFromParent="functionOnParent"/>
</div>
`,
components: {Child},
methods: {
functionOnParent: function(){
console.log("there we go");
}
},
data() {
return {
message: 'Hello'
}
}
}
new Vue({
render: h => h(App),
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
</div>
If you're trying to call a function in the parent from the child component, then try
this.$parent.parentMethod()
This will invoke the method in parent component.

how to enable v-model binding when building a custom components from other custom components

I am able to build a simple textbox component from <input /> and setup v-model binding correctly.
I'm trying to do same with a custom component: vs-input from vuesax.
Following the pattern below does not work as expected:
<template>
<div>
<vs-input type="text" v-model="value" #input="text_changed($event)" />
<!-- <input type="text" :value="value" #input="$emit('input', $event.target.value)" /> -->
</div>
</template>
<script>
export default {
name: 'TestField',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {}
},
methods: {
text_changed(val) {
console.log(val)
// this.$emit('input', val)
}
}
}
</script>
In building custom components from other custom components is there anything particular we should look out for to get v-model binding working properly?
Following code might help you.(Sample code try it in codepen)
updating props inside a child component
//html
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ message }}</p>
<input type="text" :value="test" #change="abc">
{{ test }}
</div>
//VUE CODE
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
},
props:{
test:{
type:String,
default:''
}
},
methods:{
abc:function(event){
//console.log("abc");
console.log(event.target.value);
this.test=event.target.value;
}
}
})
I prefer to interface props with computed:
<template>
<div>
<vs-input type="text" v-model="cValue" />
</div>
</template>
<script>
export default {
name: 'TestField',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {}
},
computed: {
cValue: {
get: function(){
return this.value;
},
set: function(val){
// do w/e
this.$emit('input', val)
}
}
}
}
</script>
Computed Setter