Need to Access Item Index in Bootstrap Vue Table when doing a loop - vuejs2

I have a Bootstrap-Vue parent b-table running for loop on bids. Each bid also has items.
Initially it shows each bid on each row.
When that row is clicked it shows a child table that has every bid items.
So a child table inside <template slot="hidden-details> tries to
loop through bid.items to display those items for each bid.
Since I am unable to get earlier bid loop indexes I cannot run this
nested loop unless I manually set it like: :items="bid[0].items
Script
new Vue({
el: '#app',
data:{
bids: [{
id: "Bid Number One",
item: [{
item_number: 11,
item_text: "Item 1 Comment 1",
code: "1",
},
{
item_number: 22,
item_text: "Item 1 Comment 2",
code: "2",
}
]
},
{
id: "Bid Number Two",
item: [{
item_Number: 33,
item_text: "Item 2 Comment 3",
code: "3",
},
{
item_Number: 44,
item_text: "Item 2 Comment 4",
code: "4",
}
]
}
],
bidHeaderFields:[
{key:"id", label: "Header"},
],
bidIndex: 0,
},
methods: {
showBidItems(bids, index) { // Here I receive row index from #row-clicked
this.bids[index]._showDetails = !this.bids[index]._showDetails;
this.bidIndex = index; // I change the value of bidIndex to row-clicked index
},
}
})
This JSFiddle has the table code.

The complete row's item data is available in the scoped details slot. If the bid details are a sub-array of the item, you can just pass that array to the child b-table:
<b-table :items="bids" :fields="..." ... >
<template slot="row-details" slot-scope="{ item }"> // item === 'bid' row data
// item.item === the rows 'item' array
<b-table :items="item.item" :fields="..."></b-table>
</template>
</b-table>

Related

How to do cell editing in vue-good-table?

I am trying to create a table where the data can be edited directly in the cells. However, I don't understand how to do this.
I am using Vue.js, Vuexy and vue-good-table 2.21.1. The data is contained in the mediaPlanData variable and is reactive. The table is successfully populating with data and can be modified. However, when I change the data in the cells, this variable does not change. I did not find in the documentation vue-good-table how to do this. Please tell me how can I achieve the desired result?
<vue-good-table
:columns="columns"
:rows="mediaPlanData"
:select-options="{
enabled: true,
selectOnCheckboxOnly: true, // only select when checkbox is clicked instead of the row
selectionInfoClass: 'custom-class',
selectionText: 'rows selected',
clearSelectionText: 'clear',
disableSelectInfo: true, // disable the select info panel on top
selectAllByGroup: true, // when used in combination with a grouped table, add a checkbox in the header row to check/uncheck the entire group
}"
#on-selected-rows-change="onRowClick"
>
<template slot="table-row" slot-scope="props">
<span>
<b-form-input v-model="props.row[props.column.field]"
type="text"
></b-form-input>
</span>
</template>
</vue-good-table>
data() {
return {
pageLength: 10,
columns: [
{
label: 'Channel Code',
field: 'channelCode',
},
{
label: 'Product',
field: 'product',
}
],
mediaPlanData: [] //[ {"channelCode": "P230", "product": "Test"}, {"channelCode": "P230", "product": "Test4"}, {"channelCode": "P230", "product": "Test45"}, {"channelCode": "Р230", "product": "Test2"}]
}
}
methods: {
onRowClick(params) {
console.log('onRowClick' + JSON.stringify(params))
this.$toast({
component: ToastificationContent,
props: {
title: `Hello user! You have clicked on row ${params.selectedRows.product}`,
icon: 'UserIcon',
variant: 'success',
},
})
}
}
I have solved this problem.
Added to b-form-input
#change="changeCell(props.row[props.column.field], props.column.field, props.row.originalIndex)"
And adden method
changeCell(changedData, column, row) {
this.mediaPlanData[row][column] = changedData
},

v-model full array inrow but not just value of row

I want to display an array in a table. In one column, I use a checkbox that is either checked or unchecked, depending on the value of the v-model which should be the value of array.selected for each element.
However, the v-model contain all array.selected elements of the whole array but not just the one of the current row.
Does anybody know how to introduce some row check to only display the array.selected of the current row ?
The HTML part:
<b-table :items="rooms" :fields="roomsHeader">
<template v-slot:cell(selected)="row" >
<b-checkbox v-for="(item, index) in rooms" v-model="item.selected" :key="index"></b-checkbox>
</template>
</b-table>
The data:
data () {
return {
rooms: [
{
"room": "A",
"capacity": 80,
"selected": false,
},
{
"room": "B",
"capacity": 140,
"selected": true,
},
{
"room": "C",
"capacity": 100,
"selected": true,
}
],
roomsHeader: [
{
key: 'room',
label: 'Raum'
},
{
key: 'capacity',
label: 'Kapazität'
},
{
key: 'selected',
label: 'Verfügbar'
}
],
}
},
Output screenshot (yellow the expected)
Content of slot v-slot:cell(selected)="row" is evaluated for each row of your table, so like this it will render one checkbox for every row in your table in each row !
You don't need v-for in that slot, just use <b-checkbox v-model="row.item.selected" />
Think about that scoped slot as a callback - every time b-table is rendering selected column in a current row, it will call your cell(selected) slot to get a content passing a current row as an argument...
Docs

return unique values from array in vuejs

