How to validate multiple fields with same name in vue.js - vue.js

I have some input fields with same name, but when one of this input is invalid,all the other input fields display error.What is a good way to resolve this.
<tr v-for="(form, index) in forms"
:key="index">
<div>
<input
class="input"
#keyup.prevent="validateField('sourceName')"
type="text"
v-model="form.description"
>
</div>
<span v-if="hasErrorName">
{{ msgName }}
</span>
</tr>
methods: {
validateField(field) {
if (field === 'name') {
if (!this.form[0].description) {
this.hasErrorName = true;
this.msgName = 'Source Name is required.';
} else {
this.hasErrorName = false;
this.msgName = null;
}
}
}

You seem to be using a single variable msgName to track all the entries.
i'll modify your code a bit and you can try to change it. You can define msgName as an empty object msgName: {} in your data object.
<tr v-for="(form, index) in forms"
:key="index">
<div>
<input
class="input"
#keyup.prevent="validateField('sourceName', index)"
type="text"
v-model="form.description"
>
</div>
<span v-if="msgName[index]">
{{ msgName[index] }}
</span>
</tr>
methods: {
validateField(field, index) {
if (field === 'sourceName') {
if (!this.form[index].description) {
this.msgName[index] = 'Source Name is required.';
} else {
this.msgName[index] = null;
}
}
}

Related

Unable to set the value to the first input field using id of the field - in vue

I have an input field and a button (when clicked on displays a dropdown with few items) when selecting the items it has to be shown on the first input field. Similarly when clicking on the 2nd button where the dropdown is shown the selected value is shown in the 2nd input field. This entire runs in a for loop , which is where I am facing the problem.
<tr v-for="items in itemList">
<td valign="top"> {{items}} </td>
<td align="left" nowrap>
<input v-model="itemCode" type="text" :id="'item_code_'+items"
#input="handleInput"
size="20" maxlength="27"
autocomplete="off">
<br/>
</td>
<td align="left" nowrap>
<a id="myDropdown" class="dropdown" style="text-decoration:none;font-
size:10pt; padding-left: 10px;padding-right: 10px;"
#click="loadFavs()"
href="javascript:void(0)" title="Click to choose an item from your
favorites">
<img hspace="3" alt="Favorites" src="/images/icons/LoadFav.png"
height="16" width="16"
onmousemove="this.style.cursor='pointer'"
:id="'bd_fav_image_' + items" title="Click to choose an item from
your favorites">
<select class="dropdown-content" v-if="showFav" name="BOMList"
:id="'bd_list_'+items" style="font-size:10pt;width: 100%" v-
model="selected" #change="selectingFav(items)">
<option value=""></option>
<option v-for="(fav,index) in favList" :id="index" v-
bind:value="fav" :key="fav" v-bind:index="index">{{fav}}
{{index}}</option>
</select>
</a>
</td>
<td valign="top" nowrap >
<input type="Text"
:id="'bd_qty_ '+ index"
value="" size="2"
inputmode="numeric"
maxlength="">
</td>
</tr>
favList--> this list holds a list of items , For eg:- A,B,C,D
When I select A it has to be shown in the input field.
selectingFav: function(value) {
console.log("Inside the selectingFav..." + this.selected + "value is ." +value);
setTheValue(value);
}
function setTheValue(val){
console.log("Inside the setThevlaue");
if (val === 1 ){
console.log("inside the if");
$j('#item_code_1').val(this.selected);
console.log("inside the if witht the value " + $j('#item_code_1').val());
}
Tried setting the value based on the id of the input field but nothing is showing up.
If I set the v-model to the input field then all the 3 fields will be showing up the same value.
Can someone please let me know what is the issue. Hope these details are sufficient.
a) v-model is internally implemented as:
<input v-model="myval">
<!-- is --!>
<input :model-value="myval" #update:model-value="v => myval = v">
so you can freely define your own function
<input v-for="obj, ind of whatever" #update:model-value="v => myfn(v, obj, ind)">
b) same as you have an array you v-for on you may make a parallel array
// you get the idea
data: () => ({ imputs: entries.map(e => 0) })
<div v-for="entry, ind of imputs">
<Whatever :entry="entry"/>
<imput v-model="imputs[ind]">
</div>
c) keep your imputs in objects, generally the best choice
// you get the idea
data: () => ({ imputs: entries.map(e => ({ entry: e, input: 0 })) })
// or
computed: {pairs(){ return this.entries.map(e => ({ entry: e, input: 0 })) }}
<div v-for="item of imputs">
<Whatever :entry="item.entry"/>
<imput v-model="item.input">
</div>
Here is how you can achieve that.
data() {
return {
itemList: [
{ id: 1 , value: '' },
{ id: 2, value: '' },
{ id: 3, value: '' }
]
}
},
methods:{
selectingFav: function(value) {
// value holds the index
if (value === 1 )
this.itemList[0].value = this.selected;
else if(value === 2 )
this.itemList[1].value = this.selected;
else
this.itemList[2].value = this.selected;
}
}
}
In HTML template section
<tr v-for="(items,i) in itemList">
<td valign="top"> {{items.id}} </td>
<td align="left" nowrap>
<input v-model="items.value" type="text" :id="'item_code_'+items"
#input="handleInput" size="20" maxlength="27" autocomplete="off">
<br/>
</td>

