How to handle different input values created with v-for - vue.js

I was wondering how to handle different input values when they have been created with v-for. In this example, let's say I have some tweets from different users, each having their own reply field. How can I treat the input values as separate entities?
In this Fiddle, you can see the input values bind to the other input values. How could I pass the value of the selected field to the the reply method without the current binding side-effect? Thanks.
JS Fiddle: https://jsfiddle.net/n2fole00/n60d4qos/
HTML
<div id="app" style="margin:10px;">
<div v-for="(tweet, key) in tweets">
<p><strong>{{ tweet.user }} wrote...</strong></p>
<p>{{ tweet.text }}</p>
<label v-bind:for="'reply-'+key">Reply: </label>
<input v-bind:id="'reply-'+key" v-model="replies" type="text">
<button v-on:click="reply(replies, tweet.user)">Reply</button>
<hr>
</div>
</div>
Vue
new Vue({
el: "#app",
data: {
replies: [],
tweets: [
{ text: "Learn JavaScript", user: "Bob" },
{ text: "Learn Vue", user: "Alice"},
{ text: "Play around in JSFiddle", user: "John" },
]
},
methods: {
reply(reply, user) {
alert("You replied\n" + reply + "\nto " + user);
},
},
})

In a larger/production app, I would probably make each user field its own component (or two, one for the tweet and one for the reply, both orchestrated by this parent).
in this case, you can use the key as the array index for replies and get only the relevant information. See the updated fiddle.
in template:
<input v-bind:id="'reply-'+key" v-model="replies[key]" type="text">
<button v-on:click="reply(replies, tweet.user, key)">Reply</button>
in methods:
reply(reply, user, key) {
alert("You replied\n" + reply[key] + "\nto " + user);
},
https://jsfiddle.net/n60d4qos/9/

Related

Vue - Select all checkbox

I'm relatively new to Vue so bare with me on this.
I have a v-for which produces a list of checkboxes, these all individually function correctly if I were to click on them separately, what I am after, like the title says is a select all checkbox to sit ontop which selects all in the list, but I'm running into a few issues which I don't understand, see relevant code below:
new Vue({
el: "#lister-app",
mixins: [pagination, baseDownloadParent],
components: { selectField, articleItem, documentItem },
data: {
facets: {},
isCheckAll: false,
},
computed: {
getFacets() {
return this.facets;
},
getFacetsLength() {
return this.facets.length;
},
},
methods: {
toggleSelect(item, facet) {
if (!this.params[facet.name]) Vue.set(this.params, facet.name, []);
const existElem = this.params[facet.name].findIndex((el) => {
return el === item.identifier;
});
if (existElem !== -1) this.params[facet.name].splice(existElem, 1);
else this.params[facet.name].push(item.identifier);
},
checkAll(){
console.log('FunctionWhichChecksAll');
},
},
});
<label class="option-checkbox" for="Select all">
<input id="Select all" class="option-checkbox__input" type="checkbox" #click='checkAll()' v-model='isCheckAll'>
<span class="option-checkbox__text">Select all</span>
<span class="option-checkbox__icon"></span>
</label>
<option-field inline-template v-for="(item, i) in facet.items" :for="item.name" :key="i + item.name">
<label class="option-checkbox">
<input :id="item.name" class="option-checkbox__input" type="checkbox" v-model="checked" #change="toggleSelect(item, facet)">
<span class="option-checkbox__text">{{item.name}} </span>
<span class="option-checkbox__icon"></span>
</label>
</option-field>
What I am picturing is as piece of script which is inside the checkAll() function?
Any help would be appreciated, thank you in advance.
Here is a working demo, based on your code (simplified):
https://stackblitz.com/edit/vue-prghrq?file=src%2FApp.vue
Checkboxes in Vue can sometimes be a mess, when you combine v-model with #click handlers and especially if you have a check-all field. That's why I usually don't use v-models for it. Using only the :checked value as a one-way binding makes it easier to read and to maintain. With the click handler you can then update the state of each entry.

