Dynamic replacement of URL text with a hyperlink - vuejs2

I have a v-data-table component which cells are filled with text containing URLs: "Text with URL https://stackoverflow.com that I'd like to replace with hyperlink"
How can I dynamically replace all these URLs inside text with a-tags? The rest of the text except the URLs should remain unchanged.
<template>
<v-data-table
:headers="headers"
:items="items"
:single-expand="singleExpand"
:expanded.sync="expanded"
item-key="id"
show-expand
>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
{{ item.text }}
</td>
</template>
</v-data-table>
</template>
<script>
data: () => ({
singleExpand: false,
expanded: [],
headers: [],
}),
methods: {
urlify(text) {
const urlRegex = /(https?:\/\/[^\s]+)/g;
return text.replace(urlRegex, '$1');
},
},
</script>

In order to output real HTML, you will need to use the v-html directive.
You already have the urlify(text) method in your script section to convert text to URL. A simple use case of this directive with a method is:
<td :colspan="headers.length" v-html="urlify(item.text)">
</td>
For more information, you can read the official documentation.

Related

Change vuetify simple-table to data-table

I have this simple table in Vuetify, without headers and only one column. How can I change it to vuetify v-data-table?
<v-simple-table>
<thead />
<tbody>
<tr
v-for="item in categories"
:key="item"
#click="handleClick"
>
<td>{{ item }}</td>
<v-switch />
</tr>
</tbody>
</v-simple-table>
categories is a simple array of strings. I want to change it to data-table in order to nicely handle clicking and selecting rows.
Check this codesandbox I made: https://codesandbox.io/s/stack-71617004-simple-to-v-data-table-bm2yn1?file=/src/components/Example.vue
Using body slot
You can use the body slot of the data table and use almost the same code you have in your simple table like this. This way you set up the handleClick function in the tr:
<v-data-table
:headers="headers"
:items="items"
hide-default-footer
hide-default-header
:items-per-page="-1"
:footer-props="{
itemsPerPageOptions: [-1],
}"
>
<template v-slot:body="{ items}">
<tbody>
<tr v-for="item in items" :key="item" #click="handleClick(item)">
<td align="left">{{item}}</td>
</tr>
</tbody>
</template>
</v-data-table>
Using item slot
Or you can use the item slot, like this. In this other way, the handleClick function is configured using the #click:row event of the data table.
If you try to use the item slot with your array of strings, it will work but you'll get some warnings in your console. Telling you that your data-table item slot expected an object and received an string. That's because v-data-table component expects to receive an array of objects.
To avoid this warning you can simply convert your array of string into a dummy array of objects using Array.prototype.map, and bind the computed property instead.
computed: {
myItemsTransformed() {
return this.items.map(item => ({ name: item }));
}
},
<v-data-table
:headers="headers"
:items="myItemsTransformed"
hide-default-footer
hide-default-header
:items-per-page="-1"
:footer-props="{
itemsPerPageOptions: [-1],
}"
#click:row="(item) => handleClick(item.name)"
>
<template #item.name="{ item }">
{{ item.name }}
</template>
</v-data-table>
Notice that in both examples I have used the props hide-default-footer, hide-default-header to hide the footer and header of the data table and also set the items-per-page to -1. To show all the items of the table and avoid the pagination.
You can change like this :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
categoryList: ['Category 1', 'Category 2', 'Category 3'],
}),
computed: {
categoriesHeader() {
return [
{ text: "Name", value: "name" }
];
},
},
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.5.0/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app id="a">
<v-data-table :headers="categoriesHeader" :items="categoryList" item-key="id" class="elevation-1">
<template v-slot:[`item.name`]="{ item }">
{{ item }}
</template>
</v-data-table>
</v-app>
</div>

Rest API to v-data-table using v-select but v-data-table can't show so I use v-for to

