I have a v-data-table and I'd like to make a method to change the attribute "loading" to false in a method.
Does anyone
<v-layout fluid v-resize="onResize" child-flex>
<v-data-table
loading=""
loading-text=""
:footer-props="{ 'items-per-page-options': [50, 100, 250, -1] }"
dense
dark
fixed-header
calculate-widths
:height="windowSize.y - 63 - 60"
:headers="headers"
:items="res"
class="elevation-1"
sort-by="publicationDate"
:sortDesc= "sortVal"
>
</v-layout>
know how to do that please ?
You can simply achieve it by doing a dynamic binding to loading attribute.
In template :
<v-data-table
...
:loading="loadTable"
>
In script :
data() {
return {
loadTable: true
}
}
mounted() {
// logic comes here and based on that loadTable boolean value can be assign.
this.loadTable = false;
}
You can try this approach as well
<template>
<!--
Bind the loading prop with the value declared on the data block
-->
<v-data-table
...
:loading="loadingValue">
...
</v-data-table>
</template>
<script>
export default {
data: () => ({
//initially set the loader value to false
loadingValue:false
}),
methods:{
//you can control your loading value in any function within this method block
getSomeDataTableInfo() {
/*If you will call this method many times you can reset the loading value to true
after completing your process you can set it back to false
*/
/*
Let us assume you have an async function which fetches data and returns a response
*/
this.loadingValue = true;
await myFunction.then((response)=>{
/**
* DO SOMETHING HERE
*/
}).catch((someErrors)=>{
/**
* DO SOMETHING HERE
*/
}).finally(()=>{
this.loadingValue = false;
})
}
}
}
</script>
<style>
</style>
Related
I'm currently working on a vue project using vuetify as our main component-library. We display some information with the v-data-table component and are redirecting to another view if a row is clicked. This works totally fine.
In user testing an unexpected behaviour occured. If the user tries to highlight the value of a column e.g. to copy the value, he is redirected as if the whole row is selected.
<template>
<v-data-table
:headers="columns"
:items="filteredPlannings"
:item-class="setDeactivationClass"
:items-per-page="itemsPerPage"
:custom-filter="searchFunction"
multi-sort
:loading="isFindPending"
:search="search"
loading-text="Loading... Please wait"
hide-default-footer
:page.sync="page"
#page-count="pageCount = $event"
#click:row="handleRowClick"
#pagination="pagination = $event"
>
</v-data-table>
</template>
<script>
export default {
setup(props, context) {
const {$router} = context.root;
const handleRowClick = ({ id }) =>
$router.push({ name: "ProjectDetails", params: { id } });
return {
handleRowClick,
}
}
}
</script>
You can manipulate with native window.getSelection() method to avoid this. Just prevent your router.push event emit when there's something in selection:
<v-data-table
...
#click:row="handleRowClick"
></v-data-table>
...
data () {
return {
...
lastSelectedItem: null,
}
},
methods: {
handleRowClick(item) {
if (window.getSelection().toString().length === 0) {
this.lastSelectedItem = item.name; //Use router.push here
}
}
}
Test this at CodePen.
But personally I don't think it's a good UX to use #click:row in your case. Possibly you should use #dblclick:row event, or create a special "Actions" column with a "Link to..." button.
I've been working on Vue project for almost a year, and I've just observed unexpected behavior below for the first time today...
Here is a link to code sandbox:
https://codesandbox.io/s/2bnem?file=/src/App.vue
And a code snippet from above link:
<template>
<div>
<div>{{a}}</div>
<div>{{translator(b)}}</div>
<input v-model="a" />
</div>
</template>
<script>
export default {
data() {
return {
a: 'a',
b: 'b',
}
},
computed: {
translator: function() {
return function(value) {
console.log(`translated...: ${value}`)
return value
}
}
}
}
</script>
Now every time I hit the key on input, the translator is triggered.
Is this a correct behavior?
If so, what is the cause of this problem or a background reasoning of this behavior?
Note that my vue version is 2.6.14(latest).
Your original issue is that you were attempting to use a method to render parts of your template. Methods used like this will execute for every update cycle, regardless of what changed.
The better solution is to use a computed property. Here's a somewhat dynamic example that wraps each of your data properties with a computed translator_x property
<template>
<div>
<div>{{ a }}</div>
<div>{{ translator_b }}</div>
<input v-model="a" />
</div>
</template>
<script>
const defaultData = {
a: "a",
b: "b"
}
export default {
data: () => ({ ...defaultData }),
computed: Object.fromEntries(Object.keys(defaultData).map(k => [
`translator_${k}`,
vm => {
console.log("translated...", k)
return vm[k]
}
]))
};
</script>
Each translator_x property will only be evaluated if the underlying data property is changed.
How do I make my vue.js component more general and reusable and can pass only the neccessary data?
this is what I want to build:
Structure:
the component has a header
the component has different inputs
the component has a submit button
the component has a list as a footer - that is passed depending on the input
My Approach
the parent
// App.vue
<template>
<div>
<!-- Component A -->
<SettingsCard
:listA="listA"
cardType="CompA"
>
<template v-slot:header>
Foo - Component A
</template>
</SettingsCard>
<!-- Component B -->
<SettingsCard
:listB="listB"
cardType="CompB"
>
<template v-slot:header>
Bar - Component B
</template>
</SettingsCard>
</div>
</template>
the child:
// SettingsCard.vue
<template>
<div>
<slot name="header"></slot>
<div v-if="cardType === 'CompA'">
<!-- Show input and submit button for component a -->
</div>
<div v-if="cardType === 'CompB'">
<!-- Show input and submit button for component b -->
</div>
<ListComponent
:cardType="cardType"
:list="computedList"
/>
</div>
</template>
<script>
export default {
props: {
cardType: String, // for the v-if conditions
listA: Array,
ListB: Array
},
data() {
return {
namefromCompA: '', // input from component A
namefromCompB: '' // input from component B
}
},
computed: {
computedList() {
// returns an array and pass as prop the the card footer
}
}
}
</script>
The problems
I have undefined props and unused data in my SettingsCard.vue component
// CompA:
props: {
cardType: 'compA',
listA: [1, 2, 3], // comes from the parent
listB: undefined // how to prevent the undefined?
}
// CompA:
data() {
return {
namefromCompA: 'hello world',
namefromCompB: '' // unused - please remove me
}
}
to use v-if="cardType === 'compA'" feels wrong
Do you have a better approach in mind to make this component reusable and remove anything unnecessary?
use a method instead of "cardType === 'CompA'".
just try this in your SettingsCard.vue
methods: {
showMeWhen(type) {
return this.cardType === type;
},
},
}
and your v-if render condition would be like:
v-if="showMeWhen('compA')"
update
for exmaple in your namefromCompA/B you can just pass a new prop to display the correct name.
props: {
cardType: String, // for the v-if conditions
listA: Array,
ListB: Array,
namefromComponent: {
type: String,
default: 'NoName'
}
},
then in your usage you just pass it like you do with the other props.
<SettingsCard
:listB="listB"
cardType="CompB"
namefrom-component="my Name for component B"
>
I have two methods in a vue component.
First makes the user choose from a v-select, either itemone or itemtwo. Then, to retreive the value for later i call #change to assign the variable to a method declared later - getItemValue.
Second is a submit button, when clicked, we go to handleSubmit.
After handleSubmit is called, I want to use the value I got from getItemValue (in variable theItem), but how can I call another method if it's out of my scope?
Mycomponent.vue
<template>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-select
v-model="select"
:items="items"
#change="getItemValue"
></v-select>
<v-btn
#click="handleSubmit"
>
Submit
</v-btn>
</v-form>
</template>
<script>
export default {
data: () => ({
items: [
'itemone',
'itemtwo'
],
}),
methods: {
getItemValue(theItem) {
},
handleSubmit(e) {
e.preventDefault()
// i need "theItem" here!
}
},
}
</script>
v-model already writes to your local variable, so there is absolutely no need to setup a get method to write the select value to a variable.
Actually, v-model is a bit more complicated than just 'write' to a variable, but the important bit is that in your template you are setting up v-model="select", which basically means that whenever the user uses the select to pick a value, your local select variable will be updated with the selected value.
Now, there is no select in your example component data, I don't know why. But if you had it, you could just sent that variable in your handleSubmit:
<template>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-select
v-model="select"
:items="items"
></v-select>
<v-btn
#click="handleSubmit"
>
Submit
</v-btn>
</v-form>
</template>
<script>
export default {
data: () => ({
select: '',
items: [
'itemone',
'itemtwo'
],
}),
methods: {
handleSubmit(e) {
e.preventDefault()
doSomethingWith(this.select); // this will be updated at this point
// with the option the user selected
}
},
}
</script>
Now, however, be aware that if the select variable is a component prop, then you should not do this right away, since props are not intended to be modified directly by child components. If that would be the case, please update your question with more info.
You would simple set the variable (theItem) value to the data
getItemValue(theItem) {
this.theItem;
},
and then retrieve it later
handleSubmit(e) {
e.preventDefault()
// i need "theItem" here!
// simple access theItem
console.log('theItem', this.theItem);
}
I have an component that I want to use on different pages. Well, it is working well till the first toggle. It shows like it used to, but when I click the 'Close' button, it closes, but console outputs :
[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: "visible"
found in
---> at src/components/Snackbar.vue
at src/views/Login.vue
And after that it doesn't show up on click
Any way to fix this?
Snackbar.vue
<template>
<v-snackbar v-model.sync="visible" :timeout="5000" bottom>
{{ content }}
<v-btn flat color="primary" #click.native="visible = false">Close</v-btn>
</v-snackbar>
</template>
<script>
export default {
name: 'snackbar',
props: [
'visible',
'content'
]
}
</script>
Login.vue
<template>
<div class="login">
<Snackbar :visible="snackbar.visible" :content="snackbar.content"></Snackbar>
</div>
</template>
<script>
import Snackbar from '#/components/Snackbar.vue'
export default {
components: {
Snackbar
},
data: function() {
return {
email: '',
password: '',
snackbar: {
visible: false,
content: ''
}
}
},
methods: {
login: function() {
if (this.email != '' && this.password != '') {
// Do something
} else {
this.snackbar.content = 'Fields can\'t be empty';
this.snackbar.visible = true;
}
}
}
}
</script>
The console error is being triggered by this:
#click.native="visible = false"
The component is directly mutating the incoming prop. If you want to keep this level of control where the parent component controls the visibility you'll have to do it by having the click event emit an event, which the parent component receives and sets this.snackbar.visible = false thereby triggering a prop change and the child component is hidden.
<Snackbar :visible="snackbar.visible" :content="snackbar.content"
v-on:requestClose="close"></Snackbar>
<v-btn flat color="primary" #click.native="$emit('requestClose')">Close</v-btn>
methods: {
close: function() {
this.snackbar.visible = false;
}
}