Vue-MultiSelect: Can't seem to get property from an object during select event

I am using the Vue-Multiselect plugin for an input drop down box and trying to get the customer's last name when they fill out a form. For some reason I am just getting an object as the value of the issue.customer_last_name property (see screenshot below). I want to get a string Doe, instead.
Anyone have any ideas ? My goal is to eventually submit this data (POST request) via a submit button to a remote api.
Link to codesandbox replicating the issue: https://codesandbox.io/s/vue-template-9z1wd?fontsize=14&module=%2Fsrc%2Fcomponents%2FTest.vue
Full code:
<template>
<div>
<label for="customer_last_name_input">Last Name:</label>
<multiselect
id="customer_last_name_input"
v-model="issue.customer_last_name"
:options="customers"
placeholder="Select an existing customer or type to add a new one"
:custom-label="customerSelectName"
label="lastname"
track-by="uid"
:close-on-select="true"
#select="onSelect"
></multiselect>
<br>
<!-- <input id="customer_last_name_input" type="text" class="form-control" v-model="issue.customer_last_name" placeholder required /> -->
<div>
<label for="customer_first_name_input">First Name:</label>
<input
id="customer_first_name_input"
type="text"
v-model="issue.customer_first_name"
placeholder
required
>
</div>
</div>
</template>
<script>
import Multiselect from "vue-multiselect";
export default {
name: "test",
components: {
Multiselect
},
data() {
return {
customers: [
{
uid: "1",
firstname: "John",
lastname: "Doe",
email: "johndoe#aol.com",
phone: null,
c_organization: "ACME",
c_title: null
},
{
uid: "2",
firstname: "Mary",
lastname: "Smith",
email: "msmith#aol.com",
phone: null,
c_organization: "NBA",
c_title: "Miss"
}
],
issue: {
customer_first_name: "",
customer_last_name: ""
//customer_email: ""
}
};
},
// async created() {
// await this.getCustomers()
// },
methods: {
customerSelectName(option) {
//return `${option.lastname}, ${option.firstname}`
return `${option.lastname}, ${option.firstname}`;
},
onSelect(option) {
console.log(option.lastname);
this.issue.customer_last_name = option.lastname;
this.issue.customer_first_name = option.firstname;
// this.issue.customer_email = option.email
}
}
};
</script>
The problem is the v-model="issue.customer_last_name" on the multi-select itself. That's setting the selection of the multi-select component to the whole object that the user selects, not just the last name.
Since you're using #select="onSelect" to do all the updating necessary, just remove the v-model (line 6 in the code sandbox) and you'll be all set.
Edited to add:
When you're seeing unhelpful [object Object], it can be useful to render that somewhere else, where it's more likely to be fully expanded. It's all somewhat browser dependent, but adding <div>{{issue.customer_last_name}}</div> was a helpful debugging tool.

How i can validate data() value without input with vee-validate

I have a button, for load files or add some text
After load it pushed in data() prop
How i can validate this prop, if them not have input
Im found only one solution - make watch for data props. and set validate in
Maybe exist more beautiful way?
I try validator.verify() - but it dont send error in main errorBag from validateAll
This is example
<div id="app">
<testc></testc>
</div>
<script type="text/x-template" id="test">
<div>
<input type="text" v-validate="'required'" name="test_vee">
{{errors.first('test_vee')}}
<hr>
<button #click="addRow">Add</button>
<input type="text" v-model="inputValue" name="test_input"/>
<hr>
{{rows}}
<hr>
{{errors.first('rows')}}
<button #click="validateForm">Validate</button>
</div>
</script>
and script
Vue.component('testc', {
template: '#test',
data() {
return {
inputValue: '',
rows: []
}
},
watch: {
rows: {
handler: function(newVal, oldVal) {
this.$validator.errors.remove('rows');
if (this.rows.length < 2) {
this.$validator.errors.add({
id: 'rows',
field: 'rows',
msg: 'Need 2 rows!',
});
}
}
}
},
methods: {
addRow: function() {
this.rows.push(this.inputValue);
this.inputValue = '';
},
validateForm: function(){
this.$validator.validateAll();
}
}
});
Vue.use(VeeValidate);
new Vue({
el: '#app'
})
https://codepen.io/gelid/pen/YBajER
First input in example: default validate - its ok
Second input: for add items - dont need validate or has self validate (not empty for example)
In data of component i have prop rows - it is need validate before ajax request to backend for save data

