How to map specializations array with JSON Vue/vuex - vue.js

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>

Related

Show data from api to a table in vue js

How do i display data from the database. I have fetched data as an object when displaying it works well but when i convert the data to array it does not display the content of the table.
Am also trying to implement the routing such that when one clicks the button on the Actions column it redirect to another page that has a specific user details.
Am getting the data from the API and in the network fetch xhr am able to see the data.
This is the vue template where i want to display the data received by the API I guess am going wrong somewhere
<v-data-table hide-actions flat :headers="headers" :="doctors">
<template v-slot:items="props">
<td>{{ props.index + 1 }}</td>
<td>{{ props.item.full_name }}</td>
<td>{{ props.item.email }}</td>
<td>{{ props.item.username }}</td>
<td>{{ props.item.username }}</td>
<td>
<v-btn outline small color="indigo" #click="view(props.item)">
<i class="fa fa-eye"></i> make payment
</v-btn>
</td>
</template>
<template v-slot:no-results>
<h6 class="grey--text">No data available</h6>
</template>
</v-data-table>
</div>
</template>
<script>
import {mapActions, mapGetters} from "vuex";
export default {
data() {
return {
page: 1,
dateFormat: "DD MMM, YYYY",
selected: null,
dialog: false,
loading: false,
saveLoader: false,
headers: [
{text: "#", value: ""},
{text: "name", value: "name"},
{text: "email", value: "email"},
{text: "role", value: "role"},
{text: "updated_at", value: "updated_at"},
{text: "Action", value: "action"},
],
};
},
computed: {
...mapGetters({
doctors: "getListDoctors",
}),
},
methods: {
...mapActions({
fetchDoctors: 'setListDoctors'
}),
view(id) {
this.$router.push({ path: `/finance/pay-doctors/${item.id, '_blank'}` });
}
},
mounted() {
this.fetchDoctors();
},
}
</script>
Here is sample of data received from the API
{
"data": [
{
"id": 1,
"name": "TEST JOHN",
"full_name": "Mrs. JOHN",
"mobile": "0700000001",
"email": "TEST#TEST.com",
"active": 0,
"created_at": "Sep 10, 2019 07:01:43 am",
"roles": "testpharm",
"username": "USER1"
},
]
}
This should work fine regarding the doc
<template>
<v-data-table hide-actions flat :headers="headers" :items="doctors" :items-per-page="5">
<template v-slot:body="{ items }">
<tbody>
<tr v-for="item in items" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.full_name }}</td>
<td>{{ item.mobile }}</td>
<td>{{ item.email }}</td>
<td>{{ item.username }}</td>
</tr>
</tbody>
</template>
<template v-slot:no-results>
<h6 class="grey--text">No data available</h6>
</template>
</v-data-table>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
export default {
data() {
return {
headers: [
{ text: "#", value: "" },
{ text: "name", value: "name" },
{ text: "email", value: "email" },
{ text: "role", value: "role" },
{ text: "updated_at", value: "updated_at" },
{ text: "Action", value: "action" },
],
};
},
computed: {
...mapGetters({
doctors: "getListDoctors",
}),
},
async mounted() {
await this.fetchDoctors();
},
}
</script>
Use your Vue devtools to be sure to pass data.data to the final doctors Vuex getter (regarding your JSON example).
Vue devtools will help you see what state you currently have + the usage of the network tab can also help quite a lot.

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

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"
},]

[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>