add hyperlink in v-data-table Vuetify - vue.js

I've been slowly learning the joys of vue.js and Vuetify.
I'm trying to get the emails and website as working links but just can't figure out how to do it.
I have my v-data-table
<v-data-table :headers="headers" :items="companies" :search.sync="search" :items-per-page="5">
In my script I'm having first:
data: () => ({
headers: [
{ text: "Bedrijfsnaam", align: "start", value: "name" },
{ text: "Telefoon", value: "phone" },
{ text: "e-Mail", value: "email" },
{ text: "Website", value: "website" },
{ text: "Locatie", value: "location" },
{ text: "Actions", value: "actions", sortable: false }
],
companies: [],
}),
And finally
methods: {
initialize() {
this.companies = [
{
name: "Lorem NV",
phone: "+32 1 234 56 78",
email: "info#lorem.be",
website: "www.lorem.be",
location: "Gent"
},
];
}

Welcome to Stack Overflow!
You need to implement a custom template for the row that has the link:
<v-data-table
:headers="headers"
:items="companies"
:search.sync="search"
:items-per-page="5"
>
<template #item.phone="{ item }">
<a target="_blank" :href="`tel:${item.phone}`">
{{ item.phone }}
</a>
</template>
<template #item.email="{ item }">
<a target="_blank" :href="`mailto:${item.email}`">
{{ item.email }}
</a>
</template>
<template #item.website="{ item }">
<a target="_blank" :href="item.website">
{{ item.website }}
</a>
</template>
</v-data-table>
You can put whatever content inside of the <template> elements that you want, so, for example, if you had an id field for each company and you wanted to link the company name to the page within your website that has the details page about that company, you could do:
<v-data-table
:headers="headers"
:items="companies"
:search.sync="search"
:items-per-page="5"
>
<template #item.name="{ item }">
<router-link :to="{ name: 'company', params: { id: item.id } }">
{{ item.name }}
</router-link>
</template>
</v-data-table>
You can put buttons, icons, or whatever you want inside of the template. Hope this helps!

You can use the item.<name> slot. For example, where website is the property name:
<template #item.website="{ value }">
<a :href="value">
{{ value }}
</a>
</template>
OR email,
<template #item.email="{ value }">
<a :href="`mailto:${value}`">
{{ value }}
</a>
</template>
This only needs to be used for the fields you want to customize.
Demo: https://codeply.com/p/CX3vXv6x6R

Related

Using Vue multiselect to change Algolia index

I currently have a page where I'm able to switch Algolia indices with this:
<template>
<button #click="selectedIndex = a">List A</button>
<button #click="selectedIndex = b">List B</button>
<A v-if="selectedIndex === a" />
<B v-if="selectedIndex === b" />
</template>
<script>
import A from '#/A.vue';
import B from '#/B.vue';
export default {
components: {
A,
B
},
data() {
return {
selectedIndex: `a_${this.$root.index}`,
query: ''
};
},
computed: {
a() {
return `a_${this.$root.index}`;
},
b() {
return `b_${this.$root.index}`;
}
}
};
</script>
This is in a file called Index.vue. The different indices are in files A.vue and B.vue.
But now I need to be able to do the same content switching using a vue-multiselect in A.vue and B.vue.
Currently in A.vue, I have
<template>
<ais-instant-search
:search-client="searchClient"
:index-name="a"
:routing="routing"
>
<multiselect
v-model="selectedIndex"
:options="switcherOptions"
:searchable="false"
:close-on-select="true"
:show-labels="false"
placeholder="Choose"
>
<template slot="singleLabel" slot-scope="{ option }">
{{ option.text }}
</template>
<template slot="option" slot-scope="{ option }">
{{ option.text }}
</template>
</multiselect>
</ais-instant-search>
</template>
export default {
components: {
Multiselect
},
data() {
searchClient: algoliasearch(window.algolia.id, window.algolia.key),
selectedIndex: { value: 'a', text: 'List A' },
switcherOptions: [
{ value: 'a', text: 'List A' },
{ value: 'b', text: 'List B' }
]
};
},
};
What I don't know how to do now is send the value from the multi-select from A.vue back up to Index.vue where the different indices are defined.
First observation that I have, in your A.vue you are binding variable named a to your index, but do not have a defined in your data, same thing for routing.
What is the point of computed properties a and b, they are just returning strings, with do not do any computing, this could probably be defined in data:
data() {
return {
selectedIndex: 'a_index',
query: '',
a: 'a_index',
b: 'b_index',
};
},
Are A.vue and B.vue identical components? If only bindings are different, you can probably combine them into one component, and just pass different props to them. Hard to know for sure without seeing complete code.
So one way of doing this is emitting events from child to parent components. Documentation reference: https://vuejs.org/guide/essentials/event-handling.html
On your multiselect component add #select="$emit("indexSelected", selectedIndex)"
Like so:
<multiselect
v-model="selectedIndex"
:options="switcherOptions"
:searchable="false"
:close-on-select="true"
:show-labels="false"
placeholder="Choose"
#select="$emit("indexSelected", selectedIndex)"
>
This will emit event with name of indexSelected and included selectedIndex value in its payload.
Then in Index.vue you need to add these event listeners to both A and B Components:
<A v-if="selectedIndex === a" #indexSelected="selectedIndex = $event.value === 'a' ? 'a_index' : 'b_index'" />
<B v-if="selectedIndex === b" #indexSelected="selectedIndex = $event.value === 'a' ? 'a_index' : 'b_index'" />
Or if you would like cleaner template you can create a method:
methods: {
updateSelectedIndex(event){
this.selectedIndex = event.value === 'a' ? 'a_index' : 'b_index';
}
}
And then update template to:
<A v-if="selectedIndex === a" #indexSelected="updateSelectedIndex" />
<B v-if="selectedIndex === b" #indexSelected="updateSelectedIndex" />
EDIT:
I think this is kind of what you are looking for(reading between the lines lol), this rolls Index.vue, A.vue, B.vue into one component, because what you are trying to achieve is a lot simpler this way IMO. Obviously your actual app is more complex, so apply this as needed.
IndexAB.vue would look like this, values replaces with algolia demo, so substitute as needed:
<template>
<div>
<p>
<strong>Changing Index with buttons: </strong>
<button
v-for="option in searchIndexOptions"
:key="option.value"
#click="searchIndexName = option"
>
{{ option.text }}
</button>
</p>
<ais-instant-search
:search-client="searchClient"
:index-name="searchIndexName.value"
>
<p>
<strong>Changing Index with vue-multiselect: </strong>
<VueMultiselect
v-model="searchIndexName"
:options="searchIndexOptions"
:searchable="false"
:close-on-select="true"
track-by="value"
label="text"
placeholder="Change Search Index Here"
>
<template v-slot:singleLabel="{ option }">
<strong>{{ option.text }}</strong>
</template>
<template v-slot:option="{ option }">
<strong>{{ option.text }}</strong>
</template>
</VueMultiselect>
</p>
<ais-search-box />
<ais-hits>
<template v-slot:item="{ item }">
<h2>{{ item.name }}</h2>
</template>
</ais-hits>
</ais-instant-search>
</div>
</template>
<script>
import VueMultiselect from "vue-multiselect";
import algoliasearch from "algoliasearch/lite";
import "instantsearch.css/themes/satellite-min.css";
import "vue-multiselect/dist/vue-multiselect.css";
export default {
name: "IndexAB",
components: { VueMultiselect },
data: () => ({
searchClient: algoliasearch("latency", "6be0576ff61c053d5f9a3225e2a90f76"),
searchIndexName: { value: "instant_search", text: "List A" }, // Defaults to instant_search/List A
searchIndexOptions: [
{ value: "instant_search", text: "List A" },
{ value: "airbnb", text: "List B" },
{ value: "airports", text: "List C" },
],
}),
};
</script>
And sandbox: https://codesandbox.io/s/compassionate-ptolemy-9ljmhh?file=/src/components/IndexAB.vue
I had started with Vue 3 sandbox, so few things will be sligtly different(like import of vue-multiselect, and v-slot syntax)