Array of inputs, with last field always blank for new add

JSBin and Stackoverflow snippet are below.
I am trying to have a list of input components. If all input components are filled with a value (not blank), then there should be a "new blank field" visible at the end for the user to type into. When he types into it, it should make this field apart of the list above it, maintaining focus in it.
However the problem I'm having is, focus maintains in the new field, and never moves into the array. Here is my code:
JSBIN and stackoverflow snippet - https://jsbin.com/cudabicese/1/edit?html,js,output
const app = new Vue({
el: '#app',
data: {
inputs: [
{ id:'foo', value:'foo' },
{ id:'bar', value:'bar' }
]
},
methods: {
addRow(e) {
this.inputs.push({
id: Date.now(),
value: e.target.value
})
},
deleteRow(index) {
this.inputs.splice(index, 1)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<div id="app">
<ul>
<li v-for="(input, index) of inputs">
<input type="text" v-model="input.value">
</li>
<li v-if="inputs.filter(input => !!input.value).length">
<input type="text" #input="addRow">
</li>
</ul>
</div>
I'd recommend you put the input for the list within a computed function vs directly using the data. The examples at https://v2.vuejs.org/v2/examples/ are a good place to start.

How to get checked value of checkbox if use array as a model in vue

I wonder if I use array as model for multiple-checkbox list, how can I check which items is checked and which are unchecked efficiently rather than compare one by one with that array?
<ul>
<li v-for="task in tasks">
<input type="checkbox" :id="task.title" :value="task" v-model="selectedTasks" #change="handleTasks(task)">
<label :for="task.title">{{task.title}}</label>
</li>
</ul>
new Vue({
data: {
tasks: [
{ title: 'Go to the store' },
{ title: 'Clean the house' },
{ title: 'Learn Vue.js' }
],
selectedTasks: []
},
})
You could add a property to each task (e.g., checked) and bind that to each input's v-model, making it trivial in code to check whether a task is checked/selected:
new Vue({
el: '#app',
data() {
return {
tasks: [
{ title: 'Go to the store', checked: false },
{ title: 'Clean the house', checked: false },
{ title: 'Learn Vue.js', checked: false }
]
};
},
methods: {
clearCheckedTasks() {
this.tasks = this.tasks.filter(x => !x.checked);
}
}
})
<script src="https://unpkg.com/vue#2.5.16"></script>
<div id="app">
<ul>
<li v-for="task in tasks">
<input type="checkbox" :id="task.title" v-model="task.checked">
<label :for="task.title">{{task.title}}</label>
</li>
</ul>
<button #click="clearCheckedTasks">Clear checked tasks</button>
<h3>tasks (live)</h3>
<pre>{{tasks}}</pre>
</div>
based on your comment that "I also want to know if one item is checked or unchecked when I click on it", I would use the DOM Event object to detect if it's checked.
demo: https://jsfiddle.net/jacobgoh101/m480bupd/6/
add #click="clickHandler" to the input
<input type="checkbox" :id="task.title" :value="task" v-model="selectedTasks" #change="handleTasks(task)" #click="clickHandler">
use e.target.checked to get the checked
methods: {
clickHandler(e) {
console.log(e.target.checked);
},
// ...
}
Use the loop index:
<li v-for="(task, index) in tasks">
<input type="checkbox" :id="task.title" :value="task" v-model="selectedTasks[index]" #change="handleTasks(task)">
<label :for="task.title">{{task.title}}</label>
</li>