What is the purpose of <template> usage in Vuetify? - javascript

I want to use Vuetify 2.0 in my project and currently reading about v-stepper component which is used to display progress through numbered steps.
In the playground example provided I see that they are using <template> element to wrap content of v-stepper component. HTML looks something like this (note: I removed unnecessary details):
<v-stepper>
<template v-for="n in steps">
<v-stepper-content
:key="`${n}-content`"
:step="n"
>
</v-stepper-content>
</template>
</v-stepper>
Notice the <template> tag used. What is the purpose of it? When should I use it as opposed to just putting the <v-stepper-content> directly inside of <v-stepper>?
I've read a bit about element on MDN but I am not sure how to use it specifically with Vuetify (or more generally with Vue.Js or just pure HTML/CSS/JS for that matter).

a <template> in the context of a v-for loop is an organizational item.
It does not get rendered by the browser. It is there to help with more complex rendering situations, where you don't want to limit yourself to a single element
In most cases you have a pretty straight forward mapping of items, each item in an array gets a <li> element. If this is the case, you're not likely to use this.
Here is an example of a problem where it might help...
Let's say you want to loop through an array of objects, and render a v-btn if the object is a button, and a v-image if the object is an image.
without template...
<span v-for="item in items">
<v-btn v-if="item.isBtn"></v-btn>
<v-img v-else-if="item.isImg"></v-img>
</span>
The problem is that each item will be wrapped in the span.
<span>
<v-btn/>
</span>
<span>
<v-img/>
</span>
<span>
<v-btn/>
</span>
If you, however, use the template element, the wrapping element is no longer there.
<template v-for="item in items">
<v-btn v-if="item.isBtn"></v-btn>
<v-img v-else-if="item.isImg"></v-img>
</template>
and you will get...
<v-btn/>
<v-img/>
<v-btn/>
You can also have it return multiple items in one instance of the loop.
in the vue docs at https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
it shows an example of rendering more than one item per iteration:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
There are some other cases where this might be helpful but not likely something you come across on a daily basis.
TL;DR;
Vue doesn't render the <template> element. It helps organize code chunk without the need of a single child element when looping
part 2.
When should I use it as opposed to just putting the directly inside of ?
Because the structure of vertical and horizontal steppers is different, the vuetify authors used it in the playground to allow users to toggle it. The first level of template (<template v-if="vertical">) is used do determine whether the next level should render the v-stepper-step elements as vertical or as horizontal. The second level is used to do the iterating of items.
example:
vertical (step and content are siblings):
<template>
<v-stepper v-model="e6" vertical>
<v-stepper-step :complete="e6 > 1" step="1">
Select an app
<small>Summarize if needed</small>
</v-stepper-step>
<v-stepper-content step="1">
<v-card color="grey lighten-1" class="mb-12" height="200px"></v-card>
<v-btn color="primary" #click="e6 = 2">Continue</v-btn>
<v-btn text>Cancel</v-btn>
</v-stepper-content>
<v-stepper-content step="2">...</v-stepper-content>
<v-stepper-step :complete="e6 > 3" step="3">...</v-stepper-step>
</v-stepper>
</template>
horizontal (each step is separate):
<template>
<div>
<v-stepper>
<v-stepper-header>
<v-stepper-step step="1">Select campaign settings</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="2">Create an ad group</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step step="3">Create an ad</v-stepper-step>
</v-stepper-header>
</v-stepper>
<v-stepper value="2" class="mt-12">
...
</v-stepper>
<v-stepper value="3" class="mt-12">
...
</v-stepper>
</div>
</template>

Related

what does {on, attrs} make in vue/vuetify?