Vuetify table - add hyperlink to column

In my vue component I am using vuetify table. It works properly, but I need to make that click on items in column "Category name" redirect to vue view which shows details of clicked item (I need to forward item id to view which shows details of item). I don`t know how to do that?
In method 'editItem' I also need to redirect to other page, with forwarded id, I also don`t know how to do that?
I tried to make "Category name" items as hyperlink, but it does not work and it is without id:
<template v-slot:item.categoryName="{ item }">
<a :href="this.$router.push({name: 'EditCategory'})">{{item.categoryName}}</a>
</template>
This is code of my vue component 'CategoryTable':
<template>
<v-data-table
:headers="headers"
:items="categories"
sort-by="categoryName"
class="elevation-1"
:footer-props="{
'items-per-page-options': [1, 2, 3, 4, 5]
}"
:items-per-page="2"
>
<template v-slot:top>
<v-toolbar
flat
>
<v-toolbar-title>Categories Table</v-toolbar-title>
<v-divider
class="mx-4"
inset
vertical
></v-divider>
<v-spacer></v-spacer>
<button #click="$router.push({name: 'AddCategory'})">Add category</button>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon
small
class="mr-2"
#click="editItem(item)"
>
mdi-pencil
</v-icon>
<v-icon
small
#click="deleteItem(item)"
>
mdi-delete
</v-icon>
</template>
</v-data-table>
</template>
<script>
export default {
name: "CategoriesTable",
data: () => ({
headers: [
{
text: 'Category name',
align: 'start',
value: 'categoryName',
},
{ text: 'Description', value: 'description' },
{ text: 'Actions', value: 'actions', sortable: false },
],
categories: [],
editedIndex: -1,
defaultItem: {
id: 0,
categoryName: '',
description: '',
},
}),
created () {
this.initialize()
},
methods: {
initialize () {
this.$axios.get('/api/categories').then((response) => {
console.log(response.data)
this.categories = response.data;
});
},
editItem (item) {
this.$router.push({name: 'CategoryEdit'});
item.id
},
deleteItem (item) {
this.$axios.delete('/api/categories/' + item.id).then((response) => {
console.log(response)
});
},
},
}
</script>
If you want the user to be redirected after clicking on a particular category name, you will pass the corresponding route or id like i did below:
<v-data-table
:headers="headers"
:items="categories"
sort-by="categoryName"
class="elevation-1"
:footer-props="{
'items-per-page-options': [1, 2, 3, 4, 5],
}"
:items-per-page="2"
>
<template v-slot:item.categoryName="{ item }">
<a
#click="$router.push(`/details/${item.itemID}`)"
>
{{item.categoryName}}
</a>
</template>
</v-data-table>
the item id is passed after clicking on the category