Purpose:
1.Rest API >>> by axios
2.Select category >>> by v-select
3.Show table >>> by v-data-table but i can't show table so I use v-for to show but I got this
here what I got
it show many table (according to the number of data sets)
Here is my code
HTML Part
<template>
<v-container>
<h1>JSON Data</h1>
<span>Data Test Table For Current Sensor Table</span>
<!-- Choose -->
<div id="top">
<!-- left-right -->
<v-row align-center>
<!-- title -->
<v-col cols="5">
<v-subheader>Choose Generation: {{ selected }}</v-subheader>
</v-col>
<!-- dropdown -->
<v-col cols="7">
<v-select :items="items" v-model="selected" dense></v-select>
</v-col>
</v-row>
</div>
<!-- Button -->
<v-btn block v-on:click="getData()">Load Data</v-btn>
<div v-for="(item, i) in data" :key="i" >
<v-data-table :items="data" :headers="headers" :items-per-page="5">
<template slot="data" slot-scope="props">
<td>{{ props.data.userId }}</td>
<td>{{ props.data.id }}</td>
<td>{{ props.data.title }}</td>
</template>
</v-data-table>
</div>
<!-- Test -->
<span class="data_id">***{{ data }} </span><br />
</v-container>
</template>
Script Part
<script>
import axios from "axios";
export default {
name: "Current",
data: () => ({
items: ["albums", "todos", "posts"],
selected: "",
headers: [
{ text: "USER_ID", align: "start", sortable: false, value: "userId" },
{ text: "ID", value: "id" },
{ text: "TITLE", value: "title" },
],
data: [],
}),
methods: {
getData() {
axios
.get("https://jsonplaceholder.typicode.com/users/1/" + this.selected)
.then((response) => {
this.data = response.data;
})
.catch((err) => alert(err));
},
},
mounted() {
this.getData();
},
};
</script>
Vue&Vuetify is new to me. If you have any suggestions, please tell me.
I try to remove div <div v-for="(item, i) in data" :key="i" ></div> but when I refresh this page it gone and show only empty table
show only empty table
Please remove v-for.
Because v-data-table displays table for data you passed, it will display same table as much as count of items in the data.
https://prnt.sc/1nsxc7w

vue.js how do I make a v-slot template dynamic?

