How to pass elements in a for loop to parent component? - javascript

Background I have a child component that loops through an array called "expenseButton" passed from my parent component. Within this for loop are elements that are getting updated. Specifically the "expense".
Child component
<form class="container">
<div class="buttonList" v-for="(expense, index) in expenseButton" :key="index">
<button type="button" #click="expenseButtonClick(expense)">{{expense.expensesKey}}</button>
<input class="textInput" v-model.number="expense.subExpense" type="number" />
</div>
</form>
<script>
export default {
props: {
expenseButton: Array,
},
methods: {
expenseButtonClick(expense) {
expense.expensesValue = expense.expensesValue - expense.subExpense;
}
}
}
</script>
Problem I understand that $emit events can pass data to the parent. However, I am trying to figure the best way to send the updated elements of the array back to the parent component.
The Parent component data
<template>
<expense-button :expenseButton="expenseButton"></expense-button>
</template>
<script>
export default {
components: {
"expense-button": Expenses
},
data() {
return {
expenseButton: [
{"expensesKey":"rent","expensesValue":null,"subExpense":""},
{"expensesKey":"movies","expensesValue":null,"subExpense":""},
{"expensesKey":"clothes","expensesValue":null,"subExpense":""}
],
};
}
}
</script>

I think you should use $emit.
Child component:
<form class="container">
<div class="buttonList" v-for="(expense, index) in expenseButton" :key="index">
<button type="button" #click="expenseButtonClick(expense)">{{expense.expensesKey}}</button>
<input class="textInput" v-model.number="expense.subExpense" type="number" />
</div>
</form>
<script>
export default {
props: {
expenseButton: Array,
},
methods: {
expenseButtonClick(expense) {
expense.expensesValue = expense.expensesValue - expense.subExpense;
this.$emit("expense-btn-clicked", expense)
}
}
}
</script>
Parent component:
<template>
<expense-button :expenseButton="expenseButton" #expense-btn-clicked="btnClickedHandler"></expense-button>
</template>
<script>
export default {
components: {
"expense-button": Expenses
},
data() {
return {
expenseButton: [
{"expensesKey":"rent","expensesValue":null,"subExpense":""},
{"expensesKey":"movies","expensesValue":null,"subExpense":""},
{"expensesKey":"clothes","expensesValue":null,"subExpense":""}
],
}
},
methods: {
btnClickedHandler(e) {
console.log(e)
}
}
}
</script>

Related

Vue.js - Pass data from child component to parent using emit but without button

I have a parent form template and each question of the form is inside a child component, like this
<template>
<question1
#display-answer-1="setAnswer1"
/>
<!-- other child components here... -->
</template>
<script>
import Question1 from '...path...';
export default{
components: { Question1 },
data() {
answer1: ''
},
methods: {
setAnswer1(answer1) {
this.answer1 = answer1;
}
}
};
and my child component is like this
<template>
<input type="text" v-model="answer1"/>
<div>
<button
type="button"
#click="saveQ2"
>Save
</button>
</div>
</template>
<script>
export default {
data() {
return {
answer1: ''
};
},
methods: {
saveQ2() {
const answer1 = this.answer1;
this.$emit('display-answer-1', answer1);
}
}
};
This code works, but in this way I'm forced to put a button whenever there is a question to pass data from the child to the form template parent. Is there a smart alternative not to put a save button under each question?
you can use the blur event whenever an input gets unfocused it'll fire the event .
<template>
<input #blur="saveQ2" type="text" v-model="answer1"/>
</template>
<script>
export default {
data() {
return {
answer1: ''
};
},
methods: {
saveQ2() {
const answer1 = this.answer1;
this.$emit('display-answer-1', answer1);
}
}
};

Automatically update the input field within a form, vue.js

I want to search for elements using an input field within a form. I am passing a value from a component to another and the content changes but only after pressing enter. Is there a way to automatically update the list, after every new letter is typed?
Here is my code:
Parent(App):
<template>
<div id="app">
<Header v-on:phrase-search="passPhrase" />
</div>
</template>
<script>
import Header from ...
export default {
name: "App",
components: {
Header,
},
data() {
return {
posts: [],
searchedPhrase: ""
};
}
computed: {
filteredPosts() {
let temp_text = this.searchedPhrase;
temp_text.trim().toLowerCase();
return this.posts.filter(post => {
return post.name.toLowerCase().match(temp_text);
});
}
},
methods: {
passPhrase(phrase) {
this.searchedPhrase = phrase;
}
}
};
</script>
Child(Header):
<template>
<div class="child">
<p>Search:</p>
<form #submit.prevent="phrasePassed">
<input type="text" v-model="phrase" />
</form>
</div>
</template>
<script>
export default {
name: "search",
data() {
return {
phrase: ""
};
},
methods: {
phrasePassed() {
this.$emit("phrase-search", this.phrase);
}
}
};
</script>
passPhrase() brings the value from the child to the parent and then filteredPosts() find appropriate elements. I suspect that the form might be guilty of this issue but I do not know precisely how to get rid of it and still be able to pass the value to the parent.
Thanks
in the child you use submit event which called on enter. you should use #input on the input itself. and btw you didnt need even to declare pharse in the data because you didnt use it in the child. you just pass it up
you it like so
<template>
<div class="child">
<p>Search:</p>
<form>
<input type="text" #input="phrasePassed">
</form>
</div>
</template>
<script>
export default {
name: "search",
methods: {
phrasePassed(e) {
this.$emit("phrase-search", e.target.value);
}
}
};
</script>

pattern to add to array item from child component

