Vue warn: Avoid mutating a prop directly when use vuetify <v-dialog> - javascript

Vue v2. Can't fix this warn, i'm use v-dialog in child element.Child element have prop - dialog for toggle v-dialog. Try to use for v-dialog v-bind:value and #input instead of v-model but with this don't open dialog. Try to use computed dialogLocal - dialog can't open with computed prop.
Parent
<template>
<div>
<p class='subtitle_text'>Поставщик</p>
<v-autocomplete
clearable
dense
v-model='supplier'
:items='suppliers'
:item-text='item => item.supplierName'
return-object
>
<template v-slot:item="{ item }">
<v-list-item-content>
<v-list-item-title v-text="item.supplierName"></v-list-item-title>
</v-list-item-content>
<v-list-item-action>
<v-btn
fab
x-small
dark
color='primary'
#click="openEditDialog(item)"
>
<v-icon
x-small
dark
>
mdi-pencil
</v-icon>
</v-btn>
</v-list-item-action>
<v-list-item-action>
<v-btn
style='margin-left: 15px'
fab
x-small
dark
color='red'
#click="openDeleteDialog(item)"
>
<v-icon
x-small
dark
>
mdi-delete
</v-icon>
</v-btn>
</v-list-item-action>
<DeleteDialog
message = 'Удалить поставщика'
v-bind:info = currentSupplier.supplierName
v-bind:id = currentSupplier.id
v-bind:isActive = 'isDeleteDialogActive'
#confirmAction = 'confirmDeleteSupplier'
#cancelAction = 'cancelDeleteSupplier'
ref='deleteDialog'
/>
</template>
<template v-slot:append-item>
<div style="padding-left: 0px; max-height: 2rem">
<v-btn
#click='openCreateDialog'
block
text
large
style='font-size: 1em'
>
<v-icon
left
large
color='green'
>
mdi-plus-circle
</v-icon>
Создать
</v-btn>
</div>
<SupplierDialog
v-bind:dialog='isSupplierDialogActive'
v-bind:title='supplierDialogTitle'
v-model:supplier='currentSupplier'
#save='confirmSupplierDialog()'
#cancel='cancelSupplierDialog'
/>
</template>
</v-autocomplete>
</div>
</template>
<script>
imports ...
export default {
components: {
DeleteDialog,
SupplierDialog
},
data() {
return {
suppliers: null,
currentSupplier: {id: null, name: null},
supplier: {id: null, name: null},
supplierDialogTitle: null,
isSupplierDialogActive: false,
isDeleteDialogActive: false
}
},
methods: {
getAllSuppliers() {
...
},
openEditDialog(supplier) {
this.isSupplierDialogActive = true
this.currentSupplier = supplier
this.supplierDialogTitle = 'Редактирование поставщика'
},
openDeleteDialog(supplier) {
...
},
confirmDeleteSupplier(id) {
...
},
cancelDeleteSupplier() {
...
},
openCreateDialog() {
this.currentSupplier = {id: null, name: null}
this.isSupplierDialogActive = true
this.supplierDialogTitle = 'Создание поставщика'
},
cancelSupplierDialog() {
this.currentSupplier = {id: null, name: null}
this.getAllSuppliers()
this.isSupplierDialogActive = false
},
confirmSupplierDialog(supplier) {
this.getAllSuppliers()
this.supplier = supplier
this.isSupplierDialogActive = false
}
},
mounted() {
this.getAllSuppliers()
}
}
</script>
<style>
...
</style>
Child
<template>
<div>
<v-dialog
v-model='dialog'
max-width="50vw"
persistent
>
<v-card class='dialog_card'>
<v-card-title class="text-h5">
{{ title }}
</v-card-title>
<v-form
>
<v-text-field
label='Наименование поставщика'
required
v-model='supplier.supplierName'
></v-text-field>
</v-form>
<v-card-actions>
<v-btn
#click='addSupplier'
>
Сохранить
</v-btn>
<v-spacer></v-spacer>
<v-btn
#click='cancelDialog'
>
Отмена
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import ...
export default {
props: ['dialog','title', 'supplier'],
model: {
prop: 'supplier',
event: 'change'
},
computed: {
supplierLocal: {
get: function () {
return this.supplier
},
set: function (value) {
this.$emit('change', value)
}
},
},
methods: {
cancelDialog() {
this.supplierLocal = {id: null, supplierName: null}
this.$emit('cancel')
},
addSupplier() {
if (this.supplierLocal.supplierName != null && this.supplierLocal.supplierName != '') {
if(this.supplierLocal.id == null) {
RestService.postSuppliers(this.supplierLocal).then((response) =>
{
this.$emit('save', response.data)
},
error => {
this.content =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString();
this.isLoading = false;
if (error.response && error.response.status === 403) {
EventBus.dispatch("logout");
}
}
)
} else {
RestService.putSuppliers(this.supplierLocal).then((response) =>
{
this.$emit('save', response.data)
},
error => {
this.content =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString();
this.isLoading = false;
if (error.response && error.response.status === 403) {
EventBus.dispatch("logout");
}
}
)
}
}
}
}
}
</script>
<style>
.dialog_card {
padding: 4% 10%;
}
</style>
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "dialog"
found in
---> <SupplierDialog> at src/components/main_container/letter_of_authorization/SupplierDialog.vue
<VList>
<VSelectList>
<VThemeProvider>
<VMenu>
<VAutocomplete>
<SupplierForm>
<VCard>
<CreateLetterOfAuthorization> at src/views/app/CreateLetterOfAuthorization.vue
<MainContainer> at src/components/main_container/MainContainer.vue
<VApp>
<Application> at src/views/app/Application.vue
<App> at src/App.vue
<Root>

