I'm doing a simple crud using vue, vuetify and vuex. but I am having a problem when updating my data that are already rendered in my template, the mutations in my store state work correctly, therefore these changes should be updated in my template, however they keep their previous state, the states in vuex are not reactive? Should not they listen if there is any change and change its value?
this is my template where I use a getters from my store to be able to iterate in my table:
<v-data-table
:headers="headers"
:items="categories"
:search="search"
>
<template slot="items" slot-scope="props">
<td>{{ props.item.name }}</td>
<td>{{ props.item.description }}</td>
<td>
<v-btn icon class="mx-0" #click="editItem(props.item)">
<v-icon color="teal">edit</v-icon>
</v-btn>
<v-btn icon class="mx-0" #click="deleteItem(props.item)">
<v-icon color="pink">delete</v-icon>
</v-btn>
</td>
</template>
</v-data-table>
this is the script of my component (a summary):
import { mapGetters } from 'vuex'
export default {
name: 'category',
data: () => ({
dialog: false,
search: '',
headers: [
{ text: 'Name', value: 'name' },
{ text: 'Description', value: 'description' },
{ text: 'Actions', value: 'name', sortable: false }
]
}),
computed: {
...mapGetters([
'categories'
])
}
}
and this a summary of my store, as I mentioned before all the mutations that I perform work and the state is modified according to the type of mutation that is made, but this is not reflected in my template, there is something that I am not handling correctly of vuex ?
const state = {
categories: []
}
const mutations = {
GET_CATEGORIES(state, categories) {
state.categories = categories.reverse()
},
ADD_CATEGORY(state, category) {
state.categories.unshift(category)
},
UPDATE_CATEGORY(state, category) {
const index = state.categories.findIndex(x => x.id == category.id)
state.categories[index] = { ...state.categories[index], ...category }
},
DELETE_CATEGORY(state, category) {
state.categories.splice(category, 1)
}
}
You are using mapGetters but I don't see any getters at your code.
Read about getters here: https://vuex.vuejs.org/en/getters.html
Read about how to map state to props here: https://vuex.vuejs.org/en/state.html
And one important note about this line state.categories[index] = { ...state.categories[index], ...category }
Read about reactivity for arrays and objects in Vue: https://v2.vuejs.org/v2/guide/list.html#Caveats
Hope it will help.
Related
i'm kinda stuck. i get this error every time i try to update my store. what i'm doing is, i fetch my product and store it. here on upload image section (component) at created fill the image with state to show the already uploaded images, then on each upload change my state via dispatch and mutation. i know it has something to do with created() when i fill my data with state, but don't know how to solve that.
here is my template:
<v-card flat class="pb-4" max-width="450px">
<v-img class="rounded-lg theme__main__color" :src="url"></v-img>
<v-list container inline class="transparent text-center pa-0">
<v-list-item class="px-0">
<v-list-item class="px-1">
<v-btn
width="100%"
class="theme__btn__w text_main_color"
:loading="isSelecting"
#click="onChooseClick"
>{{lang.chooseimage}}</v-btn>
</v-list-item>
<v-list-item class="px-1">
<v-btn
width="100%"
class="theme__btn__s text_main_color"
#click.prevent="uploadImage"
>{{lang.upload}}</v-btn>
<input
ref="uploader"
class="d-none"
type="file"
accept="image/*"
#change="onFileChanged"
>
</v-list-item>
</v-list-item>
</v-list>
</v-card>
and this is my script:
export default {
data(){
return{
isSelecting: false,
selectedFile: null,
url: null,
placeholder: '/images/placeholder/place-800.png',
section: 'img',
pimages: [],
pId: null
}
},
methods:{
onChooseClick(){
this.isSelecting = true
window.addEventListener('focus', () => {
this.isSelecting = false
}, { once: true })
this.$refs.uploader.click()
},
onFileChanged(e) {
this.selectedFile = e.target.files[0]
this.url = URL.createObjectURL(this.selectedFile)
// do something
},
async uploadImage(){
if(this.notEmpty(this.selectedFile) && this.notEmpty(this.pId)){
const data = new FormData()
data.append('image', this.selectedFile)
data.append('pId', this.pId)
// let response = await this.axiosPost('product/createproimg', data)
this.pimages.push({"url": this.url})
this.url = this.placeholder
this.setEditProductImg(this.pimages)
}
}
},
created(){
this.url = this.placeholder
this.pId = this.editProduct.pId
this.pimages = this.editProduct.images
this.$nuxt.$on('insert',(section)=>{
if(section === 'desc' && !this.pId){
this.pId = this.editProduct.pId
}
})
}
}
and of course my store:
state:
editProduct: {
pId: null,
images: []
}
getter:
editProduct(state){
return state.editProduct
}
mutation:
SET_EDITPRODUCT_IMG(state, img){
state.editProduct.images = img
},
action:
setEditProductImg({commit}, img){
commit('SET_EDITPRODUCT_IMG', img)
},
UPDATE
thanks to #skirtle the above problem has been solved! but got a new same error on something else. as advise by #skirtle used const to mutate my state but get error. to be more clear, my state is an empty array, it will be filled the first time, but i get the error if i even try to change my select!!! let alone send another mutation!! here is the codes:
<template>
<div class="pt-6">
<v-row class="ma-0">
<v-col cols="12" md="12" class="pa-0">
<v-row class="ma-0">
<!-- form 1 -->
<template v-for="(select, index) in selects">
<component
:is="select"
:key="select.name"
v-model="catId"
#changed="addComponent(index)"
:catid="catId"
:selectindex="index"
:pcat="productCat[index]"
:subcat="subCat"
></component>
</template>
<!-- btn -->
<v-col cols="12" sm="6" md="3" class="px-1">
<v-btn width="100%" class="theme__little__color2 text_main_color px-2" #click.prevent="addCatBtn()">{{lang.addcat}}</v-btn>
</v-col>
{{selectedCatArr}}
<!-- btn -->
<addproductbtn :section="section" />
</v-row>
</v-col>
</v-row>
</div>
</template>
<script>
import addproductbtn from '~/components/global/cms/addproductbtn'
import selectcategory from '~/components/global/cms/selectcategory'
export default {
components:{
'addproductbtn': addproductbtn
},
data(){
return{
section: 'cat',
selects: [selectcategory],
catId: 0,
subCat: true,
selectedCatArr: []
}
},
methods:{
addComponent(index){
this.selects.length = index + 1
setTimeout(() => {
this.selects.push(selectcategory)
}, 1);
},
addCatBtn(){
this.goToRedirect('/cms/category/insert', this.$route.path, this.ProductId)
},
async insertCategory(){
const data = {
pId: this.editProduct.pId,
catId: this.catId
}
// let response = await this.axiosPost('product/catupdate', data)
const productCategory = this.selectedCatArr
this.setEditProductCat(productCategory)
}
},
computed:{
productCat(){
return this.editProduct.categories
},
ProductId(){
return this.editProduct.pId
}
},
created(){
this.$nuxt.$on('insert', ()=>{
this.insertCategory()
})
this.$nuxt.$on('nextcat', (subCat)=>{
this.subCat = subCat
})
this.$nuxt.$on('nextpanel', ()=>{
this.insertCategory()
})
this.$nuxt.$on('selectedcat', (selected, index)=>{
delete selected.subCategory
this.selectedCatArr.length = index
this.selectedCatArr.push(selected)
})
}
}
</script>
and my select component:
<template>
<v-col cols="12" sm="6" md="3" class="px-1 text_details_color3" v-if="showCat">
<v-select
return-object
:items="items"
:label="lang.category"
v-model="selected"
#change="emitEvent"
item-text="title"
item-value="id"
outlined></v-select>
{{selected}}
</v-col>
</template>
<script>
export default {
props:['selectindex','catid','pcat','subcat'],
data(){
return{
selected:{},
items:[],
showCat: true
}
},
async fetch(){
// this.items = await this.axiosGet(`categories/${this.catid}/1`)
this.items = [
{id: this.catid + 1, title: this.catid+'title1', subCategory: true},
{id: this.catid + 2, title: this.catid+'title2', subCategory: true},
{id: this.catid + 3, title: this.catid+'title3', subCategory: false},
{id: this.catid + 4, title: this.catid+'title4', subCategory: true}
]
},
methods:{
emitEvent(){
this.$emit('input', this.selected.id)
this.$emit('changed')
$nuxt.$emit('nextcat', this.selected.subCategory)
$nuxt.$emit('selectedcat', this.selected, this.selectindex)
}
},
computed:{
//
},
created(){
},
mounted(){
this.selected = this.pcat
this.showCat = this.subcat
}
}
</script>
I believe the problem is this line:
this.pimages.push({"url": this.url})
The array this.pimages is the same array that's inside the store state and by calling push you're modifying it outside the store.
There are a couple of ways you could fix this.
One way would be to perform the push inside a mutation, e.g. by having an ADD_EDITPRODUCT_IMG mutation:
mutation:
ADD_EDITPRODUCT_IMG(state, img){
state.editProduct.push(img)
},
You'd then call that in much the same way as with your current mutation, except that you'd just pass it the new image to add rather than passing the full array.
An alternative approach that is a bit closer to what you currently have would be to take a copy of the array rather than modifying the original:
const newImages = [...this.pimages, {"url": this.url}]
this.setEditProductImg(newImages)
Using this approach you wouldn't need any changes to your existing store.
Update:
I also suggest making pimages a computed property. There doesn't seem to be any good reason to 'copy' the data out of the store state in a created hook.
After hours of searching and trying to find the correct method my n00b brain exploded.
I've tried so many things that I'm complete lost. Everything works like I want it to, can remove the customer I want, the front refreshes etc. Except the dialog.
Can you please explain how to close this dialog?
Here is my dialog.
<template>
<div>
<template>
<tbody>
<tr v-for="customer in AllCustomers" :key="customer.id" class="todo">
<td>{{customer.ID}}</td>
<td>{{ customer.name }}</td>
<td>{{customer.telephone}}</td>
<td>{{customer.email}}</td>
<v-btn color="success" #click="showDeleteDialog(customer)">DELETE</v-btn>
</tr>
</tbody>
</template>
<v-dialog v-model="dialogDelete" persistent max-width="500px">
<v-card>
<v-card-title>Delete</v-card-title>
<v-card-text>Weet je zeker dat je {{customerToDelete}} wenst te verwijderen?</v-card-text>
<v-card-actions>
<v-btn color="primary" text #click="close">Annuleer</v-btn>
<v-btn color="primary" text #click="deleteCustomer(customer.ID)">Verwijderen</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { mapGetters, mapActions, mapState } from "vuex";
export default {
name: "AllCustomers",
data() {
return {
customerToDelete: "",
dialogDelete: false
};
},
methods: {
...mapActions(["fetchAllCustomers", "deleteCustomer"]),
async close() {
this.dialogDelete = false;
},
async showDeleteDialog(customer) {
this.customer = Object.assign({}, customer);
this.customerToDelete = this.customer.name;
this.dialogDelete = !this.dialogDelete;
this.$store.commit("toggleDialog");
}
},
computed: mapGetters(["AllCustomers"]),
created() {
this.fetchAllCustomers();
},
...mapState(["dialogDelete"])
};
</script>
And here my module js.
import axios from 'axios';
const state = {
customers: [],
dialogDelete: false
};
const getters = {
AllCustomers: state => state.customers
};
const actions = {
async fetchAllCustomers({ commit }) {
const response = await axios.get(
'http://localhost:8888'
);
console.log(response.data.data);
commit('setAllCustomers', response.data.data);
},
async deleteCustomer({ commit }, id) {
await axios.delete(`http://localhost:8888/delete`, {
data: {
id: id
}
})
console.log(id)
commit('removeCustomer', id, this.dialogDelete = false);
},
}
const mutations = {
setAllCustomers: (state, customers) => (state.customers = customers),
removeCustomer: (state, id) =>
(state.customers = state.customers.filter(customer => customer.ID !== id)),
}
export default {
state,
getters,
actions,
mutations
};
You should use mapState to get your dialogDelete variable from store:
// in your dialog
import { mapState } from "vuex"
computed: {
...mapState(["dialogDelete"])
}
and you should change its state in mutations with a commit:
// in vuex store
const mutations = {
setAllCustomers: (state, customers) => (state.customers = customers),
removeCustomer: (state, id) =>
(state.customers = state.customers.filter(customer => customer.ID !==
id)),
toggleDialog: (state) => (state.dialogDelete = !state.dialogDelete)
}
// in your dialog
this.$store.commit("toggleDialog")
Since you didn't include the <script> tag in your code, I'm assuming that you're trying to toggle the vuex state directly by your close event from the vue component, which wouldn't work this way.
Instead, you would want to dispatch an action that commits a mutation that toggles the vuex state.
However, a better idea is to encapsulate the dialog component in a separate vue SFC (single file component) that has a local stateisActive, which you can toggle on or off via a local method this.isActive = false, and once you import that component you then give it a ref ref="deleteDialog", you'll then be able to access the component's internal methods like this: this.$refs.deleteDialog.close()
For more info about refs, see the docs.
EDIT
So for example:
DialogDelete.vue
<template>
<v-dialog v-model="isActive" persistent max-width="500px">
<v-card>
<v-card-title>Delete</v-card-title>
<v-card-text>Weet je zeker dat je {{customerToDelete}} wenst te verwijderen?</v-card-text>
<v-card-actions>
<v-btn color="primary" text #click="close">Annuleer</v-btn>
<v-btn color="primary" text #click="deleteCustomer(customer.ID)">Verwijderen</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
data() {
return {
isActive: false
}
},
methods: {
close() {
this.isActive = false
},
open() {
this.isActive = true
}
},
}
</script>
Parent.vue
<template>
<div>
<v-btn #click="openDialog">Open Dialog</v-btn>
<dialog-delete ref="deleteDialog" />
</div>
</template>
<script>
export default {
components: {
dialogDelete: () => import("./DialogDelete.vue"),
},
methods: {
openDialog() {
this.$refs.deleteDialog.open()
}
},
}
</script>
It's also clear to me that you're not placing the vuex helper methods (like mapGetters & mapState) where they should be, for example both of mapState & mapGetters should be within the computed:
computed: {
...mapGetters(["getterName"]),
...mapState(["stateName"])
}
check out the vuex docs.
My v-switch from vuetify is only binding one way.
If i load in my data it switches on or off. so its working if i load data in the v-model of the v-switch.
But if i switch the v-switch, it switches off, but does not change anything.
here is the code:
<v-data-table :headers="datatable.headers" :items="datatable.items" class="elevation-1">
<template v-slot:body="{ items }">
<tr v-for="(item, index) in items" :key="index">
<td>{{item.name}}</td>
<td #click="() => { $router.push(`/settings/${item.name.toLowerCase()}`) }"><v-icon small>edit</v-icon></td>
<td><v-switch v-model="inMenu[item.name.toLowerCase()]" :label="`Switch 1: ${inMenu[item.name.toLowerCase()]}`"></v-switch></td>
</tr>
</template>
</v-data-table>
<script>
export default {
data() {
return {
tabs: [
'Content types'
],
tab: null,
datatable: {
items: [],
headers: [{
text: 'Content types', value: "name"
}]
},
settings: null,
inMenu: {},
}
},
mounted() {
this.$axios.get('/settings.json').then(({data}) => {
this.settings = data
});
this.$axios.get('/tables.json').then(({data}) => {
// set all content_types
data.map(item => {
this.datatable.items.push({
name: item
})
})
// check foreach table if it's in the menu
this.datatable.items.forEach(item => {
this.inMenu[item.name.toLowerCase()] = JSON.parse(this.settings.menu.filter(menuitem => menuitem.key == item.name.toLowerCase())[0].value)
})
})
},
updated() {
console.log(this.inMenu)
}
}
</script>
so i clicked on the first switch and it does not change the state
i tried to have a normal prop in the data function.
i made a switch: null prop and it will react fine to that, but not to my code.
Any idea?
My guess is that your data is not reactive when you write:
// check foreach table if it's in the menu
this.datatable.items.forEach(item => {
this.inMenu[item.name.toLowerCase()] = JSON.parse(this.settings.menu.filter(menuitem => menuitem.key == item.name.toLowerCase())[0].value)
})
You should use the $set method instead and write:
// check foreach table if it's in the menu
this.datatable.items.forEach(item => {
this.$set(this.inMenu, item.name.toLowerCase(), JSON.parse(this.settings.menu.filter(menuitem => menuitem.key == item.name.toLowerCase())[0].value)
}))
See https://v2.vuejs.org/v2/guide/reactivity.html for more information on reactivity
Does this solve your problem?
I am using Vue-CLI. I have a Vue component which is called viewGenres.vue. This component contains Vuetify table which presents all of the current genres from Vue store. I am trying to add a new option for each genre which is "Edit".
My objective is that for each line in the table there will be an edit button. Once the button is clicked, a new component called editGenre.vue should be rendered.
This component should contain a filled-out form with all the existing details of the specific genre.
I have several questions:
1) Once I click on the edit button, the following exception appears on browser:
ReferenceError: Vue is not defined at VueComponent.editGenre
2) In order for me to load the right properties from the DB, I need to define the "ID" prop of the editGenre component. Does anyone have any recommendation on the best method to do so?
This is viewGenres.vue: (the method editGenre is the one responsible for rendering the new component).
<template>
<div class="root" ref="container">
<h2>Genres Info</h2>
<br>
<v-data-table
:headers="headers"
:items="genres"
hide-actions
class="elevation-1">
<template slot="items" slot-scope="props">
<td class="text-xs-left">{{ props.item.id }}</td>
<td class="text-xs-left">{{ props.item.name }}</td>
<td class="text-xs-left">{{ props.item.desc }}</td>
<td class="text-xs-left">{{ props.item.artists }}</td>
<td class="text-xs-left"><v-btn flat #click="editGenre(props.item.id)">EDIT</v-btn></td>
<td class="text-xs-left"><v-btn flat #click="deleteGenre(props.item.id)">Delete</v-btn></td>
</template>
</v-data-table>
</div>
</template>
<script>
import editGenre from '#/components/administratorView/Genres/editGenre.vue'
const firebase = require('../../../firebaseConfig.js')
export default {
data: function(){
return{
headers: [
{ text: 'ID', value: 'id'},
{ text: 'Name', value: 'name'},
{ text: 'Description', value: 'desc'},
{ text: 'Artists', value: 'artists'},
{ text: 'Edit Genre'},
{ text: 'Delete From DB'}
]
}
},
computed: {
genres: function(){
return this.$store.state.genre.genres
}
},
components: {
editGenre
},
methods: {
editGenre: function(id){
var ComponentClass = Vue.extend(editGenre)
var instance = new ComponentClass()
instance.$mount()
this.$refs.container.appendChild(instance.$el)
},
deleteGenre: function(id){
console.log("Trying to delete " +id)
firebase.firestore.collection("genres").doc(id).delete().then(()=>{
this.$store.dispatch('genre/getGenresFromDB')
alert("Deleted Document Successfully")
}).catch(function(error){
alert(error)
})
}
},
mounted(){
this.$store.dispatch('genre/getGenresFromDB')
}
}
</script>
<style scoped>
</style>
This is editGenre.vue:
<template>
<v-dialog v-model="editGenre" persistent max-width="500px">
<v-card>
<v-card-title>
<h2>Edit Genre {{genre.name}}</h2>
</v-card-title>
<v-card-text>
<v-text-field
v-model="name"
label="Name"
:error-messages="nameErrors"
#touch="$v.name.$touch()"
#blur="$v.name.$touch()"
/>
<v-textarea
v-model="desc"
label="Description"
box
/>
<v-combobox
v-model="artists"
label="Artists"
:items="artistNames"
:error-messages="artistsErrors"
#touch="$v.artists.$touch()"
#blur="$v.artists.$touch()"
multiple>
</v-combobox>
<v-btn
color="primary"
#click="submit">
Submit
</v-btn>
<v-btn
color="primary"
#click="close">
Close
</v-btn>
</v-card-text>
</v-card>
</v-dialog>
</template>
<script>
import { required } from 'vuelidate/lib/validators'
const firebase = require('../../../firebaseConfig')
export default{
data: function(){
return{
name: '',
desc: '',
artists: []
}
},
props: {
id: String
},
mounted: function(){
let docRef = firebase.firestore.collection("genres").doc(this.id)
docRef.get().then(function(doc){
if(doc.exists){
this.name = doc.data().name
this.desc = doc.data().desc
this.artists = doc.data().artists
}
else{
console.error("Doc Doesn't Exist!")
}
}).catch(function(error){
console.error(error)
})
}
}
</script>
<style scoped>
</style>
Thank You!
Tom
You missed to import Vue in your viewGenres.vue component, so add it as follow :
....
<script>
import Vue from 'vue'
import editGenre from '#/components/administratorView/Genres/editGenre.vue'
const firebase = require('../../../firebaseConfig.js')
....
You could pass props by this way :
var ComponentClass = Vue.extend(
props:{
id:{type:String, default () { return id}}
},editGenre)
and remove this :
props: {
id: String
}
according to Evan You :
It's not recommended to use new to manually construct child components. It is imperative and hard to maintain. You probably want to make your child components data-driven, using and v-for to dynamically render child components instead of constructing them yourself.
I want to use the vuetify framework with Vuex , but there is limited documentation about using it with Vuex.
I want to:
Get data from an external API ( but only the data needed )
Then Save the data in state and edit or whatever
Then push any changes back to the api
I have tried some of the external pagination and sorting examples with vuetify , but I can't get it to show all record count unless I hard code it.
I am quite new to Vue and Vuetify , so maybe I am misunderstanding something.
<template>
<div>
<v-data-table
:headers='headers'
:items='items'
:length='pages'
:search='search'
:pagination.sync='pagination'
:total-items='totalItemCount'
class='elevation-1'
>
<template slot='items' slot-scope='props'>
<td class='text-xs-right'>{{ props.item.id }}</td>
<td class='text-xs-right'>{{ props.item.first_name }}</td>
<td class='text-xs-right'>{{ props.item.last_name }}</td>
<td class='text-xs-right'>{{ props.item.avatar }}</td>
</template>
</v-data-table>
</div>
</template>
<script>
import moment from 'moment'
import axios from 'axios'
export default {
name: 'test-table',
watch: {
pagination: {
async handler () {
const rowsPerPage = this.pagination.rowsPerPage
// const skip = (this.pagination.page - 1) * rowsPerPage
const pageNumber = this.pagination.page
const res = await axios.get(`https://reqres.in/api/users?page=${pageNumber}&per_page=${rowsPerPage}`)
this.items = res.data.data
this.$store.commit('saveTableData', this.items)
},
deep: true
}
},
computed: {
pages () {
return 171
},
totalItemCount () {
return 400
}
},
async mounted () {
const rowsPerPage = this.pagination.rowsPerPage
const skip = (this.pagination.page - 1) * rowsPerPage
const res = await axios.get(`https://reqres.in/api/users?page=${skip}&per_page=${rowsPerPage}`)
this.items = res.data.data
this.$store.commit('saveTableData', this.items)
},
methods: {
nzDate: function (dt) {
return moment(dt).format('DD/MM/YYYY')
}
},
data: () => ({
search: '',
// totalItems: 0,
items: [],
pagination: {
sortBy: 'Date'
},
headers: [
{ text: 'ID', value: 'id' },
{ text: 'First Name', value: 'first_name' },
{ text: 'Last Name', value: 'last_name' },
{ text: 'Avatar', value: 'avatar' }
]
})
}
This is my working setup:
<template>
<v-data-table
:total-items="pagination.totalItems"
:pagination.sync="pagination"
:items="rows"
:headers="columns">
<template slot="headers" slot-scope="props">
<tr :active="props.selected">
<th v-for="column in props.headers">
{{ column.value }}
</th>
</tr>
</template>
<template slot="items" slot-scope="props">
<tr>
<td v-for="cell in props.item.row">
<v-edit-dialog lazy>
{{ cell.value }}
<v-text-field
:value="cell.value"
single-line
counter>
</v-text-field>
</v-edit-dialog>
</td>
</tr>
</template>
</v-data-table>
</template>
<script>
export default {
data: () => ({
pagination: {
page: 1,
rowsPerPage: 10,
totalItems: 0
},
selected: []
}),
computed: {
columns: {
get () {
return this.$store.state.columns
}
},
rows: {
get () {
return this.$store.state.rows
}
}
},
methods: {
async getRowsHandler () {
try {
const {total} = await this.$store.dispatch('getRows', {
tableIdentifier: this.$route.params.tableIdentifier,
page: this.pagination.page,
size: this.pagination.rowsPerPage
})
this.pagination.totalItems = total
} catch (error) {
// Error
}
}
}
}
</script>
I didn't implement everything. If you miss a specific part ask again and I will update my example. One more tip: You should avoid watch deep wherever possible. It can result in heavy calculations.
Assuming this is Vuetify v1.5, the documentation on the total-items prop on data-tables states:
Caution: Binding this to a blank string or using in conjunction with
search will yield unexpected behaviours
If you remove the 'search' prop from your table the record count will show again. If you're doing external stuff anyway, you'll won't want the default search functionality.