V-data-table template v-slot syntax - vue.js

Hello and thank you for reading my question.
I have an array of objects looking like this :
[{
id=1,
products= [a,b,c]
},
{
id=2,
products= [d,e,f]
}]
I want to display data in a v-data-table with two columns (id and products) and products items in a nested v-data-table.
So far I found that i should use template and v-slot but I don't find the right way to do it.
<v-data-table
item-key="id"
items={products}
>
<template v-slot="">
<v-data-table> ... </v-data-table>
</template>
</v-data-table>
Instead of the nested table I get
[object Object],[object Object]
How to use template v-slot in tsx files ?
And more generally, how to 'translate' vuetify js to tsx (documentation) ?
Thanks again

you have made a slot which is correct, but you need to tell vuetify which slot you want to access. To access the default <td> slot of all rows you can simply use v-slot:body="{ items }" and pass it the items, so you can create a view per column. (Available slots are listed here Vuetify doc, if you scroll down. Each component has them)
I have made a codepen that you can check out Codepen. As of translating it to tsx, I can't help you, since I haven't worked with it, but if you grasp the concept of how to do it on the vuetify part, you should be good!

Related

Vue.Draggable : Use component inside draggable transition-group

I'm having trouble trying to use the Vue.Draggable library. I would like to use a component inside my draggable while keeping the transition-group. It's working without the transition group but whenever Im adding the transition-group tag with the animation name its not working anymore. My components elements are not showing up and I'm having this error :
TypeError: Cannot set properties of null (setting '__draggable_context')
Here is my code :
<draggable :list="teams" item-key="idteam" tag="transition-group" :component-data="{name:'fade'}>
<template #item="{ element, index }">
<my-team :id="element.idteam" :name="element.teamName" :index="index">
</my-team
></template>
</draggable>
Any idea how could I make all work together ?
Thanks for your help
You need to add :animation="200"; only adding tag="transition-group" is not enough.

How to use `v-if` and `v-for` on the same element?

Hi I'm trying to figure out how to use v-if on a iterated element which also uses v-for. I need to check if the current element has any of a series of classes, which are numbers.
so the classes of each article would be:
<article class="post-item post-guide 12 22 19 30 55">...
this is the HTML that renders all:
<article v-if="showIfHasClass" :class="'post-item post-guide ' + guide.categories.toString().replace(/,/g, ' ')"
v-for="(guide, index) in guides" :key="index">
<header>
<h1 class="post-title">
{{ guide.title.rendered}}
</h1>
</header>
</article>
I have tried with methods that check the class of all elements, that works, but i'm trying to use a clean Vue built-in solution with v-if without success, i'm not able to retrieve the class of this in a successful way.
Should showIfHasClass be a computed property? I have tried with that too... but it seems, I'm missing something along the way.
my data I have to check against is an array:
data:{
guides: [...]
selectedCategories: [1, 22, 33, 100, 30];
}
or maybe it is better to directly loop over the guides and check if they have the selectedCategory or not, then remove the element from the guides data array?
What is more effective?
Besides the option to create an additional filtered computed (effectively eliminating the need to use v-for and v-if on the same element), you also have a template level way of dealing with such edge-cases: the <template> tag.
The <template> tag allows you to use arbitrary template logic without actually rendering an extra element. Just remember that, because it doesn't render any element, you have to place the keys from the v-for on the actual elements, like this:
<template v-for="(guide, index) in guides">
<article v-if="isGuideVisible(guide)"
:key="index"
class="post-item post-guide"
:class="[guide.categories.toString().replace(/,/g, ' ')]">
<header>
<h1 v-text="guide.title.rendered" />
</header>
</article>
</template>
isGuideVisible should be a method returning whether the item is rendered, so you don't have to write that logic inside your markup. One advantage of this method is that you can follow your v-if element with a fallback v-else element, should you want to replace the missing items with fallback content. Just remember to also :key="index" the fallback element as well.
Apart from the above use-case, <template> tags come in handy when rendering additional wrapper elements is not an option (would result in invalid HTML markup) (i.e: table > tr > td relations or ol/ul > li relations).
It's mentioned here as "invisible wrapper", but it doesn't have a dedicated section in the docs.
Side note: since you haven't actually shown what's inside guide.categories, I can't advise on it, but there's probably a cleaner way to deal with it than .toString().replace(). If guide.categories is an array of strings, you could simply go: :class="guide.categories".
I think the most Vue way is to create a computed property with filtered items from selected categories, then use that in v-for loop (the idea is to move the business logic away from template).
computed: {
filteredItems(){
return this.guides.filter(e => this.selectedCategories.includes(e.category))
}
}
Also, as a note, it is not recommended to use v-if and v-for on the same element, as it may interfere with the rendering and ordering of loop elements. If you don't want to add another level of nesting, you can loop on a 'template' element.
<template v-for="item in items">
// Note the key is defined on real element, and not on template
<item-element v-if='condition' :key="item.key"></item-element>
</template>

Vue Js not rendering few components in v-for