Related

data table template as prop vuejs

I have a component that I use in various parts of my application to render tables.
What I want to do now in view of the fact that every time I need to add more custom fields to the table (templates), is to be able to pass these templates as a prop to the parent component so that I do not have to always be editing this component.
As you can see I have tried in many ways and I have not been able to find the solution.
Father component
<template>
<section>
<data-table-search
:titleFilter="titleFilter"
:selectFilter="selectFilter"
:srchfunction="srchfunction"
:clkfunction="clkfunction"
:filterable="filterable"
:itemsFilter="itemsFilter"
:clickSelect="clickSelect"
:btnew="btnew"
:elevationNew="elevationNew"
:btnNewClass="btnNewClass"
:btsmall="btsmall">
</data-table-search>
<div :is="processedHtml"></div>
<v-data-table
class="tableBackground"
:dense="dense"
:headers="headers"
:items="items.data"
:server-items-length="items.recordsFiltered"
:options.sync="options"
:loading="loading"
:footer-props="footerProps"
no-data-text="No hay datos disponibles"
:loading-text="$t('comunes.general.cargando')">
<template v-slot:progress>
<v-progress-linear
height="2"
:color="colorProgress"
indeterminate
></v-progress-linear>
</template>
<template v-if="Object.keys(spanState).length > 0 && spanState.state == true" v-slot:item.state="{ item }">
<div class="">
<span v-if="item.state === 1" color="red">Abierto</span>
<span v-else-if="item.state === 2" color="green">En bodega</span>
<span v-else-if="item.state === 3" color="green">Revisado</span>
<span v-else-if="item.state === 4" color="green">En correo</span>
<span v-else-if="item.state === 5" color="green">Aceptada</span>
<span v-else-if="item.state === 6" color="green">Parcial</span>
<span v-else-if="item.state === 7" color="green">Rechazada</span>
<span v-else-if="item.state === 8" color="green">Orden de trabajo</span>
<span v-else-if="item.state === 9" color="green">Incompleto</span>
<span v-else-if="item.state === 10" color="green">Facturado</span>
<span v-else color="green"></span>
</div>
</template>
<template v-slot:item.activo="{ item }">
<v-icon :small="btsmall" color="green" v-if="item.activo">check_circle</v-icon>
<v-icon :small="btsmall" color="red" v-else>cancel</v-icon>
</template>
<template v-if="actions.length>1" v-slot:item.opciones="{ item }">
<v-menu offset-y small>
<template v-slot:activator="{ on }">
<v-btn :small="btsmall" class="btn-opciones" dark v-on="on">{{ $t('comunes.general.opciones') }}</v-btn>
</template>
<v-list class="dt-actions">
<v-list-item-group v-for="(action, index) in actions" :key="index">
<v-list-item #click="_onClick(action.action, item.id)">
<v-list-item-icon :class="action.color">
<v-icon :small="btsmall" class="text-white" v-text="action.icon"></v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title v-text="action.text"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-menu>
</template>
<template v-else v-slot:item.opciones="{ item }">
<v-btn :small="btsmall" class="btn-opciones" #click="_onClick(actions[0].action, item.id)" dark v-on="on">{{ btnSeleccionar ? btnSeleccionar : $t('comunes.general.seleccionar') }}</v-btn>
</template>
</v-data-table>
</section>
</template>
<script>
import datatableMixin from '#/mixins/datatable'
import dataTableSearch from '#/components/utilidades/dataTableSearch'
export default {
name: 'datatable',
mixins: [ datatableMixin ],
components: { dataTableSearch },
data () {
return {
on: false,
template: '<h1>Hola</h1>'
}
},
props: {
searchFunction: Function,
clkfunction: Function,
clickSelect: Function,
endpoint: String,
dense: Boolean,
btsmall: Boolean,
// initialData: {
// type: Boolean,
// default: true
// },
re: Function,
initialData: {
type: Boolean,
default () {
return true
}
},
headers: {
type: Array,
default: { }
},
spanState: {
type: Object,
default () {
return {}
}
},
itemsFilter: {
type: Array,
default: Array
},
templates: {},
filterable: {
type: Boolean,
default: false
},
selectFilter: {
type: Boolean,
default: false
},
titleFilter: {
type: String,
default: 'Filtro'
},
btnew: {
type: Boolean,
default: false
},
btnActionClass: {
type: String,
default: ''
},
btnNewClass: {
type: String,
default: ''
},
elevationAction: {
type: Number,
default: 2
},
elevationNew: {
type: Number,
default: 2
},
colorProgress: {
type: String,
default: 'primary'
},
btnSeleccionar: {
type: String,
default: null
},
actions: Array,
endManual: Boolean
},
computed: {
processedHtml () {
// let html = this.html.replace('[Placeholder]', '<my-component></my-component>')
return {
template: this.template
}
}
},
mounted () {
this.getData()
if (Object.keys(this.spanState).length > 0) {
console.log('fds', this.items)
}
},
methods: {
_onClick (event, id) {
this.clkfunction(event, id)
},
async srchfunction (text) {
this.search = text
this.options.page = 1
this.getData()
}
}
}
</script>
Child component:
<template>
<div>
<v-dialog v-if="dialogModalBuscador" v-model="dialogModalBuscador" scrollable max-width="600px">
<v-card>
<v-card-title>
<span class="h2" >{{title}}</span>
</v-card-title>
<v-card-text class="pb-0">
<data-table
:btsmall="true"
:endpoint="endpoint"
:headers="headers"
:filterable="true"
:actions="actions"
:clkfunction="clickFunction"
:initialData="initialDataL"
v-on:changeInitialDatastateEmit="emm"
:showCountOrdenesTecnico="True"
:customTemplate="re"
:endManual="true">
<template
v-for="header in headers"
v-slot:[`item.${header.value}`]="{ item }"
>
<slot :name="[`item.${header.value}`]" :item="item">
{{ getVal(item, header.value) }}
</slot>
</template>
<template v-slot:item="{ item }">
<div class="text-center">
<span v-if="item.countOrdenNoFacturada === 0" color="red">Sin Entregar</span>
<span v-else-if="item.countOrdenNoFacturada === 2" color="green">Solicitado</span>
<span v-else color="green"></span>
</div>
</template>
</data-table>
</v-card-text>
<v-card-actions class="d-block" >
<v-btn color="red" #click="dialogModalBuscador = false" outlined >{{$t('comunes.general.cancelar')}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import configMixin from '#/mixins/config'
import dataTable from '#/components/utilidades/dataTable'
export default {
mixins: [ configMixin ],
components: { dataTable },
data () {
return {
dialogModalBuscador: false,
loading: false,
initialDataL: this.initialData
// templates: `1.0/cda/serviciosproductosobtener/${id}/`
}
},
props: {
title: {
type: String,
default: 'Listado default'
},
endpoint: {
type: String,
default: {}
},
headers: {
type: Array
},
hidden: {
type: Boolean,
default: true
},
initialData: {
type: Boolean,
default: true
},
actions: {
type: Array,
default: () => [ { 'text': 'Seleccionar', 'icon': 'check_box', 'action': 'ver', 'color': 'primary' } ]
}
// endpoint: String
},
mounted () {
},
methods: {
re () {
return { template: `<template v-slot:item.countOrdenNoFacturada="{ item }">
<div class="">
<span v-if="item.countOrdenNoFacturada === 0" color="red">Abiertoooo</span>
<span v-else color="green"></span>
</div>
</template>
` }
},
async clickFunction (event, id) {
if (event === 'ver') this.$emit('getId', id)
if (event === 'editar') this.$router.push({ name: this.openRoute(`cda/formulario/${id}/`) })
if (event === 'crear') this.$router.push({ name: this.openRoute(`cda/formulario/${id}/`) })
},
emm (e) {
console.log('EMMIT', e)
this.initialDataL = e
}
}
}
</script>
You can use v-bind="$attrs". which means that all the attributed passed to the parent component will pass on to the child component.
so in your case use it like so:
parent component:
<data-table-search v-bind="$attrs">
and now you don't need to declare all of those props in the parent component.