Datatable v-for to produce checkbox or input based on data

Wondering how I can get my datatable to build a column that produces either a checkbox or input based on a value from data. This is the what I have but I have a good feeling there is a way better way of doing this.
<div v-for=”shirt in shirts”>
<div v-if=”stock.shirts < 2”>
<td><input type="checkbox"></td>
</div>
<div v-else>
<td><input type="text"> of {{ props.item.shirts }}</td>
</div>
</div>
Any help would be greatly appreciated
reduce your if clause
<td v-for=”shirt in shirts”><input type="checkbox"></td>
<td v-else><input type="text"> of {{ props.item.shirts }}</td>
vue docs - Conditional Rendering
or you can use dynamic components, like so:
<template>
<td>
<component :is="component" :data="passthroughdata" />
</td>
</template>
//...
props: ["value", "passthroughdata"],
data() {
return {
component: {}
},
},
watch: {
value:{
handler: async function(){
try{
await import(`./components/${valueBasedComponent}/index.vue`)
this.component = () => import(`./${valueBasedComponent}/index.vue`)
} catch() {
this.component = () => import(`./${someDefaultComponent}/index.vue`)
}
},
// immediate: true
}
}
vue docs - Dynamic & Async Components

Checking checkbox programmatically doesn't render the change when using nested arrays with objects