I am using v-for for and then showing the components. is rendering fine but inner element is missing. I am unable to understand. You can see two columns are empty but in the console three are 5 components rendered but showing only 3 components. Tell what may be the problem. any help will be appreciated
<div v-for="(counterDate, index) in fulldate_array" >
<template v-if="shift[officer.oid]">
<shiftlayout v-for="(data,shiftKey) in shift[officer.oid][fulldate_array[index]]" :shift_data="data" :shiftKey="shiftKey" :date="singledate_array[index]" :fulldate="fulldate_array[index]" :width="shiftWidth"/>
</template>
</div>

bootstrap-vue toggle expand table row

This seems to remain unanswered so here is another attempt at a solution.
Currently in bootstrap-vue, I am rendering a b-table. I would like to improve this by having the ability to select a row and collapse/expand an extra div/row/etc to show further information.
In the below snippet you will see what I am trying. The problem is that I can't seem to get the expanded data to span the number of columns in the table. I have tried adding <tr><td colspan="6"></td></tr> but it doesn't seem to span like I would expect. Any workarounds for this? Thanks.
<b-table
:items="case.cases"
:fields="tableFields"
head-variant="dark">
<template
slot="meta.status"
slot-scope="data">
<b-badge
v-b-toggle.collapse1
:variant="foobar"
tag="h6">
{{ data.value }}
</b-badge>
</template>
<template
slot="#id"
slot-scope="data">
<span
v-b-toggle.collapse1>
{{ data.value }}
</span>
<b-collapse id="collapse1">
Collapse contents Here
</b-collapse>
</template>
</b-table>`
Sounds like you could use the Row Details slot:
If you would optionally like to display additional record information (such as columns not specified in the fields definition array), you can use the scoped slot row-details
<b-table :items="case.cases" :fields="tableFields" head-variant="dark">
<template slot="meta.status" slot-scope="data">
<b-button #click="data.toggleDetails">
{{ data.value }}
</b-button>
</template>
<template slot="row-details" slot-scope="data">
<b-button #click="data.toggleDetails">
{{ data.detailsShowing ? 'Hide' : 'Show'}} Details }}
</b-button>
<div>
Details for row go here.
data.item contains the row's (item) record data
{{ data.item }}
</div>
</template>
</b-table>
There is a good example in the docs at https://bootstrap-vue.js.org/docs/components/table#row-details-support
I (think) I had the same issue, and I came up with a solution which leverages the filtering functionality of the bootstrap-vue <b-table> to achieve the effect of expanding and collapsing rows.
There's a minimal example in a JSFiddle here:
https://jsfiddle.net/adlaws/mk4128dg/
Basically you provide a tree structure for the table like this:
[
{
columnA: 'John', columnB:'Smith', columnC:'75',
children:
[
{ columnA: 'Mary', columnB:'Symes', columnC:'46' },
{ columnA: 'Stan', columnB:'Jones', columnC:'42' },
{ columnA: 'Pat', columnB:'Black', columnC:'38' },
]
}
]
The tree is then "flattened" out to rows which can be displayed in a table by the _flattenTreeStructure() method. During this process, the rows are also annotated with some extra properties to uniquely identify the row, store the depth of the row (used for indentation), the parent row of the row (if any) and whether or not the row is currently expanded.
Once this is done, the flattened structure can be handed to the <b-table> as it is just an array of rows - this is done via the computed property flattenedTree.
The main work now is done by the _filterFunction() method which provides custom filtering on the table. It works off the state of the expandedRowIndices property of the filterObj data item.
As the expand/collapse buttons are clicked, the row index (as populated during the flattening process) is inserted as a key into expandedRowIndices with a true or false indicating its current expanded state.
The _filterFunction() uses this to "filter out" rows which are not expanded, which results in the effect of expanding/collapsing a tree in the table.
OK, so it works (yay!), but...
it's not as flexible as the base <b-table>; if you want to show different columns of data, you'll need to do some work and to re-do the <template slot="???"> sections for the columns as required.
if you want to actually use filtering to filter the content (with a text search, for example) you'll need to extend the custom filter function to take this into account as well
sorting the data is not something I had to do for my use case, and I'm not sure how it would work in the context of a tree structure anyway - maintaining the tree's parent/child relationships while changing the order of the rows around would be... fun, and I suspect this would be a nice challenge to implement for someone who is not as time poor as me. ;)
Anyway, I hope this is of use to someone. I'm reasonably new to Vue.js, so there may be a better way to approach this, but it's done the job I needed to get done.

Vue-Tables-2: How to pass a VueJS custom filter?

How would one go about applying a VueJS filter (i.e. {{ price | currency }}) to data displayed using vue-tables-2?
I tried playing around with slots with one of the demo tables to no avail: https://jsfiddle.net/jfa5t4sm/11/
This is a bit annoying, as 'custom filters' mean different things in different context, so searching the docs is not bearing fruit.
Anyone have ideas?
Since it's a scoped slot you have to access the value through the props object and give it the correct slot name.
<template slot="price" scope="props">
<div>
<p>{{ props.row.price | currency }}</p>
</div>
</template>
Working JsFiddle