Vuetify dialog component to correctly delete item from parent component

I have a delete dialog component that when clicking Cancel or Delete nothing happens, not even errors. What am I missing to correctly delete or cancel
<template>
<v-dialog
v-bind="$attrs"
v-on="$listeners"
max-width="500px"
persistent
>
<v-card>
<v-card-title
class="headline"
>
{{ title }}
</v-card-title>
<v-card-text>
{{ message }}
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="grey darken-1"
text
#click="$emit('closeDeleteDialog')"
>
Cancel
</v-btn>
<v-btn
color="primary darken-1"
text
#click="$emit('deleteItem')"
>
Delete
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
name: 'DeleteDialog',
props: {
title: {
type: String,
required: true
},
message: {
type: String,
default: ''
}
},
emits: ['closeDeleteDialog', 'deleteItem']
}
</script>
This is how I use my component:
<DeleteDialog
v-model="dialogDelete"
title="Delete location"
message="Are you sure you want to delete this location?"
/>
On the same view as where I import my component I have my methods.
export default {
components: {
DeleteDialog: () => import('#/components/Shared/DeleteDialog'),
},
data: () => ({
locationId: null,
dialog: false,
dialogDelete: false,
})
},
methods: {
deleteItem () {
this.$store.dispatch('deleteFirebaseDoc', { docId: this.locationId, collection: 'locations' })
this.locationId = null
this.dialogDelete = false
},
deleteItemConfirm (item) {
this.locationId = item.docId
},
closeDeleteDialog () {
this.dialogDelete = false
}
}
}
</script>
How can I correctly access my component to delete and item or cancel the dialog?
In the parent component you need to to listen to those events you emit
<DeleteDialog
v-model="dialogDelete"
title="Delete location"
message="Are you sure you want to delete this location?"
#close-delete-dialog="dialogDelete = false" // or call closeDeleteDialog ()
#delete-item="deleteItem()"
/>
I've never used camelCase for events emitting so I'd rather write it like $emit('close-delete-dialog') and $emit('delete-item')