I am using Vuejs and Vuex to return an array of item objects. The items can be submitted to the database with the same name multiple times. I need to create a list of unique item names in an array in Vuejs.
from map getters below all Items returns an array of objects that looks like
[
{"name": "item one", "number": "001", "size": "4000kb"}
{"name": "item two", "number": "002", "size": "5000kb"}
{"name": "item three", "number": "003", "size": "6000kb"}
]
methods: {
...mapActions(["fetchItems"])
},
computed: {
...mapGetters(["allItems"]),
itemNames: function() {
return [...new Set(this.allItems.name)]
}
},
created() {
this.fetchItems(),
this.itemNames()
},
In computed properties itemNames if I take off the .name [..new Set(this.allItems)]
the array returns the complete objects - how can I just pull the name out to a list?
The v-for does not return the array
<v-list-item v-for="(itemName, index ) in itemNames" :key="index">
<v-list-item-content> {{ itemName }}</v-list-item-content>
</v-list-item>
Thanks for any help.
I solved this one in computed properties by mapping the items to a new array like so
itemNames: function() {
return [...new Set(this.allItems.map(x => x.item.Name))]
}

VueJS - Auto create a A-Z letters list from the data

Is it possible to create a A-Z letters list (like this) from the data from a API and Vue to be able to determine if a property in a data contains a name that starts with what letter. If the data doesn't contain a specific letter name then remove/disable the href attribute from the letter anchor tag.
In the linked example, letters K, X and Z are missing coz they don't have the data
JSON
[
{
"id": 77,
"link": "http://my-site/cosmoquotes/authors/anonymous/",
"name": "Anonymous",
"slug": "anonymous"
},
{
"id": 72,
"link": "http://my-site/authors/ferdinand-marcos/",
"name": "Ferdinand Marcos",
"slug": "ferdinand-marcos"
},
{
"id": 75,
"link": "http://my-site/authors/john-f-kennedy/",
"name": "John F. Kennedy",
"slug": "john-f-kennedy"
},
{
"id": 67,
"link": "http://my-site/authors/john-maxwell/",
"name": "John Maxwell",
"slug": "john-maxwell"
}
]
Component
export default {
data() {
return {
authorsRequest: {
type: 'authors',
params: {
per_page: 100
}
},
}
},
computed: {
authors () {
return this.$store.getters.requestedItems(this.authorsRequest)
},
},
methods: {
getAuthors() {
return this.$store.dispatch('getItems', this.authorsRequest)
},
},
created() {
this.getAuthors()
}
}
So as per the returned data, only the letters 'A', 'F' and 'J' should be clickable/displayed.
I managed to do it like this,
unfortunatly it needs the authors array and the conditionnal function to be outside of the Vue component because I couldn't find how to pass argument to computed values
But since I'm new to vue (didn't even finish reading the introduction) I'm sure there has to be a better solution
EDIT: found the way to have the function in the component with methods, I could then move the data in the component too
let a = new Vue({
el: "#selector",
data: {
authors: [{"id": 77,"link": "http://my-site/cosmoquotes/authors/anonymous/","name": "Anonymous","slug": "anonymous"},{"id": 72,"link": "http://my-site/authors/ferdinand-marcos/","name": "Ferdinand Marcos","slug": "ferdinand-marcos"},{"id": 75,"link": "http://my-site/authors/john-f-kennedy/","name": "John F. Kennedy","slug": "john-f-kennedy"},{"id": 67,"link": "http://my-site/authors/john-maxwell/","name": "John Maxwell","slug": "john-maxwell"}]
},
computed: {
// there have to be a way to get this array without doing it like this but I don't know it ^^
letters() {
let letters = []
for(let i = "A".charCodeAt(0); i <= "Z".charCodeAt(0); i++) {letters.push(String.fromCharCode([i]))}
return letters
}
},
methods: {
// you may add a toUpperCase()/toLowerCase() if you're not sure of the capitalisation of you datas
isALink(letter) {
return this.authors.some(aut => aut.name.startsWith(letter))
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="selector">
<template v-for="letter in letters">
<a v-if="isALink(letter)" :href="letter">{{ letter }}</a>
<a v-else>{{ letter }}</a>
</template>
</div>
you can set the unique name as the id of dom. when you click to letter X, just get the first name start with X , and use getElementById to match the dom and scroll to the dom.

Vuex How to track index and pass it to component?

I have two components which gets data from vuex. First component is a slider with images. When you click on image, second component should display information about each picture. How to track index and pass it to the second component to display correct info
Here is my store:
sideSwiperItems: [
{
id: "001",
smallImage: require("#/assets/jpg/sideMenu-01.jpg"),
data: [
{
height: "100 ft",
widht: "10 ft",
},
],
},
{
id: "002",
smallImage: require("#/assets/jpg/sideMenu-02.jpg"),
data: [
{
height: "200 ft",
widht: "20 ft",
},
],
},
first component
swiper-slide.swiper__thumb-position(
v-for='(item, idx) in this.$store.state.buildingData.sideSwiperItems'
:key='idx'
:class=`'slide-'+(idx+1)`
) {{item.smallImage}}
second component
div(
v-for=`(item, idx) in this.$store.state.buildingData.sideSwiperItems`
:key='idx'
)
p.side__block-month
| {{item.data.height}}
| {{item.data.width}}
You have to store the data you want to pass into the second element.
For example you want to pass the selected index of the first component, then you have to store the selected object into the store:
sideSwiperItems: [
...
],
selectedObject: null
You can easily assign the selected object like this
swiper-slide.swiper__thumb-position(
v-for='(item, idx) in this.$store.state.buildingData.sideSwiperItems'
#click="onClick"
:key='idx'
:class=`'slide-'+(idx+1)`
) {{item.smallImage}}
methods: {
onClick: (item) => {
this.$store.state.buildingData.selectedObject = item
}
}
If the first and second element is in the same component you can do this without assigning the selected object into Vuex store. Just use the component's data