I am aware that this question has been asked already but I still struggle to understand what does it mean?
<v-dialog
v-model="dialog"
width="500"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
color="red lighten-2"
dark
v-bind="attrs"
v-on="on"
>
Click Me
</v-btn>
</template>
(that is taken from official vuetify documentation https://vuetifyjs.com/en/components/dialogs/#usage , but I suspect there is just a JS thing that I don't understand).
What does {on, attrs} mean and how they propagate down to v-btn where they are used in v-on and v-bind?
There a few concepts here that need to be explained.
"Scoped slots" (or just "slots" since Vue 3) can pass data to the parent component. Think of the slot as kind of like a function with arguments. In this case the activator slot of <v-dialog> is passing an object containing properties on and attrs for use inside the slot content template. You should refer to the <v-dialog> docs to know how this data should be used; in this case <v-dialog> needs to know when the activator is clicked in order to present the dialog, which is why it exposes on and attrs that you need to link to whatever custom button you designate to be the dialog activator.
In a lot of component libraries, it's common for slots to expose on (event listeners) and attrs (attributes/props) that the component requires you to set on a component in the template; for this you use v-on and v-bind respectively. Refer to the docs to see how these directives can be used in this way to bind multiple attributes at once:
<!-- binding an object of attributes -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
This specific syntax (object destructing):
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on">
is the same as this:
<template v-slot:activator="scope">
<v-btn v-bind="scope.attrs" v-on="scope.on">
The on and attrs properties are just being extracted into separate variables.

How bind vuetify skeleton-loader to v-image load event

My target is to build a conditional rendering between <v-skeleton-loader> and <v-card>Skeleton binding them to a property called isActive
I want that Card component only renders when the <v-image> has been loaded.
"Officially" based on Vuetify's guideline, the <v-image> component has its own "load" event based on this sample codepen, this could be easilly handled by calling the loadCard(item) and setting isActive=true after image has been loaded <v-img :src="item.img" #load="loadCard(item)" > but it doesn't trigger on #load event.
<div v-for="item in items" :key="item.id" >
<v-card v-if="item.isActive">
<v-img :src="item.img" #load="loadCard(item)" >
......
</v-card>
<v-skeleton-loader v-else
:loading="item.isActive"
style="width:13.020vw; margin-bottom:2.395833vw;"
transition="scale-transition"
type="card">
</v-skeleton-loader>
I've tried to put call the function into the skeleton loaded by #click="loadCard(item)" and it works perfectly. After the click the Card renders.
<v-skeleton-loader v-else
#click="loadCard(item)"
:loading="item.isActive"
style="width:13.020vw; margin-bottom:2.395833vw;"
transition="scale-transition"
type="card">
</v-skeleton-loader>

get specific element of an table

hello every one I am trying to get a specific element in my table and add a class to it to make the red buttons green when an image is uploaded. But i can't get the specific row in my table. Any one a solution for this?
My code:
<b-table hover
:filter="filter"
id="my-table"
:bordered="bordered"
:fields="fields"
:items="items"
:per-page="perPage"
:current-page="currentPage"
responsive
striped
small>
<template v-slot:cell(actions)="row">
<b-button id="btnImage" size="sm" #click="info(row.item, $event.target)" class="addModal mr-2" ref="addModal">
<font-awesome-icon :icon="['fas', 'image' ]" />
</b-button>
</template>
</b-table>
If i do this: var tr = document.getElementById("my-table").getElementsByTagName("tr"); I get the following output:
inside every tr is an element thats called innerText that reference to the id that you can see here in my table:
But how do i get the specific row in my table and add an class to the button of that specific row?
Use scoped field slots
<template v-slot:cell(nameage)="data">
{{ data.item.name.first }} is {{ data.item.age }} years old
</template>
Refer https://bootstrap-vue.org/docs/components/table#scoped-field-slots. I think it's well documented.
For every one that needs it. I fixed the problem to give row.item as a parameter to my method very simple but is works great

Vue Named Slots Caveat?

When using Named Slots with Vue (utilizing the older, more verbose API for component slots), if I have a reusable component defined with a template like this:
<template>
<div v-for="field in formFields">
<slot name="`${field.key}_PREPEND`">
<span hidden></span>
</slot>
<slot name="`${field.key}_FIELD`">
<slot name="`${field.key}_LABEL`">{{ field.label }}</slot>
<slot name="`${field.key}_CONTROL`">
<input v-if="field.type === 'text'" v-model="model[field.key]"></input>
<input type="checkbox" v-else-if="field.type === 'checkbox'" v-model="model[field.key]"></input>
</slot>
</slot>
<slot name="`${field.key}_APPEND`">
<span hidden></span>
</slot>
</div>
</template>
(this is essentially a hollowed out version of an auto-form generating component I have)
I can then reuse this component like so:
<auto-form
:fields="someArray"
:model="someObject"
>
<template slot="Name_PREPEND"> This goes before the name field </template>
<template slot="Name_FIELD"> For some reason this isn't being rendered, the default slot markup is</template>
<template slot="Name_APPEND"> This goes after the name field </template>
</auto-form>
For some reason, using the above markup (<auto-form>), the slot "${field.key}_FIELD" is ignored.
If I change the inner markup of the _PREPEND field like so
<slot name="`${field.key}_PREPEND`">
<span hidden>
<slot name="`${field.key}_CONTENT`"></slot>
</span>
</slot>
I similarly cannot override the _PREPEND slot (but can override _CONTENT)
Is this simply a limitation of Vue component slots? i.e. Are nested component slots not allowed?
In this particular case, the limitation would prevent a developer using this AutoForm component from say, overriding both the control and label at once via the _FIELD slot (for my uses I wanted to add logic that made a particular field conditional based on the value of other fields in the form)
In case anyone else runs into this issue, it's a bit of a sneaky one.
It looks like if you do conditional rendering on markup that is supposed to override a slot, the default slot will render in its place when it is not conditionally rendered.
So, the simple solution is to use v-show instead of v-if when you try to override the component slot.
(Has nothing to do with nested component slots as originally suspected)

How to add tooltip to datatable header in vuetify?

In older version on vuetify you could access headerCell slot and easily add tooltips - see https://codepen.io/nueko/pen/dZoXeZ
In the latest version you have named slots, so you need to know header name before
<template v-slot:header.givenname="{ header }">
Is there a way to add a tooltip to all headers?
There are 2 ways to achieve this.
Option 1: Customizing whole table row
If you need to customize whole row element inside table heading this might be useful. Even though I would not recommend to follow this way if you don't want to loose sorting functionality which exist by default in v-data-table.
Example:
<template v-slot:header="{ props: { headers } }">
<thead>
<tr>
<th v-for="h in headers">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{h.text}}</span>
</template>
<span>{{h.text}}</span>
</v-tooltip>
</th>
</tr>
</thead>
</template>
Working pen: https://codepen.io/onelly/pen/QWWmpZN
Option 2: Customizing each heading without losing sorting functionality
I guess this is more like what you are trying to do and the replacement for the old way. You can loop <template v-slot:header> and use Dynamic Slot Names to accomplish this. Syntax for Dynamic slot name looks like this <template v-slot:[dynamicSlotName]>.
Example:
<template v-for="h in headers" v-slot:[`header.${h.value}`]="{ header }">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{h.text}}</span>
</template>
<span>{{h.text}}</span>
</v-tooltip>
</template>
Working pen: https://codepen.io/onelly/pen/ExxEmWd

Categories

Resources