Passing props from one step to another (steppers) in Vue

I am using steppers in vue. I am creating an agency in step 1 and on step 2 I am displaying the agency details. The issue is that the agency details don't appear on step 2 until I refresh page after going on step 2. Is there a way to pass agency as a prop from agency.vue to event.vue so that I dont need to refresh the page to make the agency details appear on step 2.
stepper.vue
<template>
<div >
<v-stepper v-model="e1">
<v-stepper-header>
<v-stepper-step :complete="e1 > 1" step="1">Agency</v-stepper-step>
<v-divider></v-divider>
<v-stepper-step :complete="e1 > 2" step="2">Event</v-stepper-step>
<v-divider></v-divider>
</v-stepper-header>
<v-stepper-items>
<v-stepper-content step="1">
<Agency #next="goTo(2, true)"></Agency>
</v-stepper-content>
<v-stepper-content step="2">
<Event/>
</v-stepper-content>
</v-stepper-items>
</v-stepper>
</div>
</template>
<script>
import Agency from 'parties/agency';
import Event from 'events/event';
export default {
components: {
Agency,
Event
},
data () {
return {
e1: 0,
agency: {
id: '',
name: '',
phone_number: ''
}
}
},
created() {
this.step = 1;
this.getAgency();
},
methods: {
getAgency() {
this.$axios.get('/agency.json')
.then(response => {
if (Object.keys(response.data).length > 0) {
this.agency = response.data;
}
})
},
goTo(step, can) {
if (can) {
this.e1 = step;
}
},
}
};
</script>
agency.vue
<template>
<v-card>
<v-form :model='agency'>
<v-layout row wrap>
<v-flex xs12 sm12 lg12 >
<v-layout row wrap>
<v-flex xs12 md6 class="add-col-padding-right">
<v-text-field
label='Agency Name'
v-model='agency.name'>
</v-text-field>
</v-flex>
</v-layout>
<v-layout row wrap>
<v-flex xs12 md6 class="add-col-padding-right">
<v-text-field
label='Agency Phone Number'
v-model='agency.phone_number'>
</v-text-field>
</v-flex>
</v-layout>
<v-layout row wrap>
<div>
<v-btn #click.prevent='saveAgency'>Save</v-btn>
</div>
</v-layout>
</v-flex>
</v-layout>
</v-form>
<v-btn #click.prevent='nextStep'>
Next
</v-btn>
</v-card>
</template>
<script>
export default {
data: function () {
return {
agency: {
id: '',
name: '',
phone_number: ''
}
};
},
created: function() {
this.getAgency();
},
methods: {
nextStep() {
this.$emit('next');
},
getAgency() {
this.$axios.get('/agency.json')
.then(response => {
if (Object.keys(response.data).length > 0) {
this.agency = response.data;
}
})
},
saveAgency() {
this.$axios.post('/agencies.json', { agency: this.agency })
.then(response => {
});
},
}
};
</script>
event.vue
<template>
<v-card class="mb-12">
<v-form :model='agency'>
{{ agency.name }}<br/>
{{ agency.phone_number }}<br/>
</v-form>
</v-card>
</template>
<script>
export default {
data: function () {
return {
agency: {
id: '',
name: '',
phone_number: ''
},
event_final: false
};
},
created: function() {
this.getAgency();
},
methods: {
getAgency() {
this.$axios.get('/agency.json')
.then(response => {
if (Object.keys(response.data).length > 0) {
this.agency = response.data;
if (this.agency.name === "XYZ") {
this.event_final = true;
}
}
}).
then(() => {
});
},
}
};
</script>
Have Agency emit the details in its next event, capture them at the parent and pass them as a prop to Event.
Given you load the initial data in stepper.vue, you can also pass that into Agency for editing
For example
// agency.vue
props: { editAgency: Object },
data () {
return { agency: this.editAgency } // initialise with prop data
},
methods: {
nextStep() {
this.$emit('next', { ...this.agency }) // using spread to break references
},
// etc, no need for getAgency() here though
}
<!-- stepper.vue -->
<Agency :edit-agency="agency" #next="nextAgency"></Agency>
<!-- snip -->
<Event :agency="agency" />
// stepper.vue
methods: {
nextAgency (agency) {
this.agency = agency
this.goTo(2, true)
},
// event.vue
export default {
props: { agency: Object } // no need for a local "data" copy of agency, just use the prop
}

Two vue components are sharing instance

I have a component named concepts, with its data and methods.
In another view I have two instance of this component (I'm using vuetify too):
// Index.vue
<template>
<v-card>
<v-toolbar>
<v-toolbar-title>Conceptos</v-toolbar-title>
</v-toolbar>
<v-tabs class="elevation-2" color="primary">
<v-tab>Ordinarios</v-tab>
<v-tab>Extraordinarios</v-tab>
<v-tab-item :key="1">
<concepts :is_extra="false" :key="1"></concepts>
</v-tab-item>
<v-tab-item :key="2">
<concepts :is_extra="true" :key="2"></concepts>
</v-tab-item>
</v-tabs>
</v-card>
</template>
<script>
import concepts from './Concepts.vue'
export default {
components: {
concepts,
},
data() {
return {
}
}
}
</script>
In the concepts component the is_extra property is used as a parameter, In the method created() of the vue instance, the function fetchAll retrieve from API the data correctly, the param is sent with the correct value:
// Concepts.vue
// this works correctly
created() {
const isExtra = this.is_extra //true or false depends of the property value
// GET /concepts?is_extra=true ...or false
this.server
.setParams({ is_extra: isExtra })
.fetchAll()
}
I have a method named upload (to upload a file) where I use this property again, but the value always is false, Always of the first component
...
// Concepts.vue
methods: {
upload() {
const isExtra = this.is_extra // always false
this.server.setParams({is_extra: isExtra}).upload()
}
}
The full Concepts.vue component here:
<template>
<v-data-table
:divider="true"
:headers="headers"
:items="concepts">
<template v-slot:top>
<v-toolbar flat color="white">
<v-spacer></v-spacer>
<template v-if="me.caps.canUploadCsv()">
<v-btn color="secondary" class="mr-2" dark>
Agregar concepto
</v-btn>
<input type="file" class="d-none" id="csv_file" #change="confirm" accept=".csv">
<v-btn #click="selectFile" :loading="uploading" color="green" dark>
<v-icon left>mdi-file-upload</v-icon> Cargar CSV
</v-btn>
</template>
</v-toolbar>
<v-dialog v-model="confirmDialog"
#click:outside="closeConfirm" #keydown="closeConfirm" max-width="320">
<v-card>
<v-card-title>
<span class="headline">Confirmar</span>
</v-card-title>
<v-card-text>
¿Desea continuar con la carga del archivo?
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text #click="closeConfirm">
No
</v-btn>
<v-btn color="primary" dark #click="uploadFile">Si</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<template v-slot:item.description="{ item }">
<span :title="item.description">
{{ item.description.substr(0, 60) }}...
</span>
</template>
<template v-slot:item.amount_formated="{ item }">
$ {{ item.amount_formated }}
</template>
<template v-slot:item.iva_formated="{ item }">
$ {{ item.iva_formated }}
</template>
<template v-slot:item.total_formated="{ item }">
$ {{ item.total_formated }}
</template>
</v-data-table>
</template>
<script>
import { bus } from '../../app'
import User from '../../models/User'
import Concept from '../../models/Concept'
export default {
props: ['is_extra'],
data() {
return {
me: null,
headers: [
{ text: 'Clave', value: 'code' },
{ text: 'Categoría', value: 'category' },
{ text: 'Descripción', value: 'description' },
{ text: 'Unidad', value: 'unity' },
{ text: 'Total', value: 'total_formated', align: 'end' },
],
concepts: [],
model: null,
loaded: false,
inputFile: null,
confirmDialog: false,
uploading: false,
}
},
created() {
this.me = (new User()).getMe()
const params = this.$route.params
this.model = new Concept(params.companyId, params.contractId)
const isExtra = this.is_extra
this.model.server
.setParams({ is_extra: isExtra })
.fetchAll(this.onSuccess)
},
mounted() {
this.inputFile = document.getElementById('csv_file')
console.log('1', this)
},
methods: {
selectFile() {
if (this.uploading) {
return
}
this.inputFile.click()
},
confirm() {
this.confirmDialog = true
},
closeConfirm() {
this.inputFile.value = null
this.confirmDialog = false
},
uploadFile() {
console.log(this)
this.confirmDialog = false
this.uploading = true
const isExtra = this.is_extra
bus.$emit('snackbar', { color: 'info', text: 'Subiendo archivo...' })
this.model.server
.setParams({ is_extra: isExtra })
.upload(null, 'csv', this.inputFile, this.onSuccess)
},
onSuccess(resp) {
if (this.uploading) {
bus.$emit('snackbar', {
color: 'success', text: 'Conceptos actualizados correctamente'
})
}
if (this.inputFile) {
this.inputFile.value = null
}
this.uploading = false
this.concepts = resp.data
},
}
}
</script>
I put a console.log(this) inside of the created and upload methods, when I change the tab (vuetify tabs), in the first case I get:
VueComponent {_uid: 43, _isVue: true, ...} // created(), is_extra=false
VueComponent {_uid: 71, _isVue: true, ...} // created(), is_extra=true
but in the upload method (when upload the file) I get the same instance:
VueComponent {_uid: 43, _isVue: true, ...} // upload() is_extra=false
VueComponent {_uid: 43, _isVue: true, ...} // upload() is_extra=false
I added :key property but it didn't work.
Vue devtools show the two components with its correct data.
Before using vuetify tabs I already had the issue
Can someone help me please?

vuetify autocomplete preload

i'm learning vuetify framework, i managed to use most of the components but i have a problem with autocomplete component.
Using it from scratch works fine, bug if i try to set value during creation it doesn't work at all.
I tried to extend one of the vuetify example but without success.
i would like to load first value from the API during creation but it stays empty.
Thanks for your help.
new Vue({
el: '#app',
data: () => ({
descriptionLimit: 60,
entries: [],
isLoading: false,
model: null,
search: "Cats"
}),
created :function() {
model={"API":"Cats","Description":"Pictures of cats from Tumblr","Auth":"","HTTPS":true,"Cors":"unknown","Link":"https://thecatapi.com/docs.html","Category":"Animals"},
search="Cats"
},
computed: {
fields () {
if (!this.model) return []
return Object.keys(this.model).map(key => {
return {
key: key,
value: this.model[key] || 'n/a'
}
})
},
items () {
return this.entries.map(entry => {
const Description = entry.Description.length > this.descriptionLimit
? entry.Description.slice(0, this.descriptionLimit) + '...'
: entry.Description
return Object.assign({}, entry, { Description })
})
}
},
watch: {
search (val) {
// Items have already been loaded
if (this.items.length > 0) return
this.isLoading = true
console.log("loadgin data")
// Lazily load input items
axios.get('https://api.publicapis.org/entries')
.then(res => {
const { count, entries } = res.data
this.count = count
this.entries = entries
})
.catch(err => {
console.log(err)
})
.finally(() => (this.isLoading = false))
}
}
})
<div id="app">
<v-app id="inspire">
<v-card
color="red lighten-2"
dark
>
<v-card-title class="headline red lighten-3">
Search for Public APIs
</v-card-title>
<v-card-text>
Explore hundreds of free API's ready for consumption! For more information visit
<a
class="grey--text text--lighten-3"
href="https://github.com/toddmotto/public-apis"
target="_blank"
>the Github repository</a>.
</v-card-text>
<v-card-text>
<v-autocomplete
v-model="model"
:items="items"
:loading="isLoading"
:search-input.sync="search"
color="white"
hide-no-data
hide-selected
item-text="Description"
item-value="API"
label="Public APIs"
placeholder="Start typing to Search"
prepend-icon="mdi-database-search"
return-object
></v-autocomplete>
</v-card-text>
<v-divider></v-divider>
<v-expand-transition>
<v-list v-if="model" class="red lighten-3">
<v-list-tile
v-for="(field, i) in fields"
:key="i"
>
<v-list-tile-content>
<v-list-tile-title v-text="field.value"></v-list-tile-title>
<v-list-tile-sub-title v-text="field.key"></v-list-tile-sub-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-expand-transition>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
:disabled="!model"
color="grey darken-3"
#click="model = null"
>
Clear
<v-icon right>mdi-close-circle</v-icon>
</v-btn>
</v-card-actions>
</v-card>
</v-app>
</div>
If this is the code you are trying to use then you are missing a ',' at the end of the "model".
created :function() {
model={...}, <----
search="Cats"
},
blalan05 on the discrod channel just give me the reply : my created function was garbled.
new Vue({
el: '#app',
data: () => ({
descriptionLimit: 60,
entries: [],
isLoading: false,
model: null,
search: "Cats"
}),
created () {
this.model = {"API":"Cats","Description":"Pictures of cats from Tumblr","Auth":"","HTTPS":true,"Cors":"unknown","Link":"https://thecatapi.com/docs.html","Category":"Animals"},
this.search = "Cats"
},
computed: {
fields () {
if (!this.model) return []
return Object.keys(this.model).map(key => {
return {
key: key,
value: this.model[key] || 'n/a'
}
})
},
items () {
return this.entries.map(entry => {
const Description = entry.Description.length > this.descriptionLimit
? entry.Description.slice(0, this.descriptionLimit) + '...'
: entry.Description
return Object.assign({}, entry, { Description })
})
}
},
watch: {
search (val) {
// Items have already been loaded
if (this.items.length > 0) return
this.isLoading = true
console.log("loadgin data")
// Lazily load input items
axios.get('https://api.publicapis.org/entries')
.then(res => {
const { count, entries } = res.data
this.count = count
this.entries = entries
})
.catch(err => {
console.log(err)
})
.finally(() => (this.isLoading = false))
}
}
})
<div id="app">
<v-app id="inspire">
<v-card
color="red lighten-2"
dark
>
<v-card-title class="headline red lighten-3">
Search for Public APIssss
</v-card-title>
<v-card-text>
Explore hundreds of free API's ready for consumption! For more information visit
<a
class="grey--text text--lighten-3"
href="https://github.com/toddmotto/public-apis"
target="_blank"
>the Github repositoryyy</a>.
</v-card-text>
<v-card-text>
<v-autocomplete
v-model="model"
:items="items"
:loading="isLoading"
:search-input.sync="search"
color="white"
hide-no-data
hide-selected
item-text="Description"
item-value="API"
label="Public APIs"
placeholder="Start typing to Search"
prepend-icon="mdi-database-search"
return-object
></v-autocomplete>
</v-card-text>
<v-divider></v-divider>
<v-expand-transition>
<v-list v-if="model" class="red lighten-3">
<v-list-tile
v-for="(field, i) in fields"
:key="i"
>
<v-list-tile-content>
<v-list-tile-title v-text="field.value"></v-list-tile-title>
<v-list-tile-sub-title v-text="field.key"></v-list-tile-sub-title>
</v-list-tile-content>
</v-list-tile>
</v-list>
</v-expand-transition>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
:disabled="!model"
color="grey darken-3"
#click="model = null"
>
Clear
<v-icon right>mdi-close-circle</v-icon>
</v-btn>
</v-card-actions>
</v-card>
</v-app>
</div>

Categories