Hello and thank you for reading my question! I am working with Vue.js, Vuetify and v-data-table and I am working on making my v-slot work with two different strings as the name of the header.
<template>
<v-container fluid>
<v-data-table
:options="data"
:headers="headers"
:items="data
:server-items-length="40"
:loading="loading"
:multi-sort="true"
#update:options="updateThePage"
>
<template v-slot:[`header.overalls`]="{ header }" class="flight-date-header">
<overallDatePicker
:options="data"
/>
{{ header.name }}
</template>
<template v-slot:[`item.name`]="{ item }">
<p class="company-name">
{{ item.companyName }}
</p>
</template>
<template v-slot:[`item.price`]="{ item }">
<p> {{ item.price }} </p>
</template>
<template v-slot:[`item.flightDate`]="{ item }">
<p class="style">
{{ item.startDate }}
</p>
</template>
</v-data-table>
</v-container>
</template>
and I store my headers like below
headers: [
{ text: 'Campaign Name', value: 'overalls' },
],
Ideally I would like the name of this slot
<template v-slot:[`header.overalls`]="{ header }" class="flight-date-header">
<overallDatePicker
:options="data"
/>
</template>
To work with two different data options. right now the name of the header is overalls but I want the name of the header so ['header.overalls'] to be like ['header.('overalls' || 'shoes')] ,
The reason I am doing this is right now when I click on the header the options.sortBy property of the table gets set to 'overalls', but I want the icon on the column to show up if the options.sortBy property of the table is "overalls" and also show up if it is "shoes"
Please help and thank you so much!
To reuse the slot content for multiple headers/columns, use Vue's dynamic slots names feature + v-for
data() {
return {
specialHeaders: ['overalls', 'shoes'],
}
}
<template v-for="column in specialHeaders" v-slot:[`header.${column}`]="{ header }">
Special:{{header.text}}
</template>

Vuetify datatable search prop with autocomplete component

I'm trying to configure a drop down filter for each column in my vuetify data table. It seems the vuetify autocomplete component has the functionality I want but I'm not sure how to get the values from the autocomplete component to filter the data table. Here's what I have:
<template>
<v-card>
<v-card-title>
{{gridTitle}}
<v-spacer></v-spacer>
</v-card-title>
<v-data-table
v-model="selected"
:headers="headers"
:items="dataSource"
class="elevation-1"
:search="search"
:loading="isloading"
item-key="id"
show-select
multi-sort
dense
>
<template v-slot:header.id="{ header }" >
<v-autocomplete
v-model="search"
:items="dataSource"
item-text="id"
:label="header.value"
v-bind="selected"
dense
multiple
chips
small-chips
filled
>
</v-autocomplete>
</template>
<v-progress-linear
slot="progress"
color="blue"
indeterminate
></v-progress-linear>
<v-alert
slot="no-results"
:value="true"
color="error"
icon="warning"
>Your search for "{{ search }}" found no results.</v-alert>
</v-data-table>
</v-card>
</template>
<script>
export default {
name: "ConfirmationsGrid",
data() {
return {
isloading: true,
search: "",
selected: [],
};
},
props: {
dataSource: {
type: Array[Object],
default: new Array(),
},
headers: Array[String],
gridTitle: String,
},
mounted() {
this.isloading = false;
},
methods: {
onSelectMethod: function (value) {
this.$emit("select_method", value);
},
},
};
</script>
At the moment I'm testing with the one header. slot but I plan on extending to all headers. This renders the autocomplete as the column header and also shows the correct values in the drop down but selecting them doesn't filter the table. I only get the following error:
[Vue warn]: Invalid prop: type check failed for prop "search". Expected String with value "2579034", got Array
Any ideas on how I can convert the autocomplete data into a string?
You are adding the prop multiple to the v-autocomplete tag with v-model search, so it returns an Array, i.e.:
["search option1", "searchOption2"]
But the v-autocomplete tag with v-model selected is using the search prop as String, so that's te reason of the error.
The default behavior of the search prop for v-autocomplete tag is to match the string passed to it with each value in its options.
To test it, remove the multiple prop in the search v-autocomplete tag, and you can see that it works.
Now, in order to work with multiple word search, you can have a computed property as items for the first v-autocomplete:
...
computed: {
itemsForSelected() {
if (this.search.length) {
return this.dataSource.filter(item => this.search.includes(item))
}
return this.dataSource
}
}
...
<template>
<v-card>
<v-card-title>
{{gridTitle}}
<v-spacer></v-spacer>
</v-card-title>
<v-data-table
v-model="selected"
:headers="headers"
:items="itemsForSelected"
class="elevation-1"
:loading="isloading"
item-key="id"
show-select
multi-sort
dense
>
<template v-slot:header.id="{ header }" >
<v-autocomplete
v-model="search"
:items="dataSource"
item-text="id"
:label="header.value"
v-bind="selected"
dense
multiple
chips
small-chips
filled
>
</v-autocomplete>
</template>
<v-progress-linear
slot="progress"
color="blue"
indeterminate
></v-progress-linear>
<v-alert
slot="no-results"
:value="true"
color="error"
icon="warning"
>Your search for "{{ search }}" found no results.</v-alert>
</v-data-table>
</v-card>
</template>
Now you can remove the search prop from the first v-autocomplete, this solution is not useful if you want to have the search by substrings ("hol" matching "alcohol"), it just filter the items from the source that are not selected in the search filter. A better solution could be includes a regex to match more options.

Condition on template with v-if using v-slot prop

I'm trying to make a condition to enable a named slot like this:
<template v-slot:item="{ item }" v-if="item.loading">
<v-progress-circular indeterminate color="primary"></v-progress-circular>
</template>
My use case is a Vuetify datatable: each item has a "loading" property, and I'd like to activate "item" slot only if the row is loading ("item" slot is Slot to replace the default rendering of a row)
The error is that item is undefined in the v-if, which seems logic : item is only defined for template children tag.
Is there a way to solve this problem?
You can filter the items that you pass to the datatable with a computed property.
Can you just not swap element based on loading ?
Vue.config.devtools = false;
Vue.config.productionTip = false;
var app = new Vue({
el: '#app',
data: {
items: [{data : "", loading: true}, {data : "Some data", loading: false}]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="item in items">
<div>
<div v-if="item.loading">
Loading...
</div>
<div v-else>
{{item.data}}
</div>
</div>
</div>
</div>
I had a similar problem, and I solved it in Vuetify 2 by importing VDataTable/Row as 'v-data-table-row', and using it to render 'regular' table rows, and for custom rows I used my own template.
JavaScript
import Row from 'vuetify/lib/components/VDataTable/Row.js';
export default {
components: { 'v-data-table-row': Row },
data() {
return {
currentItemName: 'Ice cream sandwich'
}
}
// headers, items, etc...
}
HTML
<template v-slot:item="{ item }">
<tr v-if="item.name == currentItemName" class="blue-grey lighten-4">
<td>Custom prefix - {{ item.name }}</td>
<td colspan="2">{{ item.calories }} - Custom suffix</td>
</tr>
<v-data-table-row v-else :headers="headers" :item="item">
<template
v-for="(index, name) in $scopedSlots"
v-slot:[name.substr(5)]="data"
>
<slot
v-if="name.substr(0, 5) === 'item.'"
:name="name"
v-bind="data"
></slot>
</template>
</v-data-table-row> </template
You can check out working example here.
You can just put the v-if on the child element
<template #item="{ item }">
<v-progress-circular
v-if="item.loading"
color="primary"
indeterminate
></v-progress-circular>
</template>