I wonder how can I by clicking the individual string item of the bottom list send it's value to the input ? I mean when I click on the "car" the input gets filled with "car". I assume I'm setting incorrect params to selectCategorie method
<template>
<input type="text" v-model="input" placeholder="Search vehicle..." />
<div v-for="(categorie, index) in filteredList()" :key="categorie">
<p #onClick="selectCategorie(index)" >{{ categorie }}</p>
</div>
<div v-if="input&&!filteredList().length">
<p>No results found!</p>
</div>
</template>
<script>
export default {
data() {
return {
input: '',
categoriesStatic: ['car', 'bus', 'moto', 'bike' ]
}
},
methods: {
filteredList() {
return this.categoriesStatic.filter((categ) =>
categ.toLowerCase().includes(this.input.toLowerCase())
);
},
selectCategorie(index) {
this.input = categorie(index)
}
}
}
</script>
Related
I have this html:
<div v-for="item in my_items">
<div>
<input type="text" :value=item.name />
</div>
<div>
<button #click="edit(item.id, item.name)">Edit</button>
</div>
</div>
And then this javascript:
export default {
data() {
return {
my_items: []
}
},
methods: {
async edit(id, name){
console.log("edit", id);
console.log("name", name);
},
},
async mounted() {
this.my_items = [
{id: 1, name: 'name1'}
,{id: 2, name: 'name2'}
];
}
}
So when component is mounted, two rows will be displayed: "name1" and "name2". Along with a button to edit it.
When i write something else in the input field and then click on the edit button, in the console I still see the "old" name for the variable. How can I access the current value in the input field from the function "edit()"?
You can use v-model or else you need #input along with :value:
const app = Vue.createApp({
data() {
return {
my_items: []
};
},
mounted() {
this.my_items = [{id: 1, name: 'name1'}, {id: 2, name: 'name2'}
];
},
methods: {
edit(item) {
console.log("item", item);
},
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div v-for="item in my_items" :key="item.id">
<div>
<input type="text" v-model="item.name" />
</div>
<div>
<button #click="edit(item)">Edit</button>
</div>
</div>
</div>
The question has three parts revolving around two files App.vue and X Array.vue :-
1.When value of input is changed, how it could be written back to the array?
2.If the value entered is empty how to remove the element from array?
3.How to show one extra input element always so that it is possible to add new values(linked with 2)?
XArray should basically be an array editor.
App.vue
<template>
<div>
<XArray v-model="myArray" />
<pre>{{ myArray }}</pre>
</div>
</template>
<script>
import XArray from './components/XArray.vue';
export default {
components: {
XArray,
},
data: () => {
return {
myArray: ['one', 'two'],
};
},
};
</script>
XArray.vue
<template>
<input
v-for="(option, index) in modelValue"
:key="index"
#input="$emit('update:modelValue', [...modelValue, `${$event.target.value}`])"
/>
</template>
<script>
export default {
props: {
modelValue: {
type: Array,
required: true,
},
};
</script>
Please take a look at following snippet:
const app = Vue.createApp({
data() {
return {
myArray: ['one', 'two'],
}
},
methods: {
addEl() {
this.myArray.push('new')
}
}
})
app.component('child', {
template: `
<div>
<input
v-for="(option, index) in modelValue"
:key="index"
#input="$emit('update:modelValue', upd(index, $event.target.value))"
:value="option"
/>
</div>
`,
props: {
modelValue: {
type: Array,
required: true,
}
},
methods: {
upd(idx, val) {
return val ? [
...this.modelValue.map((item, i) =>
i !== idx
? item
: val
),
] : this.modelValue.length > 1 ?
[ ...this.modelValue.filter((item, i) => {
if(i !== idx) return item
})] :
[ "last" ]
}
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<child v-model="myArray"></child>
<pre>{{ myArray }}</pre>
<button #click="addEl">add</button>
</div>
I have the following code snippet from my app component:
<template>
<div>
<h3>Basic</h3>
<div v-for="(field, index) in basics" :key="index">
<input v-model="basics.name" placeholder="Name" type="text">
<br>
<br>
<input v-model="basics.email" placeholder="Email" type="email">
<br>
<hr/>
<button #click.prevent="addField">Add</button>
<button #click.prevent="removeField(index)">Remove</button>
<br>
<button #click.prevent="back">Back</button>
<button #click.prevent="toNext">Next</button>
</div>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
name: "Basics",
data() {
return {
basics: [{
name: "",
email: ""
}]
};
},
methods: {
...mapActions(["addBasicData"]),
addFied(){
this.basics.push({
name: "",
email: ""
});
},
removeField(index){
this.basics.splice(index, 1);
},
toNext() {
this.addBasicData(this.basics);
this.$router.push({ name: "Location" });
},
back() {
this.$router.back();
}
}
};
</script>
In the code above when I finish filling up the form and click next button the data is sent to the state and we are guided to another route named "Location".
When I click back button in the "Location" route I'm back to route named "Basic".
The issue here is when I'm brought back to the route named "Basic" the form fields are empty although they are binded with the data object.
How do I populate these input fields when I return back to same route ?
Here is the working replica of the app: codesandbox
<div v-for="(field, index) in basics" :key="index">
<input v-model="basic.name" placeholder="Name" type="text">
<input v-model="basic.email" placeholder="Email" type="email">
<button #click.prevent="removeField(index)">Remove</button>
</div>
<hr/>
<button #click.prevent="addField">Add</button>
<br>
<button #click.prevent="back">Back</button>
<button #click.prevent="toNext">Next</button>
methods: {
addField() {
this.$store.commit('addBasic',{name:"",email:""} )
},
removeField(index) {
this.$store.commit('removeField',index )
},
toNext() {
this.$router.push({ name: "Location" });
}
},
computed: {
basic:{
get() {
return this.$store.getters.getBasic;
}
}
}
store.js
// ...
state: {
basic:[{name:"Jonny",email:"jonny#mail.com"},
{name:"Bonny",email:"Bonny#mail.com"}]
}
mutations: {
addBasic(state,value) {
state.basic.push(value)
},
removeField(state,index ){
state.basic.splice(index,1);
}
}
Thats just one of two versions how you can do it.
Or you can map the mutatations and call them directly in the click event.
https://vuex.vuejs.org/guide/mutations.html
https://vuex.vuejs.org/guide/forms.html
The add field button makes only sense outside of the loop.
addBasicData you dont need it
This method somehow works:
mounted() {
// eslint-disable-next-line no-unused-vars
let fromState = this.$store.state.Basics.basics;
if (fromState) {
this.basics.name = fromState.name;
this.basics.email = fromState.email;
}
}
I will really appreciate if there are any other convenient method to achieve this.
Tried mapState but didn't work
Background- I have created an app where the parent component can create and delete a input field (child component) on a click of a button. The input's value is recorded on screen through v-model
Problem- When a new input is created the previous value is replaced by the new input value.
Expectation- When a new input is created it adds the value of the previous input value
A visual for more clarity
https://i.stack.imgur.com/Fp8Mk.png
Parent Component
<form-input v-for="n in count" :key="n" :value="expense" #input="expense = $event"></form-input>
<button #click="addInputs">Add Expense</button>
<button #click="deleteInputs">Delete</button>
<p>Total Expense: {{ expense }}</p>
export default {
components: {
"form-input": formInput
},
name: "form",
data() {
return {
count: 0,
expense: 0
};
},
methods: {
addInputs: function() {
this.count++;
},
deleteInputs: function() {
this.count--;
}
}
};
Child Component
<input type="text" placeholder="Expense" />
<input type="number" placeholder="Amount" #input="$emit('input', $event.target.value)" />
Here, I made a sandbox for you to see my solution for this as there are a lot of changes and you can see how it performs.
https://codesandbox.io/s/confident-fire-kpwpp
The main points are:
You have to keep track of the values of each input separately. This is done using an array.
When this array is changed we recalculate the total expense
Parent
<template>
<div>
<form-input v-for="(n, idx) in count" :key="n" :id="idx" #input="getExpense"></form-input>
<button #click="addInputs">Add Expense</button>
<button #click="deleteInputs">Delete</button>
<p>Total Expense: {{ totalExpense }}</p>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
components: {
"form-input": HelloWorld
},
name: "form",
data() {
return {
count: 0,
expenses: [],
totalExpense: 0
};
},
methods: {
addInputs: function() {
this.count++;
this.expenses[this.count - 1] = 0;
},
deleteInputs: function() {
this.count--;
this.expenses.pop();
this.setTotalExpense();
},
getExpense(data) {
this.expenses[data.id] = parseInt(data.value, 10) || 0;
this.setTotalExpense();
},
setTotalExpense() {
console.log(this.expenses);
this.totalExpense = this.expenses.reduce((sum, val) => {
return sum + val;
}, 0);
}
}
};
</script>
Child
<template>
<div class="hello">
<input type="text" placeholder="Expense">
<input
type="number"
placeholder="Amount"
#input="$emit('input', {
value: $event.target.value,
id
})"
>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
id: Number
}
};
</script>
I used the HelloWorld template so that's why there are some references to that, but I'm sure you can easily clean that up :)
And also, there may be some small edge case bugs that you can clean up. This should point you in the right direction though.
I'm a newbie of Vue, and I'm trying to simply clear the data of input component once I've submitted, but it seems I'm missing something, because since it's parent data is cleared, I still see the filled value of the input component.
Here is a living example.
I've set to the input child component v-model="title" from it's parent wrapper. Once I submit the data to the parent, I call addItem and in the end, I supposed to clear the data model just by clear it this.title = '', but I probably do something wrong on how to bind data from parent to child.
And above the code, starting from the parent component:
<template>
<form #submit="addItem" class="todo-insert">
<input-item
icon="create"
name="title"
placeholder="Add a ToVue item..."
v-model="title"
/>
<button-item tone="confirm" class="todo-insert__action">
Aggiungi
</button-item>
</form>
</template>
<script>
import ButtonItem from '#vue/Form/ButtonItem/ButtonItem.vue'
import InputItem from '#vue/Form/InputItem/InputItem.vue'
import uuid from 'uuid'
export default {
name: 'TodoInsert',
components: {
ButtonItem,
InputItem
},
data () {
return {
title: ''
}
},
methods: {
addItem (e) {
e.preventDefault()
const todo = {
id: uuid.v4(),
isComplete: false,
title: this.title
}
this.$emit('add-todo', todo)
this.title = ''
}
}
}
</script>
<style lang="scss" scoped src="./TodoList.scss"></style>
This is the child input component:
<template lang="html">
<label class="input">
<div v-if="label" class="input__label text-sans text-sans--label">
{{ label }}
</div>
<div class="input__row">
<input
:autocomplete="autocomplete"
:class="[hasPlaceholderLabel, isDirty]"
:name="name"
:placeholder="placeholder"
class="input__field"
type="text"
v-on:input="updateValue($event.target.value)"
v-on:blur="updateValue($event.target.value)"
>
<div v-if="placeholderLabel" class="input__placeholder text-sans text-sans--placeholder">
{{ placeholderLabel }}
</div>
<div v-if="icon" class="input__icon-area">
<icon-item
:name="icon"
/>
</div>
</div>
</label>
</template>
<script>
import IconItem from '../../IconItem/IconItem.vue'
export default {
name: 'InputItem',
props: {
autocomplete: {
type: String,
default: 'off'
},
icon: String,
label: String,
name: {
type: String,
default: 'input-text'
},
placeholder: String,
placeholderLabel: String
},
computed: {
hasPlaceholderLabel () {
return this.placeholderLabel ? 'input__field--placeholder-label' : ''
},
isDirty () {
// return this.$attrs.value ? 'input__field--dirty' : ''
return 'input__field--dirty'
}
},
methods: {
updateValue: function (value) {
this.$emit('input', value)
}
},
components: {
IconItem
}
}
</script>
<style lang="scss" src="./InputItem.scss"></style>
What am I missing?
Your child component is bound unidirectionally. It means that it can change the value, but does not receive any update from the parent component. To receive updates, you need to receive the property value in your child:
props: {
value: String
}
Then, you need to pass the value received to the input :
<input
:value="value"
:autocomplete="autocomplete"
:class="[hasPlaceholderLabel, isDirty]"
:name="name"
:placeholder="placeholder"
class="input__field"
type="text"
v-on:input="updateValue($event.target.value)"
v-on:blur="updateValue($event.target.value)"
>
Now the input should update when the parent component changes the value