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

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>

Related

Vue 3 how to remove item from array with filter method?

I want to delete specific item from array with filter method. This is how I add new items to array first:
addNewInvoiceItem() {
this.invoiceItemList.push({
id: Date.now(),
itemName: "",
qty: "",
price: 0,
total: 0,
});
},
Template:
<tr
class="table-items flex"
v-for="item in invoiceItemList"
:key="item.id"
>
<td class="item-name">
<input type="text" v-model="item.itemName" />
</td>
<td class="qty"><input type="text" v-model="item.qty" /></td>
<td class="price">
<input type="text" v-model="item.price" />
</td>
<td class="total flex">
{{ (item.total = item.qty * item.price) }}
</td>
<img
src="#/assets/trash-bin.png"
alt="Delete icon"
#click="deleteInvoiceItem(item.id)"
/>
</tr>
And delete method:
deleteInvoiceItem(id) {
this.invoiceItemList = this.invoiceItemList.filter(
(item) => item.id == !id
);
},
There is second delete method, which is working correctly:
deleteInvoiceItem(item) {
this.invoiceItemList.splice(this.invoiceItemList.indexOf(item), 1);
},
But I want to know, why method with filter is not working?
I found solution. There was a mistake in this method:
deleteInvoiceItem(id) {
this.invoiceItemList = this.invoiceItemList.filter(
(item) => item.id == !id
);
},
it should be:
(item) => item.id !== id

Vue js text field value becomes empty when new row is appended

I have an dynamic table where new row can be added as well as removed ,im populating text field values based on Onchange event of Select option ,For the first row i can populate the values but once i append a new row ,the old values of the past row gets removed.
export default {
data() {
return {
//editmode:true,
templatename: '',
type: 'margin',
games: {},
fields: {},
subs: {},
rows: []
// errors: {},
}
},
components: {
Datepicker,
MinusIcon,
PlusIcon,
XCircleIcon
},
methods: {
getexcercisesrepcount: function(event, dynamicrow) {
var currentrow = dynamicrow;
var excercise = event;
const URL = baseurl + `api/getexcercisesrepcount/`
axios({
method: 'post',
url: URL,
headers: {
'Content-Type': 'application/json',
},
data: excercise
})
.then(response => {
var rowsdata = response.data;
var i = 0;
for (i; i < rowsdata.length; i++) {
var exid = rowsdata[i].id;
if (dynamicrow != null) {
var crval = $("#set" + currentrow).val(exid); //This is where i set value for the table row
}
}
});
},
addRow: function() {
var elem = document.createElement('tr');
this.rows.push({
workoutname: "",
workoutcategory: "",
set: "",
rep: "",
resttime: "",
tempo: ""
})
},
removeElement: function(index) {
this.rows.splice(index, 1);
},
},
}
This is my table where i have added text fields as well as select option
<table class="border-collapse" style="width: 100%;" id="mytable">
<tr>
<th class="p-2 border border-solid d-theme-border-grey-light" width="25%">Workout</th>
<th class="p-2 border border-solid d-theme-border-grey-light text-center" width="25%">Excercise</th>
<th class="p-2 border border-solid d-theme-border-grey-light">Set</th>
<th class="p-2 border border-solid d-theme-border-grey-light">Rep</th>
<th class="p-2 border border-solid d-theme-border-grey-light">Rest time</th>
<th class="p-2 border border-solid d-theme-border-grey-light">Tempo</th>
</tr>
<tr :id="'tr'+index" v-for="(row, index) in rows" :key="index">
<td class="p-2 border border-solid d-theme-border-grey-light">
<vs-select v-model="row.workoutname" v-validate="'required'" #change="getworkoutexcercises($event)" name="workoutname[]" :id="'wrkout'+index" class="w-full select-large" autocomplete>
<vs-select-item :key="game.id" :value="game.id" :text="game.workoutname" v-for="game in games" class="w-full" />
</vs-select>
</td>
<td class="p-2 border border-solid d-theme-border-grey-light">
<vs-select v-model="row.workoutcategory" v-validate="'required'" #change="getexcercisesrepcount($event,index)" name="workoutcategory[]" :id="'wrkcat'+index" class="w-full select-large" autocomplete>
<vs-select-item :key="game.id" :value="game.id" :text="game.subexname" v-for="game in subs" class="w-full" />
</vs-select>
</td>
<td class="p-2 border border-solid d-theme-border-grey-light"><input type="text" class="form-control" v-validate="'required'" v-model="row.exsets" name="set[]" :id="'set'+index" value="" /></td>
<td class="p-2 border border-solid d-theme-border-grey-light"><input type="text" class="form-control" v-validate="'required'" v-model="row.rep" name="rep[]" :id="'rep'+index" /></td>
<td class="p-2 border border-solid d-theme-border-grey-light"><input type="text" class="form-control" v-validate="'required'" v-model="row.resttime" name="resttime[]" :id="'resttime'+index" /></td>
<td class="p-2 border border-solid d-theme-border-grey-light"><input type="text" class="form-control" v-validate="'required'" v-model="row.tempo" name="tempo[]" :id="'tempo'+index" /></td>
<a v-on:click="removeElement(index);" style="cursor: pointer">
<x-circle-icon size="1.5x" class="custom-class" style="margin-left: 5px;margin-top: 15px;color:red;"></x-circle-icon>
</a>
</tr>
</table>
<button style="margin-top:15px;margin-bottom:15px;" class="button btn-primary" #click="addRow">Add row</button>
You shouldn't create HTML in vue components, rather you should use the virtual DOM and things like v-for to create the HTML you want instead of doing it with createElement.
And in the case that you do use the v-dom and you run into a similar issue, you want to use key to uniquely tag each node so Vue knows what belongs with what.
But main point is that you shouldn't be manipulating the DOM directly from Vue functions ever. It's performance taxing and you lose the benefit of reactivity, as you have noticed.
Look into v-for and vue's documentation on rendering lists for what you are trying to achieve: https://v2.vuejs.org/v2/guide/list.html

