How Can I get a specific array id from a v-for loop input and pass it into a data store? - vue.js

Here is my code
<template>
<div class="w-full bg-white mt-13">
<div class="flex flex-col mx-12 mt-8">
<div>
<table class="w-full h-auto my-12 border border-collapse table-auto">
<tr class="border">
<th v-for="i in columns" :key="i.name" class="border">
{{ i.title }}
</th>
</tr>
<tr v-for="p in getParticipants" :key="p.id">
<td class="border"></td>
<td class="border">{{ p.fullName }}</td>
<td class="border">{{ p.phone }}</td>
<td class="border">{{ p.participantId }}</td>
<td
v-for="(btn, index) in buttonCheckAtt"
:key="index"
class="border select-attendance"
>
<div>
<button
:unique-key="true"
class="w-full h-16 text-transparent"
:class="[
btn.className,
{ selected: selectedIndex === index },
]"
#click="selectedIndex = index"
>
{{ btn.btnText }}
</button>
</div>
</td>
<td class="w-16 border">
<input
ref="p.participantId"
#change="updateRemarks()"
v-model="note"
/>
</td>
</tr>
<tr></tr>
</table>
</div>
</div>
</div>
</template>
<script>
import Button from "#/components/form/Button.vue";
import { mapGetters } from "vuex";
const columns = [
{
title: "#",
},
{
title: "Name",
},
{
title: "Phone Number",
},
{
title: "ID",
},
{
title: "P",
},
{
title: "A",
},
{
title: "AP",
},
{
title: "L",
},
{
title: "Remarks",
},
];
const data = [];
export default {
components: { Button },
data() {
return {
note: null,
participantId: "abc-1",
};
},
methods: {
updateRemarks() {
let data = {
participants: [
{
participantId: this.participantId,
attendance: {
status: this.status,
note: this.note,
markBy: "organizer-id",
markMethod: "manual",
},
},
],
};
this.$store.dispatch(
"$_studyGroup/addRemarks",
{ participantData: data },
{ root: true }
);
},
},
computed: {
...mapGetters({
getParticipants: "$_studyGroup/getParticipants",
}),
},
mounted() {
this.$store.dispatch("$_studyGroup/getParticipants", {}, { root: true });
},
};
</script>
I want to use this input to this input to make patch request into my getParticipants api however it requires me to pass the participantId into the participants data in order to make patch request and I have no idea how to retrieve that participantId from the v-for loop
<input
ref="p.participantId"
#change="updateRemarks()"
v-model="note"
/>
and down below is what my getParticipants api looks like I want to pass the abc-1 and abc-2 id into the attendance in order to make patch request
getParticipants = [
{
"status": "join",
"createdAt": "2022-09-20T07:30:06.753Z",
"calendarId": "6553c8ea-0139-4802-b5d6-127e44b95412",
"email": "john#gmail.com",
"fullName": "John",
"participantId": "abc-1",
"attendance": {
"participantId" : {{participantId}},
"markBy": "organizer-id",
"markMethod": "manual",
"note": "Because of traffic jam",
"status": "Present"
},
{
"status": "join",
"createdAt": "2022-09-20T07:30:06.753Z",
"calendarId": "6553c8ea-0139-4802-b5d6-127e44b95412",
"email": "chris#gmail.com",
"fullName": "Chris",
"participantId": "abc-2",
"attendance": {
"participantId" : {{participantId}},
"markBy": "organizer-id",
"markMethod": "manual",
"note": "Because of traffic jam",
"status": "Late"
},]

Related

How do I disable a button before any fields have been checked?

Not sure what I am doing wrong here, I am calling the function incorrectly or do the html tag have to specific for the disable to work? I am trying to disable the next button before any fields have been checked, I also only want one checkbox to be selected at a time. I can get the only one checkbox to be selected a time but this stop me from disabling the button cause I can't have the field be inputs.
<div>
<button #click="handleCompanySize(100)">
<CheckIcon v-show="companySize === 100"/>
</button>
<button #click="handleCompanySize(101)">
<CheckIcon v-show="companySize === 101"/>
</button>
</div>
<button :disabled="handleCompanySize" #click="next">
Next
</button>
This is a simple requirement and looks like you overthinked about it. You can use a computed property in the :disabled attribute which returns true/false based on the checkbox selection.
Live Demo :
new Vue({
el: '#app',
data: {
employees: [
{ "id": 1, "text": "0 - 1" },
{ "id": 2, "text": "2 - 10" },
{ "id": 3, "text": "11 - 50" },
{ "id": 4, "text": "50 - 100" },
{ "id": 5, "text": "> 100" }
],
selected: []
},
computed: {
disableBtn() {
return !this.selected.length ? true : false
}
},
methods: {
uniqueCheck(e) {
this.selected = [];
if (e.target.checked) {
this.selected.push(e.target.value);
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h4>Employee Selection</h4>
<table>
<tr v-for="employee in employees">
<td>
<input type="checkbox" v-model="selected" :value="employee.id" #change="uniqueCheck">
</td>
<td>{{ employee.text }}</td>
</tr>
</table>
<button :disabled="disableBtn">
Next
</button>
</div>

How to map specializations array with JSON Vue/vuex

Hello guys,
// I have one problem with map in Vue.js, how can i map array specializations ? How to display a whole array in 1 div. I would like to make a filter later.
Below it's my JSON file
My JSON file
{
"workers": [
{
"id":1,
"name":"Karolina Water",
"email":"Karolina.water#gmail.com",
"specializations": [
{
"code": "net",
"name": ".NET"
},
{
"code": "react",
"name": "React JS"
}
]
}, {
"id":2,
"name":"Marcelina Wart",
"email":"Marcelina.wart#gmail.com",
"specializations": [
{
"code": "net",
"name": ".NET"
}
]
},
]
}
Below it's my component,
<tr v-for="worker in workers" :key="worker.id">
<th scope="row">{{ worker.id }}</th> //
<td>{{ worker.name }}</td> //
<td>{{worker.specializations[0].name}} </td> //- but i have only 1 value from the array.
<td>go to projects ➡</td>
</tr>
many thanks for your help !
Make a nested v-for.
See it here in action: Vue SFC Playground
<template>
<table border>
<tr v-for="worker in workers" :key="worker.id">
<th scope="row">{{ worker.id }}</th>
<td>{{ worker.name }}</td>
<td>
<div v-for="spec in worker.specializations">
{{spec.code}} - {{spec.name}}
</div>
</td>
</tr>
</table>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
data() {
return {
workers: [
{
id: 1,
name: "Karolina Water",
email: "Karolina.water#gmail.com",
specializations: [
{
code: "net",
name: ".NET",
},
{
code: "react",
name: "React JS",
},
],
},
{
id: 2,
name: "Marcelina Wart",
email: "Marcelina.wart#gmail.com",
specializations: [
{
code: "net",
name: ".NET",
},
],
},
],
};
},
});
</script>

[Vue.Draggable]I want to drag only some columns of the table

I want to be able to drag only some columns using this library.
https://sortablejs.github.io/Vue.Draggable/#/table-column-example
What I want to do is have a header with two lines and allow only some columns to be dragged.
However, that is not possible now.
The element specified by handle(item-handle) can be moved, but the result does not change.
The error is output to the console.
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'key' of undefined"
TypeError: Cannot read property 'key' of undefined
Columns that are not subject to dragging are displayed so that their positions can be changed by dragging.
How can I eliminate these problems?
Version
"vue": "^2.6.11"
"vuedraggable": "^2.24.0"
"bootstrap-vue": "^2.16.0"
Vue Source
<template>
<div class="row">
<div class="col-8">
<h3>Draggable table</h3>
<table class="table table-striped">
<thead class="thead-dark">
<draggable
v-model="headers"
element="tr"
:options="{ handle: '.item-handle', group:'data-group' }"
#end="draggableEnd"
>
<th>Check</th>
<th v-for="header in headers" :key="header.key" scope="col" id="data-group">
<span class="item-handle">::</span>
{{ header.name }}
</th>
<th>End</th>
</draggable>
<tr>
<th>none</th>
<th v-for="header2 in headersSecond" :key="header2.key" scope="col">
<input type="text" :name="header2.key" :placeholder="header2.name" />
</th>
<th>none</th>
</tr>
</thead>
<tbody>
<tr v-for="item in list" :key="item.name">
<td>
<input type="checkbox" />
</td>
<td v-for="header in headers" :key="header.key">{{ item[header.key] }}</td>
<td>END</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import draggable from "vuedraggable";
export default {
name: "Drag",
methods: {
draggableEnd(event) {
console.log(event);
this.headers.forEach((data, index) => {
console.log("index:" + index, data.key, data.name);
});
},
},
components: {
draggable,
},
data() {
return {
headers: [
{ key: "id", name: "ID" },
{ key: "name", name: "NAME" },
{ key: "sport", name: "SPORTS" },
],
headersSecond: [
{ key: "id", name: "ID2" },
{ key: "name", name: "NAME2" },
{ key: "sport", name: "SPORTS2" },
],
list: [
{ id: 1, name: "Abby", sport: "basket" },
{ id: 2, name: "Brooke", sport: "foot" },
{ id: 3, name: "Courtenay", sport: "volley" },
{ id: 4, name: "David", sport: "rugby" },
],
dragging: false,
};
},
};
</script>
You can try to set pointer-events:none for the th elements that are not supposed to be draggable.

V-model does not get updated after checkbox clicked

Any idea how to resolve this problem:
in this example, the author uses vue 2.3.2 which works perfect,
new Vue({
el: '#app',
data: {
users: [{
"id": "Shad",
"name": "Shad"
},
{
"id": "Duane",
"name": "Duane"
},
{
"id": "Myah",
"name": "Myah"
},
{
"id": "Kamron",
"name": "Kamron"
},
{
"id": "Brendon",
"name": "Brendon"
}
],
selected: [],
allSelected: false,
userIds: []
},
methods: {
selectAll: function() {
this.userIds = [];
if (this.allSelected) {
for (user in this.users) {
this.userIds.push(this.users[user].id.toString());
}
}
},
select: function() {
this.allSelected = false;
}
}
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="app">
<h4>User</h4>
<div>
<table>
<tr>
<th>Name</th>
<th>Select All<input type="checkbox" #click="selectAll" v-model="allSelected"></th>
</tr>
<tr v-for="user in users">
<td>{{ user.name }}</td>
<td><input type="checkbox" v-model="userIds" #click="select" :value="user.id"></td>
</tr>
</table>
</div>
<span>Selected Ids: {{ userIds }}</span>
</div>
when I switch it to 2.5.16 ( <script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script> ) , the behavior is wierd:
When click the selectAll checkbox, only that checkbox checked, but when I toggle it to uncheck, all the checkboses below get checked
For consistent browser functionality, I can recommended to not use click/change on checkboxes. Instead, bind the checkbox to a value (which you've already done), and then use a watcher on the value. This way, the real value of the checkbox will always accurately represent it's state. So you'd have something like this:
<input type="checkbox" v-model="allSelected">
Vue.component({..., {
data: function() {
return {
allSelected: false,
}
}
},
watch: {
allSelected: function(val){
//Use your source of truth to trigger events!
this.doThingWithRealValue(val);
}
}
});
You're already using your component data value of allSelected as the source of truth, so you should use this source of truth as the real triggering element value, not a click. Whenever the value of allSelected changes, your code will get ran. This solves the problem without the rendering order weirdness.
As pointed out by rob in the comments and in his answer you cannot rely on #click / #input / #change to have the same behaviour in all browsers in regards to their execution order relative to the actual model change.
There is an issue at the VueJS repository with a bit more context: https://github.com/vuejs/vue/issues/6709
The better solution is to watch the model for changes and then react accordingly.
new Vue({
el: '#app',
data: {
users: [{
"id": "Shad",
"name": "Shad"
},
{
"id": "Duane",
"name": "Duane"
},
{
"id": "Myah",
"name": "Myah"
},
{
"id": "Kamron",
"name": "Kamron"
},
{
"id": "Brendon",
"name": "Brendon"
}
],
selected: [],
allSelected: false,
userIds: []
},
methods: {
selectAll: function() {
this.userIds = [];
if (this.allSelected) {
for (user in this.users) {
this.userIds.push(this.users[user].id.toString());
}
}
},
select: function() {
this.allSelected = false;
}
},
watch: {
allSelected: function () {
this.selectAll()
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h4>User</h4>
<div>
<table>
<tr>
<th>Name</th>
<th>Select All<input type="checkbox" v-model="allSelected"></th>
</tr>
<tr v-for="user in users">
<td>{{ user.name }}</td>
<td><input type="checkbox" v-model="userIds" #click="select" :value="user.id"></td>
</tr>
</table>
</div>
<span>Selected Ids: {{ userIds }}</span>
</div>

Vue Recommendation for Storing Variable Inside Templated Looping Structure?

Given the following code
// Assuming records is an array of objects
// Record is of the form {"fname": "John", "lname": "Smith", "dob": 1499994497871}
// Field mapper is
[
{
"name": "fname",
"label": "First Name",
"render": function(val){ return val}
},
{
"name": "lname",
"label": "Last Name",
"render": function(val){ return val}
},
{
"name": "dob",
"label": "Date of Birth",
"render": function(val){ return new Date(val).toDateString()}
}
]
// Assume findField returns the mapper instance for a given record in the records array
<div v-for="record in records">
<div>
<table class="table">
<tbody>
<tr v-for="(value, key) in record">
<th v-if="findField(key)">{{ findField(key).label }}</th>
<td v-if="findField(key)">{{ findField(key).render(value) }}</td>
</tr>
</tbody>
</table>
</div>
</div>
I'm making four calls to findField().
What's the Vue recommendation to storing variables inside looping structure?
In other words, after the first tr, something like:
let localInstance = findField(key)
One way is to use a component.
Vue.component("row", {
props:["map", "value"],
template:`
<tr>
<th v-if="map">{{ map.label }}</th>
<td v-if="map">{{ map.render(value) }}</td>
</tr>
`
})
And modify your template to use it.
<tr is="row" v-for="(value, key) in record"
:map="findField(key)"
:value="value">
</tr>
console.clear()
const fieldMap = [{
"name": "fname",
"label": "First Name",
"render": function(val) {
return val
}
},
{
"name": "lname",
"label": "Last Name",
"render": function(val) {
return val
}
},
{
"name": "dob",
"label": "Date of Birth",
"render": function(val) {
return new Date(val).toDateString()
}
}
]
const records = [{
"fname": "John",
"lname": "Smith",
"dob": 1499994497871
},
{
"fname": "John",
"lname": "Smith",
"dob": 1499994497871
},
{
"fname": "John",
"lname": "Smith",
"dob": 1499994497871
}
]
Vue.component("row", {
props: ["map", "value"],
template: `
<tr>
<th v-if="map">{{ map.label }}</th>
<td v-if="map">{{ map.render(value) }}</td>
</tr>
`
})
new Vue({
el: "#app",
data: {
records
},
methods: {
findField(key) {
return fieldMap.find(m => m.name === key)
}
},
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<div v-for="record in records">
<div>
<table class="table">
<tbody>
<tr is="row" v-for="(value, key) in record" :map="findField(key)" :value="value">
</tr>
</tbody>
</table>
</div>
</div>
</div>