How to add icon to the Header in vuetify DataTable

I'm trying to add (+) icon to the one of the header columns in v-datatable.
The code below does not add any icon. Actually, creating template slot for header does not have any effect on datatable.
What I try,
<template>
<v-data-table
item-key="name"
:items="complainantInfos"
:headers="headers"
sort-by="Id"
class="elevation-1">
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title>Inspection</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
</template>
<template slot="headers" slot-scope="props">
<tr>
<th
v-for="header in props.headers"
:key="header.text">
<v-icon small >plus-circle-outline</v-icon>
{{ header.text }}
</th>
</tr>
</template>
</v-data-table>
</template>
<script>
import { mapGetters } from "vuex";
export default {
data(){
return{
complainantInfos:[
{
...
}
],
headers: [
{ text: 'NameSurname', value: 'name', align: 'left' ,width: "25%" },
{ text: 'ID', value: 'PersonelIdNumber' },
{ text: 'Phone-1', value: 'Cellular1' },
{ text: 'Phone-2', value: 'Cellular2' },
{ text: 'Work Phone', value: 'WorkPhone' },
{ text: 'InterPhone', value: 'Interphone' },
{ text: 'Email', value: 'Email' },
//{ text: '+', value: 'action', sortable: false, align: 'right'}
],
I have edited the code according to the comments. The problem solved.
...
</v-card>
</v-form>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:header.Actions="{ header }">
<v-icon small>plus-circle-outline</v-icon>{{ header.text }}
</template>
...
Since you only wish to add the icon to one specific column, I would suggest header.columnName.
Your slot would look like this:
<template v-slot:header.name="{ header }">
<v-icon small>plus-circle-outline</v-icon>{{ header.text }}
</template>
If the column name is "Cellular1", the code will be <template v-slot:header.Cellular1="{ header }">.
Please make sure you have the icon set included. Otherwise, no HTML will be rendered for v-icon. You can test it with default mdi icons, for example mdi-minus.

Vuetify insert action button in data-table and get row data

Environment:
vue#^2.6.10:
vuetify#^2.1.0
I want to use v-data-table to show search results and add evaluate button in each row in the v-data-table.
Unfortunately I have two issues:
Evaluate buttons are not shown
I don't know how to get row data of pushed button
What do I need to change?
Template
<v-data-table
:headers="headers"
:items="search_result"
>
<template slot="items" slot-scope="row">
<td>{{row.item.no}}</td>
<td>{{row.item.result}}</td>
<td>
<v-btn class="mx-2" fab dark small color="pink">
<v-icon dark>mdi-heart</v-icon>
</v-btn>
</td>
</template>
</v-data-table>
Script
data () {
return {
headers: [
{ text: 'no', value: 'no' },
{ text: 'result', value: 'result' },
{ text: 'good', value: 'good'},
],
// in real case initial search_result = [], and methods: search function inject below data
search_result: [{no: 0, result: 'aaa'}, {no:2, result: 'bbb'],
}
},
slot name used to "replace the default rendering of a row" is item, not items
Add wrapping <tr> into slot template
Just add #click="onButtonClick(row.item) to v-btn and create method onButtonClick
<v-data-table :headers="headers" :items="search_result">
<template v-slot:item="row">
<tr>
<td>{{row.item.no}}</td>
<td>{{row.item.result}}</td>
<td>
<v-btn class="mx-2" fab dark small color="pink" #click="onButtonClick(row.item)">
<v-icon dark>mdi-heart</v-icon>
</v-btn>
</td>
</tr>
</template>
</v-data-table>
methods: {
onButtonClick(item) {
console.log('click on ' + item.no)
}
}
Note..
...solution above is replacing default row rendering with your own so expect some of the v-data-table features to not work (didn't try but I expect row selection, grouping, in place editing etc. will be broken). If that's problem for you, here is alternative solution:
Add one more column to your headers definition: { text: "", value: "controls", sortable: false }
Do not override item slot (row rendering). Override item.controls slot instead. Notice "controls" is the same as in column definition - we are overriding just rendering of "controls" column
Everything else is same
<v-data-table :headers="headers" :items="search_result">
<template v-slot:item.controls="props">
<v-btn class="mx-2" fab dark small color="pink" #click="onButtonClick(props.item)">
<v-icon dark>mdi-heart</v-icon>
</v-btn>
</template>
</v-data-table>
In my case the solution of Michal was throwing the following exception
The solution was using slot and slot-scope
<template>
<v-data-table
:headers="headers"
:items="items"
class="elevation-1"
>
<template slot="item.delete" slot-scope="props">
<v-btn class="mx-2" icon #click="() => delete(props.item)">
<v-icon dark>mdi-delete</v-icon>
</v-btn>
</template>
</v-data-table>
</template>
<script>
export default {
data() {
return {
headers: [
// Dynamic headers
{
text: 'Name',
value: 'name'
},
{
text: '',
// Row to replace the original template
value: 'delete'
},
],
items: [
{
id: 1,
name: 'A'
},
{
id: 2,
name: 'B'
}
]
};
},
methods: {
delete(item) {
this.items = this.items.filter((d) => d.id !== item.id);
},
},
};
</script>

bootstrap vue get row data when click on cell in custom rendering

I have a table that have two custom fields index and a trash icon in separate column for remove that row. I want to get row's in click on just trash icon (on cell) for get index of row and then remove it from items in data object but my problem is I don't know how to get row data in onclick event in custom rendering. How can I do that??
<b-table :fields="fields" :items="items">
<template v-slot:cell(index)="data">
{{ data.index + 1 }}
</template>
<template v-slot:cell(remove)="data">
<i class="fas fa-trash"></i>
</template>
</b-table>
this is my data:
fields: [
{ key: 'index', label: 'index' },
{ key: 'cardNumber',label: 'card number'},
{ key: 'status',label: 'status'},
{ key: 'remove',label: 'remove'}
],
items: [
{ cardNumber: '123456', status: 'accepted' ,_cellVariants: { status: 'accepted-card-number'},_showDetails: true},
{ cardNumber: '123456', status: 'pending' ,_cellVariants: { status: 'pending-card-number'},_showDetails: true},
{ cardNumber: '123456', status: 'unaccepted' ,_cellVariants: { status: 'unAccepted-card-number'},_showDetails: true},
],
This should be what your looking for https://jsfiddle.net/q95otzbg/. You just need a click function on your trash icon passing it the index of the row.
<div id="app">
<div>
<b-table :fields="fields" :items="items">
<template v-slot:cell(index)="data">
{{ data.index + 1 }}
</template>
<template v-slot:cell(remove)="data">
<i class="fas fa-trash" #click=removeRow(data.index)></i>
</template>
</b-table>
</div>
</div>
methods: {
removeRow(index) {
this.items.splice(index,1)
}
}