сеI have an array with categories. Each category has children.
When a parent is checked/unchecked, its children must be checked/unchecked as well. If all children are checked, the parent must be checked as well.
Vue updates the fields as expected, but doesn't re-render. I cannot understand why.
Here is my code:
<template>
<div class="card">
<div class="card-body">
<ul class="list-tree">
<li v-for="category in categories" :key="category.id" v-show="category.show">
<div class="custom-control custom-checkbox">
<input :id="'category-' + category.id"
v-model="category.checked"
#change="checkParent(category)"
type="checkbox"
class="custom-control-input" />
<label class="custom-control-label"
:for="'category-' + category.id">
{{ category.name }}
</label>
</div>
<ul>
<li v-for="child in category.children" :key="child.id" v-show="child.show">
<div class="custom-control custom-checkbox">
<input :id="'category-' + child.id"
v-model="child.checked"
#change="checkChild(child, category)"
type="checkbox"
class="custom-control-input" />
<label class="custom-control-label" :for="'category-' + child.id">
{{ child.name }}
<small class="counter">({{ child.products_count }})</small>
</label>
</div>
</li>
</ul>
</li>
</ul>
</div>
</div>
</template>
export default {
data () {
return {
categories: [],
listItemTemplate: { show: true, markedText: null, checked: false }
}
},
methods: {
checkParent (category) {
category.children.forEach(child => {
child.checked = category.checked
})
},
initializeCategories () {
this.categories = []
this.originalCategories.forEach(originalCategory => {
var parent = this.copyObject(originalCategory)
this.categories.push(parent)
parent.children.forEach (child => {
child = this.copyObject(child)
})
})
},
copyObject (category) {
return Object.assign(category, {...this.listItemTemplate})
}
},
computed: {
...mapState({
originalCategories: state => state.categories,
})
},
mounted () {
this.initializeCategories()
}
}
You need to expand your scope, since you are changing it only within checkParent() method, variables that you are making changes to will not have an effect onto components variables.
Use the index instead of value in categories iteration to find correct category, and then apply changes in scope of whole component:
<li v-for="(category, categoryIndex) in categories" :key="category.id" v-show="category.show">
<div class="custom-control custom-checkbox">
<input :id="'category-' + category.id"
v-model="category.checked"
#change="checkParent(categoryIndex)"
type="checkbox"
class="custom-control-input" />
<label class="custom-control-label"
:for="'category-' + category.id">
{{ category.name }}
</label>
</div> <!-- the rest of the code ... -->
And then in component's method:
methods: {
checkParent (categoryIndex) {
let categoryChecked = this.categories[categoryIndex];
this.categories[categoryIndex].children.forEach((child, childIndex) => {
this.categories[categoryIndex].children[childIndex].checked = categoryChecked;
})
},
I fixed it. The problem wasn't related to the checkboxes at all. The problem was related to the way I've created the categories array.
When I initialize the component, I copy the array from vuex and add new properties (like checked) in order to check the children when the parent is checked. I didn't follow the rules for adding new fields, that's why the children wasn't reactive and didn't get checked when the parent was checked.
Thanks a lot for your effort to help me!

How to edit particular row in a table by popping a form modal in vuejs?

I am using a modal form to add new details to the row of a table. After adding details, I’m just adding edit and delete buttons at the end of it. Now here delete button is working fine. How to edit a row of a table by popping replicate of form modal by clicking “edit” button in a row.
Here’s my code:
<div class="layout-padding">
<div
class="item item-link"
v-for="modal in types"
#click="$refs[modal.ref].open()"
>
<i class="item-primary">add</i>
<div class="item-content has-secondary">
<div>{{modal.label}}</div>
</div>
</div>
</div>
<q-modal ref="maximizedModal" class="minimized" :content-css="{padding: '50px'}">
<div class="main">
<label>Enter Your Name:</label>
<input id="name" name="name" type="text" placeholder="Enter your Name" v-model="YourName">
<br>
<label>I am:</label>
<input type="radio" id="Male" value="male" v-model="picked">
Male
<input type="radio" id="Female" value="female" v-model="picked">
Female
<br>
<div class="button">
<button class="red" #click="$refs.maximizedModal.close()">Close</button>
<button class="blue" v-on:click="sub" #click="$refs.maximizedModal.close()">Submit</button>
</div>
</div>
</q-modal>
<table>
<thead>
<th>Name</th>
<th>Gender</th> </thead>
<tbody class="result">
<tr v-for="(h, index) in final">
<td v-for="(value, key, index) in h">
{{ value }}
</td>
<td><button id="edit" class="green edit" v-for="modal in types"
#click="ed(h, index);$refs[modal.ref].open()" type="submit">EDIT</button></td>
<td><button id="delete" class="red delete" v-on:click="del(index)" type="submit">Delete</button></td>
</tr>
</tbody>
</table>
And my script is:
export default {
data () {
return {YourName: '',
details: [],
final: [],
types: [
{
label: 'Add Details',
ref: 'maximizedModal'
}
],
position: 'bottom'
}
},
methods: {
sub: function () {
this.details = {
'name': this.YourName,
'gender': this.picked,
}
this.ed()
if (index === '[object MouseEvent]') {
this.final.push(this.details)
}
if (index > -1) {
this.final.splice(index, 1, this.details)
}
else {
alert('else')
alert(JSON.stringify(this.details))
this.final.push(this.details)
}
},
del: function (index) {
this.$delete(this.final, index)
},
ed: function (details, index) {
return index
}
}
}
If edit button is clicked, the same row should be edited. I don’t know how to proceed further. Please, guide me.
Using the 'splice' can able to modify the given array of object.
Can simply include this inside an 'if' loop:
this.final.splice(this.indi, 1, this.details)

Vue 2 - update value of the array element after click event

Is it possible, when I fire updateCountry method (defined in country-list component), to update the value (if successful call) of that array element, so that the button will dynamically change text based on country.show value?
This is the Vue code I have at the moment:
Vue.component('country-list', {
template: `
<tbody>
<tr is="country" v-for="(country, index) in countries.data">
<td>
<input
type="text"
name="name"
class="form-control country-name"
:value="country.name"
>
</td>
<td>
<select name="show" class="form-control country-show" :value="country.show">
<option value="0">No</option>
<option value="1">Yes</option>
</select>
</td>
<td>
<input
type="text"
name="order"
class="form-control country-order"
:value="country.order"
>
</td>
<td>
<button class="btn btn-primary">
{{ country.show ? "Hide" : "Show" }}
</button>
<button class="btn btn-success"
#click="updateCountry"
:data-id="country.id">Update</button>
</td>
</tr>
</tbody>
`,
props: ['countries'],
methods: {
updateCountry(event) {
let self = this;
let countryID = event.target.dataset.id;
let parent = event.target.closest('.parent');
let countryName = parent.getElementsByClassName('country-name')[0].value;
let countryOrder = parent.getElementsByClassName('country-order')[0].value;
let countryShow = parent.getElementsByClassName('country-show')[0].value;
axios.post('/country/insert', {
id: countryID,
name: countryName,
order: countryOrder,
show: countryShow
})
.then(function (response) {
console.log(self);
})
.catch(function (error) {
console.log(error);
});
}
}
});
Vue.component('country', {
template: `<tr class=parent><slot></slot></tr>`
});
Vue.component('pagination-list', {
template: `
<tfoot>
<tr align="center">
<nav aria-label="Page navigation">
<ul class="pagination">
<li :class="countries.current_page == 1 ? 'disabled' : ''">
<a
:class="countries.current_page == 1 ? 'disabled' : ''"
:href="countries.current_page == 1 ? '#' : countries.prev_page_url"
#click.prevent="pagination(countries.current_page - 1)"
aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li v-for="i in countries.last_page"
:class="countries.current_page == i ? 'active' : ''"
>
<a
:href="countries.current_page == i ? '#' : '/admin/countries?page='+i"
#click.prevent="pagination(i)"
>{{i}}</a>
</li>
<li>
<a
:href="countries.next_page_url"
#click.prevent="pagination(countries.current_page + 1)"
aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</tr>
</tfoot>
`,
props: ['countries'],
methods: {
pagination(page) {
this.$parent.getCountries(page);
}
}
});
let App = new Vue({
el: '#app-container',
data: {
countries: []
},
created() {
this.getCountries()
},
methods: {
getCountries(page) {
let self = this;
let getParam = page ? '?page=' + page : '';
axios.get('/admin/countries' + getParam)
.then(function (response) {
self.countries = response.data;
})
.catch(function (error) {
console.log(error);
});
},
filterCountries(event) {
let name = event.target.value;
let self = this;
if(name.length > 2) {
axios.get('/country/search', {
params: {
name: name
}
})
.then(function (response) {
self.countries = response.data;
console.log(self.countries);
})
.catch(function (error) {
console.log(error);
});
}
if((event.keyCode === 8 && name.length === 2) || !name.length){
this.getCountries();
}
}
}
})
This code would be much more Vue like if you used v-model and it would cut down on some of the things you are having to do. For example if you update your country-list template like this:
<tbody>
<tr is="country" v-for="(country, index) in countries.data" :key="country">
<td>
<input
type="text"
name="name"
class="form-control country-name"
v-model="country.name"
>
</td>
<td>
<select name="show" class="form-control country-show" v-model="country.show">
<option value="0">No</option>
<option value="1">Yes</option>
</select>
</td>
<td>
<input
type="text"
name="order"
class="form-control country-order"
v-model="country.order"
>
</td>
<td>
<button class="btn btn-primary">
{{ country.show ? "Hide" : "Show" }}
</button>
<button class="btn btn-success"
#click="updateCountry(country)"
:data-id="country.id">Update</button>
</td>
</tr>
</tbody>
Then your updateCountry method could just be this
updateCountry(country) {
axios.post('/country/insert', country)
.catch(err => //do something on error)
}
Because using v-model, all the values are already updated locally, and you are just posting the values to the server. Since you are passing the actual country to the updateCountry method, there is no need to get the values from the inputs.
Note also, I added :key="country" to your v-for because a key is required when you iterate a component. If you have a country.id that would be even better as the key. But, again, I don't understand why you need the country component at all. It's completely unnecessary at this point.