Vuejs2- Avoid repetition of select field options using vee-validate

Iam using vee-validate plugin for validation. In my form, there is a select field in the table. Rows will be added dynamically in the table. I don't want to select the same select(Description column) option again and again Image. Hence I want to throw an error like "Selected description already exists in a table" this using vee-validate. Kindly help me to solve this.
Here is my code:
<template>
<div>
<b-card>
<div class="panel-body" id="app">
<table class="table table-hover">
<thead>
<tr>
<th style="width: 20px;">No.</th>
<th style="width: 330px;">Description</th>
<th style="width: 130px;" class="text-right">Charges</th>
<th style="width: 130px;">Total</th>
<th style="width: 130px;"></th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows" :key="row.qty">
<td>
{{ index +1 }}
</td>
<td>
<select class="form-control" v-model="row.billChgDesc" v-validate="'required|check'" :name="'billChgDesc' + index" data-vv-as="Description" #change="checkRepetation">
<option v-for="option in billChgDescOpt" v-bind:value="option.value"
:key="option.value"> {{ option.text }}
</option>
</select>
<span v-show=" errors.has('billChgDesc' + index)" class="is-danger">{{ errors.first('billChgDesc' + index) }}</span>
</td>
<td>
<input class="form-control text-right" type="text" v-model="row.charges" data-type="currency" v-validate="'required'" :name="'charges' + index" data-vv-as="Charges" >
<span v-show=" errors.has('charges' + index)" class="is-danger">{{ errors.first('charges' + index) }}</span>
<td>
<input class="form-control text-right" :value="row.qty * row.charges" number readonly />
<input type="hidden" :value="row.qty * row.charges * row.tax / 100" number/>
</td>
<td>
<button class="btn btn-primary btn-sm" #click="addRow(index)"><i class="fa fa-plus"></i></button>
<button class="btn btn-danger btn-sm" #click="removeRow(index)"><i class="fa fa-minus"></i></button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-right">TOTAL</td>
<td colspan="1" class="text-right"><input class="form-control text-right" v-model="delivery" number/></td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</b-card>
</div>
</template>
<script>
import Vue from 'vue'
import accounting from 'accounting'
export default {
data: function () {
return {
billChgDescOpt: [
{ value: '', text: 'Select' },
{ value: 'M', text: 'Maintenance Fee'},
{ value: 'W', text: 'Water Charges'},
{ value: 'P', text: 'Penalty Fee'},
],
rows: [
{qty: 5, billChgDesc: '', charges: 55.20, tax: 10},
{qty: 19, billChgDesc: '', charges: 1255.20, tax: 20},
],
grandtotal: 0,
delivery: 40,
selectArr:[]
}
},
methods: {
addRow: function (index) {
try {
this.rows.splice(index + 1, 0, {});
} catch(e)
{
console.log(e);
}
},
removeRow: function (index) {
this.rows.splice(index, 1);
},
checkRepetation:function(){
this.$validator.extend('check', {
getMessage: field => '* Slected ' + field + ' already exists',
validate: function(value){
selectArr.push(value);
}
})
}
}
}
</script>
<style lang="scss" scoped>
.is-danger{
color: RED;
}
</style>
Thanks in advance.
You're on the right track, but a couple changes need to be made. When you call this.$validator.extend, that only needs to be done once - when your component is created. It attaches the check method to the validator, so then every time you have the attribute v-validate="'required|check'" in your HTML, it will run that check method.
In your check validator, you need to answer the question "is this value already selected". The answer is to go through the this.rows and see if any of them have the same billChgDesc property. Because this is in Vue, by the time the validator gets run, the row in question already does have that value, so you want to check if MORE than one row have that value. So, something like this:
mounted() {
var self = this;
this.$validator.extend('check', {
getMessage: field => '* Selected ' + field + ' already exists',
validate: function(value){
return (self.rows.filter(function(v){
return v.billChgDesc == value;
}).length <= 1);
}
});
}
This validator returns true if only one item has the given value. I'm using the built-in filter method of Array (see docs).
You can see an example of this all working here: https://jsfiddle.net/ryleyb/f9q50wx4/1/

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.