Vuetify v-data-table not stacking on mobile devices when using v-slot:body
How can I get the data table to stack if it implements the body v-slot? As can be seen in the Vuetify documentation this is how a normal v-data-table behaves:
And this is how it behaves when using body slot:
Thank you.
The default body implementation of v-data-table has two components for the table rows - Row and MobileRow. When the page's width is under 600px, MobileRow is used.
By using the body slot, the default implementation is discarded, along with the mobile logic, so you must supply your own. Fortunately, there's a handy helper class we can use to easily switch styling depending on the current page size.
Here's a codepen with a sketch up of the solution. The relevant part:
<template v-slot:body="props">
<tbody>
<tr v-for="item in props.items">
<td class="d-block d-sm-table-cell" v-for="field in Object.keys(item)">
{{item[field]}}
</td>
</tr>
</tbody>
</template>
For more precise styling, you might want to use the visibility helper classes and have two separate implementations for the table rows - just like the default implementation.
Related
I have a data table that displays multiple records in one page.. I need to do the following:
1- Pagination of the data table pages.
2- Display 10 records per page and give the user the ability to increase this number. For example.. (10, 20, 50, 100) records per page.
How can I do it using VueJS?
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>Date & Time</th>
</thead>
<tbody>
<tr> <td>1</td>
<td>Sara</td> <td>example1#example.com</td>
<td>12/2/2021 16:40</td>
</tr>
<tr>
<td>2</td>
<td>Safa</td> <td>example2#example.com</td>
<td>24/2/2021 08:40</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-4">
Showing 10 out of 100
</div>
<div class="col-8 text-end">
<div class="btn-group" role="group" aria-label="Basic outlined example">
<button type="button" class="btn btn-outline-primary">1</button>
<button type="button" class="btn btn-outline-primary">2</button>
<button type="button" class="btn btn-outline-primary">3</button>
</div>
</div>
</div>
You could tackle this a number of ways. To start, you could keep all of your logic in your current file, open a script block, define a Vue instance, set up a data object, define a data variable for the number of items per page as well as anything else you want to track.
Then, you'll use a v-model binding on an input field in your template, which you would need to read the Vue docs to get a better handle on (very good documentation available). Keep it simple to start. Don't have pagination, just begin with an input field in which your user enters the number of items that should render in the table. Get that working. Then, shift to a drop-down with multiple defined options, which again you could define in the data object and iterate through those (new now) in your template using a v-for loop. You can use a prebuilt dropdown with any number of Vue UI libraries like Vuetify or VueBoostrap or many others. You'll need to read their respective documentation, but it's pretty straight forward.
Then to add pagination, you'd need to learn about routing and route parameters and how to send such parameters to your API as params on the get request. Axios can handle this for you.
Sounds like you need to start with basics and get comfortable with simple projects. Building fully functional data tables are quite hard! There are many great libraries available for VueJS that help you with data tables, even large commercial ones as well.
Read the documentation and watch YT tutorials before tackling complex stuff. To be sure, tables are well suited for components as opposed to single templates. You would need to learn how to separate out logic, e.g. a dropdown component. Take your time and keep learning!
Here would be a list of table related projects to help you understand, see their docs and examples:
https://awesome-vue.js.org/components-and-libraries/ui-components.html#table
-Marcus
I have a Nuxt 2.9.0 app where I'm using #nuxtjs/vuetify 1.11.3.
I'm refactoring some HTML tables that we have in the app and replacing them with Vuetify's v-data-table components. Now, in our old tables we had these bar chart components that we would render inside the table cells, like this:
<th>
{{ totalAvg }}
<RatingBar :color="ratingColor(totalAvg)" />
</th>
That RatingBar component is basically just an empty div with a background color, as you can see below
What's the proper way in which I can keep this functionality when moving onto the Vuetify data tables?
I know that Vuetify implements some slots, for example the #header slot, but I'm not sure exactly how the slots work when it comes to table cells, since the #header slot, for example, seems to just append whatever <template> you write down for the slot, instead of actually replacing the header
Vuetify's v-data-table supports custom rendering for a specific column thru #item.<name>. Link to API doc.
I had the following data model in backend:
AccordionComponentContainer extends CMSTabParagraphContainer
AccordionItemComponent extends SimpleCMSComponent
The container extending the CMSTabParagraphContainer because extending the
AbstractCMSComponentContainer is pain in the ass (generated jalo classes has
to be adapted but this isn't important for this case, only for
understanding.
Now I have a component in Spartacus CmsAccordionComponent. I introduced a
component mapping:
AccordionComponentContainer: {
component: CmsAccordionComponent,
},
In my component html file I have something like this:
<h2>{{headline$ | async}}</h2>
<ul>
<ng-container *ngFor="let component of components$ | async; let i = index">
<li>{{component.content}}</li>
</ng-container>
</ul>
I used the files in
projects/storefrontlib/src/cms-components/content/tab-paragraph-container as
reference for my implementation (e.g. component implementation). Expect of ng-template (cxOutlet):
<ng-template [cxOutlet]="component.flexType" [cxOutletContext]="{}">
<ng-container [cxComponentWrapper]="component"></ng-container>
</ng-template>
Before, I tried the same solution as CMSTabParagraphContainer. For some reason this won't work in my project. I introduced an own component and a mapping for the children (AccordionItemComponent) but it didn't work. The child components aren't shown.
So I used my solution described above. With my solution the components are displayed (also the child components) but I cannot edit them in SmartEdit. Maybe it's related with this issue: https://github.com/SAP/spartacus/issues/1484.
For testing purpose I added the 'normal' CMSTabParagraphContainer with
CMSParagraphComponent's to my content slot in the backoffice. And I can edit
the first CMSParagraphComponent that is shown in SmartEdit. Unfortunately I
cannot add new paragraphs to the CMSTabParagraphContainer. Therefore I think
that the ng-template (cxOutlet) solution is the better one as mine.
Can you please explain how the TabParagraphContainerComponent and the snippet
ng-template (cxOutlet) works? Also I think that this should considered in the
github issue ticket (https://github.com/SAP/spartacus/issues/1484) so that
CMSTabParagraphContainer (AbstractCMSComponentContainer) are better supported
in Spartacus (SmartEdit).
Thanks for your help!
The most important piece is the cxComponentWrapper. This directive takes the component slot data and renders the component inside.
The cxComponentWrapper requires the following data set for each component:
{
flexType: item.typeCode,
typeCode: item.typeCode,
uid: item?.uid
}
A typical container component template implementation would iterate over the various components and applies the directive:
<ng-container *ngFor="let item of items$ | async">
<ng-container
[cxComponentWrapper]="{
flexType: item.typeCode,
typeCode: item.typeCode,
uid: item?.uid
}"
>
</ng-container>
</ng-container>
The problem that you'll likely face, is the lack of the component type in the container component cms data. The cms api will only expose the component UIDs for the various nested components. You need to fetch the component type from the backend, using CmsService.getComponentData. You need to do this for each component uid. If you do this in a loop, Spartacus will actually merge the various calls to CmsService.getComponentData and do a single call to the backend.
An example of such an implementation can be found at https://github.com/SAP/spartacus/blob/746a15c1b63998065b0ceea96f4da052829533fb/projects/storefrontlib/src/cms-components/content/banner-carousel/banner-carousel.component.ts#L25.
Is it allowed to use <template> tag inside template something like this?
I have to check some value.
<template>
<div>
<template v-for="category_field in category_fields">
<template v-if="category_field.show_type == 'new-row'">
//and also here can be more nested template tags
</template>
<template v-else>
//and also here can be more nested template tags
</template>
</template>
</div>
</template>
I am using this system in my project and wondering whether this is correct.
Yes, you can nest <template>s, and it's quite useful sometimes.
Vue's <template> is very similar to <React.Fragment>. It's a virtual container used for grouping or applying layout logic (using structural directives - e.g: v-for, v-if), without creating an actual DOM element.
Because it doesn't output a DOM element, it's used as a wrapper for SFC's HTML markup.
Technically, the limit on having only one child in Vue 2 was not coming from the <template> tag itself, but from the components, as Vue required them to have only one root element, which became the component's $el. More detail here.
Besides the typical usage of wrapping an SFC's markup, <template> tags are also used for:
combining structural directives (v-for, v-if) and letting Vue know in which order to apply the directives, as changing the order would likely change the result
applying layout or rendering logic (e.g: v-if) to multiple elements at once, without having to create an actual DOM wrapper around them, particularly useful when you don't want to break the parent/child relation of DOM elements (e.g: flex or grid parent/children, <ul>/<ol> + <li>, <tr> + <td>, <tbody> + <tr>, etc.).
reducing template boilerplate (e.g: moving the same v-if from multiple siblings on a <template> wrapper, so the condition is only written once).
Try using this. <template> require only one child.
<template v-for="category_field in category_fields">
<div>
<template v-if="category_field.show_type == 'new-row'">
//and also here can be more nested template tags
</template>
<template v-else>
//and also here can be more nested template tags
</template>
</div>
</template>
I am working on HTML/JS Win8 store app. I would like to render ListView in a table. How would I achieve this?
For now I have something like that:
<table id="products">
<tbody>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
<tr data-win-control="WinJS.Binding.Template" id="productsRowTemplate">
<td data-win-bind="textContent: name"></td>
<td data-win-bind="textContent: description"></td>
</tr>
</tbody>
</table>
<div id="productsListView"
data-win-control="WinJS.UI.ListView"
data-win-bind="winControl.itemDataSource: products.dataSource;"
data-win-options="{
itemTemplate: select('#productsRowTemplate'),
layout: { type: WinJS.UI.ListLayout }
}">
</div>
However, the result is not what I expect at all:
http://postimg.org/image/4z39tvu9v/
I would like to achieve effect similar to this one (I used styling from that example in my view):
http://www.w3schools.com/css/tryit.asp?filename=trycss_table_fancy
The reason why I use listView is that I am working with ViewModels which expose collections via properties - I just want to bind data to the view and show it using specific template. I would definately not want to create table dynamically from view's code behind.
If your goal is to render a table, the ListView is not what you want. The key thing is to separate the idea of the collection--your data source--from the control that you use to present it. That's why there's a WinJS.Binding.List collection class and a WinJS.UI.ListView control as separate entities.
A table is one way to present a collection; a ListView is another. So it doesn't really make sense to try to combine the two. In your markup, the template elements will actually be removed from the DOM and not render in the table. The ListView will attempt to use that template to render itself, but as it generates <tr> and <td> elements within its own div (outside of a <table>) it won't really work. That is, the template gets rendered for each item in the data source as children of the div where you declare the ListView, not at the place where you declare the template.
Although you could declare the ListView as a child of the table, it won't quite work there either because the ListView creates a deep div structure below the element where you declare it.
So count the ListView out for this purpose. What you want instead is a simple custom control that can take a WinJS.Binding.List and render a template as direct children of that control. In Windows 8.1 Preview there is a new control called the Repeater that serves this exact purpose, and the HTML Repeater control sample demonstrates it in the context of a <table>. Note that you'll want to use a <thead> for your headings and declare the control on the <tbody> so it can render copies of the template that has <tr> as the root element.
If you're targeting Windows 8 you won't have the Repeater, of course, but it's not too complicated to create one of your own (borrowing from the Win8.1 Repeater sources if you want). Define a constructor with WinJS.Class.define and make sure you have options through which you can declaratively specify the data source and the template. The template would contain your data-win-bind attributes. In the controls' constructor, iterate through the Binding.List, rendering a copy of template for each item in the list. Then call WinJS.Binding.processAll with the root element of that item and the item from the data source, which will set up the data binding you want.
You'd declare this control again on the tbody element so it creates tr children in the right place. To use the Repeater example (where it uses an inline template):
<table class="table">
<thead class="table-header"><tr><td>Id</td><td>Description</td></tr></thead>
<tbody class="table-body"
data-win-control="WinJS.UI.Repeater" data-win-options="{data: Data.items}">
<tr class="table-body-row">
<td data-win-bind="textContent: id"></td>
<td data-win-bind="textContent: description"></td>
</tr>
</tbody>
</table>
In the end you are creating the table dynamically, but it's encapsulated in the control so you can set up the data binding relationships declaratively.