I have two components, a parent and child one, the
parent data attribute I've set up it like this...
data () {
return {
users: [],
}
}
the users array is populated by a button click, i share this array with the child component.
The child component is trying to add a user to this list which works(adding value to passed in props), but because the users array is declared under data the parent component refreshes and i lose my users...
is there a pattern to keep the users array values and add to them via a child...
sorry if this is obvious but as i said i'm just starting...
edit : adding code (parent component)
<template>
<div id="app">
<div>
<button v-on:click="display()">Display users</button>
<button v-on:click="displaySingleUserInput =
!displaySingleUserInput">Add user</button>
<div>
</div>
<ul v-if="errors && errors.length">
<li v-for="error of errors">
{{error.message}}
</li>
</ul>
</div>
<add-user v-on:submit_user="addUser" v-show="displaySingleUserInput"></add-user>
<user-list v-bind:users="users"></user-list>
</div>
</template>
<script>
import axios from 'axios';
import UserList from './components/UserList';
import AddUser from './components/AddUser';
export default {
components: {
'user-list': UserList,
'add-user': AddUser
},
name: 'app',
data () {
return {
users: [],
errors: [],
displaySingleUserInput: false
}
},
methods: {
display: function(string)
{
axios.get(`users.json`)
.then(response => {
// JSON responses are automatically parsed.
this.users = response.data.users
})
.catch(e => {
this.errors.push(e)
})
},
addUser: function(id) {
this.users.push({firstname: "john", lastName: 'jones'})
},
}
}
</script>
child component
<template>
<div id="singleUserAdd">
<form id=addUser aria-label="single user add">
<button v-on:click="submit()">Submit</button>
<button>Cancel</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
submit: function() {
this.$emit('submit_user', 1)
}
}
}
</script>
I assume that you have a method called addUser in your child component :
addUser(){
this.$emit("addusr",this.newuser);
}
In the parent one :
<child-comp #addusr="addNewUser" />
...
addNewUser(newuser){
this.users.push(newuser);
}
looks like i was missing
<form v-on:submit.prevent="onSubmit"
to stop my page from refreshing

Pass data to another component

I have a simple form component:
<template>
<div>
<form #submit.prevent="addItem">
<input type="text" v-model="text">
<input type="hidden" v-model="id">
<input type="submit" value="enviar">
</form>
</div>
</template>
This component has a method that use $emit to add text item to a parent data:
addItem () {
const { text } = this
this.$emit('block', text)
},
Here is markup on my main file:
<template>
<div id="app">
<BlockForm #block="addBlock"/>
<Message v-bind:message="message"/>
</div>
</template>
And the script:
export default {
name: 'app',
components: {
BlockForm,
Message
},
data () {
return {
message : []
}
},
methods: {
addBlock (text) {
const { message } = this
const key = message.length
message.push({
name: text,
order: key
})
}
}
}
My question is: Message component list all items create by BlockForm component and stored inside message array. I add a edit button for each item inside Message list. How can I pass item text to be edited in BlockForm component?
You could just bind the input inside the BlockForm to a variable that is in the parent component. This way when you $emit from the child component, just add the value to the messages.
export default {
name: 'app',
components: {
BlockForm,
Message
},
data () {
return {
message : [],
inputVal: {
text: '',
id: ''
}
}
},
methods: {
addBlock () {
const key = this.message.length
this.message.push({
name: this.inputVal.text,
order: this.inputVal.text.length // If the logic is different here, you can just change it
})
this.inputVal = {
text: '',
id: ''
}
}
}
}
Now when you are calling the BlockForm,
<template>
<div id="app">
<BlockForm propVal="inputVal" #block="addBlock"/>
<Message v-bind:message="message"/>
</div>
</template>
and inside BlockForm,
<template>
<div>
<form #submit.prevent="addItem">
<input type="text" v-model="propVal.text">
<input type="hidden" v-model="probVal.id">
<input type="submit" value="enviar">
</form>
</div>
</template>
Now, when you press edit for existing message, simple assign that "Message" to inputVal data property mapping it to proper text and id.

Pass range input value on change in Vue 2

In Vue 2: I have an App component, which has a Slider component:
App.vue
<template>
<div>
<Slider :foo="store.foo"></Slider>
</div>
</template>
<script>
import store from './components/store.js';
import Slider from './components/Slider.vue';
export default {
name: 'app',
components: { Slider },
data() {
return {
store: store
}
},
methods: {
changeFoo(foo) {
console.log('change!', foo);
},
},
}
</script>
Slider.vue
<template>
<div>
<input type="range" min="1" max="100" step="1" #change="changeFoo" />
</div>
</template>
<script>
export default {
props: ['foo'],
methods: {
changeFoo() {
this.$emit('changeFoo', foo);
}
}
}
</script>
The problem is that the value of the slider is not being passed in the emit statement in Slider.vue. I can see why - but I'm not sure how to fix it. I tried doing:
v-model="foo"
in the input element, but Vue gives a warning that I'm not allowed to mutate props.
Instead of using prop create a new data variable for slider and pass this variable in the emit, like this:
<template>
<div>
<input v-model="sliderVal" type="range" min="1" max="100" step="1" #change="changeFoo" />
</div>
</template>
<script>
export default {
props: ['foo'],
data: function() {
return {
sliderVal: ""
}
}
methods: {
changeFoo() {
this.$emit('changeFoo', this.sliderVal);
}
}
}
</script>
Also in App.vue you will have to listed to this emitted event like this:
<template>
<div>
<Slider :foo="store.foo" #change-foo="changeFoo"></Slider>
</div>
</template>

Categories

Resources