Using watchers in VueJS? - javascript

I am trying to use Watchers in VueJS but having bit of a hard time wrapping my head around them. For example in this case i have set up a watch which checks for the tab i am on and if the tab changes it should reset the selected value back to an empty array. Now i have set the parameters as oldValue and newValue but i am not quite sure how will i use those.
Check this codepen.
Here is the Complete example:-
new Vue({
el: "#app",
data() {
return {
tabs: ["Tab1", "Tab2"],
activeTab: 0,
headers: [{
text: "Dessert (100g serving)",
align: "left",
value: "name"
},
{
text: "Calories",
value: "calories"
}
],
items: [{
name: "Ice cream sandwich",
calories: 237
},
{
name: "Frozen Yogurt",
calories: 159
}
],
selected: []
};
},
methods: {
toggleAll() {
if (this.selected.length) this.items = [];
else this.selected = this.items.slice();
}
},
watch: {
activeTab: (oldValue, newValue) => {
if (oldValue !== newValue) {
this.selected = [];
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<div id="app">
<v-app id="inspire">
<v-tabs fixed-tabs v-model="activeTab">
<v-tab v-for="tab in tabs" :key="tab">
{{ tab }}
</v-tab>
<v-tab-item v-for="tab in tabs" :key="tab">
<v-data-table v-model="selected" :headers="headers" :items="items" select-all item-key="name" class="elevation-1" hide-actions>
<template v-slot:headers="props">
<tr>
<th>
<v-checkbox :input-value="props.all" :indeterminate="props.indeterminate" primary hide-details #click.stop="toggleAll"></v-checkbox>
</th>
<th v-for="header in props.headers" :key="header.text">
<v-icon small>arrow_upward</v-icon>
{{ header.text }}
</th>
</tr>
</template>
<template v-slot:items="props">
<tr :active="props.selected" #click="props.selected = !props.selected">
<td>
<v-checkbox :input-value="props.selected" primary hide-details></v-checkbox>
</td>
<td>{{ props.item.name }}</td>
<td>{{ props.item.calories }}</td>
</tr>
</template>
</v-data-table>
</v-tab-item>
</v-tabs>
</v-app>
</div>
I am not quite sure how i will use that watch so if someone can help me with that, i sure would appreciate it. Thank you.

You're using a fat arrow function for activeTab, it needs to be a normal function or it doesn't have a proper reference to this
Change to this:
activeTab: function (oldValue, newValue) { // function instead of =>
if (oldValue !== newValue) {
this.selected = [];
}
}
Also, there's an issue with your code when you use the check all box.
As a good rule of thumb, all top level functions should be declared using function. Only use => within the functions themselves when a nested function needs access to this

Related

How to sort elements in a column in v-data-table

I have a v-data-table and the elements in the first columns are some true and false values. By clicking the 'sortable' icon in the header I sort these elements in the column. I want to make that when I open the page, the 'true' values to be sorted first. How can I manage this?
headers: [
{
text: '',
sortable: true,
value: 'status',
}]
Html:
<v-data-table
:pagination.sync="pagination"
:headers="headers"
:items="items"
>
<template>
<tr>
<td>
<v-icon v-if="status">
{{status}}
</v-icon>
</td>
</tr>
</template>
</v-data-table>
Values of status are like: True, False, True, False, False
I want to sort the elements of the column as that: True True False False False
Firstly true values and than false.
You can use sort-by and sort-desc
<v-data-table
:pagination.sync="pagination"
:headers="headers"
:items="items"
sort-by="status"
:sort-desc="true"
>
<template>
<tr>
<td>
<v-icon v-if="status">
{{status}}
</v-icon>
</td>
</tr>
</template>
</v-data-table>
You can check more here
You can use external sorting using sortBy and sortDesc:
new Vue({
el: "#app",
vuetify: new Vuetify(),
data: () => ({
headers: [ { text: '', sortable: true, value: 'status' } ],
items: [ { status: "True" }, { status: "False" }, { status: "True" } ],
sortBy: "status",
sortDesc: true
})
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<v-app id="app">
<v-data-table
:headers="headers"
:items="items"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
>
<template v-slot:item.status="{ item }">
<tr>
<td>
<v-icon v-if="item.status">{{item.status}}</v-icon>
</td>
</tr>
</template>
</v-data-table>
</v-app>

Change checkbox icon when checked Vuejs Vuetify

So I am trying to check the checkbox Icon to something more custom, perhaps a different icon, however, I tried a couple different things like pseudo elements with no success. right now what this does is loops through data and displays the name and when checked highlights it based on the css class. What I am trying to do is when check change the checkbox icon.
css
.unchecked{
color: gray;
}
.checked{
background-color: #ffff00;
}
Vuejs Component.
Vue.component('check-list', {
template: `
<v-container>
<v-row >
<v-col >
<v-checkbox
v-for="item in values"
:key="item.id"
:value="item.id"
v-model="selected"
>
<template v-slot:label>
<div :class="selected.includes(item.id) ? 'checked' : 'unchecked'">
{{item.name}}
</div>
</template>
</v-checkbox>
</v-col>
</v-row>
<pre>{{selected}}</pre>
</v-container>
`,
data: function () {
return {
selected: [],
values: [
{id:'1',name:'Name 1'},
{id:'2', name:'Name 2'},
{id:'3', name:'Name 3'},
],
ex4: ['red']
}
},
methods: {
},
})
new Vue({
el: '#components-demo',
vuetify: new Vuetify({
icons: {
iconfont: 'md',
},
}),
data: () => ({
}),
})
#chewie, it is possible to change the checkbox icon in Vuetify, There is a specia prop to handle this
:on-icon="'icon-name'"
:off-icon="'icon-name'"
Please find the full code below
<div id="app">
<v-app id="inspire">
<v-container
class="px-0"
fluid
>
<v-row >
<v-col >
<v-checkbox
:on-icon="'mdi-heart'"
:off-icon="'mdi-home'"
v-for="item in values"
:key="item.id"
:value="item.id"
v-model="selected"
>
<template v-slot:label>
<div :class="selected.includes(item.id) ? 'checked' : 'unchecked'">
{{item.name}}
</div>
</template>
</v-checkbox>
</v-col>
</v-row>
<pre>{{selected}}</pre>
</v-container>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
selected: [],
values: [
{id:'1',name:'Name 1'},
{id:'2', name:'Name 2'},
{id:'3', name:'Name 3'},
],
ex4: ['red']
}
},
})
Please find the working codepen here:
https://codepen.io/chansv/pen/QWdEPvB?editors=1010

Toggle Hide/Show element VueJS?

I am using a Vuetify data table with header and item slots. Now i have a button which by default should be hidden but i only want to show it when a row is selected or when all of them are selected. I assumed i should be able to use the selected data property but that seems to stay empty if i select one row. So not sure how i can hide/show the button.
This is a working pen.
This is the code:-
new Vue({
el: "#app",
data: () => ({
pagination: {
sortBy: "name"
},
selected: [],
headers: [{
text: "Dessert (100g serving)",
align: "left",
value: "name"
},
{
text: "Calories",
value: "calories"
},
{
text: "Fat (g)",
value: "fat"
}
],
desserts: [{
name: "Frozen Yogurt",
calories: 159,
fat: 6.0
},
{
name: "Ice cream sandwich",
calories: 237,
fat: 9.0
}
]
}),
methods: {
toggleAll() {
if (this.selected.length) this.selected = [];
else this.selected = this.desserts.slice();
},
changeSort(column) {
if (this.pagination.sortBy === column) {
this.pagination.descending = !this.pagination.descending;
} else {
this.pagination.sortBy = column;
this.pagination.descending = false;
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<div id="app">
<v-app id="inspire">
<v-data-table v-model="selected" :headers="headers" :items="desserts" :pagination.sync="pagination" select-all item-key="name" class="elevation-1">
<template v-slot:headers="props">
<tr>
<th>
<v-checkbox :input-value="props.all" :indeterminate="props.indeterminate" primary hide-details #click.stop="toggleAll"></v-checkbox>
</th>
<th v-for="header in props.headers" :key="header.text" #click="changeSort(header.value)">
<v-icon small>arrow_upward</v-icon>
{{ header.text }}
</th>
</tr>
</template>
<template v-slot:items="props">
<tr :active="props.selected" #click="props.selected = !props.selected">
<td>
<v-checkbox :input-value="props.selected" primary hide-details></v-checkbox>
</td>
<td class="text-xs-center">{{ props.item.name }}</td>
<td class="text-xs-center">{{ props.item.calories }}</td>
<td class="text-xs-center">{{ props.item.fat }}</td>
</tr>
</template>
</v-data-table>
<v-container>
<v-layout>
<v-flex xs6 class="mt-5">
<v-btn>Hide by default but show on selected</v-btn>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>
Any help will be appreciated. Thank you.
you can use selected.length.
here is a computed you can add
computed: {
showBtn() {
return this.selected.length > 0
}
},
then use showBtn in your template
<v-btn v-if="showBtn">Hide by default but show on selected</v-btn>
You can also just use it inline, but I prefer using computed, because they cache the value, and make the template more readable
new Vue({
el: "#app",
data: () => ({
pagination: {
sortBy: "name"
},
selected: [],
headers: [
{
text: "Dessert (100g serving)",
align: "left",
value: "name"
},
{ text: "Calories", value: "calories" },
{ text: "Fat (g)", value: "fat" }
],
desserts: [
{
name: "Frozen Yogurt",
calories: 159,
fat: 6.0
},
{
name: "Ice cream sandwich",
calories: 237,
fat: 9.0
}
]
}),
computed: {
showBtn() {
return this.selected.length > 0
}
},
methods: {
toggleAll() {
if (this.selected.length) this.selected = [];
else this.selected = this.desserts.slice();
},
changeSort(column) {
if (this.pagination.sortBy === column) {
this.pagination.descending = !this.pagination.descending;
} else {
this.pagination.sortBy = column;
this.pagination.descending = false;
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-data-table v-model="selected" :headers="headers" :items="desserts" :pagination.sync="pagination" select-all item-key="name" class="elevation-1">
<template v-slot:headers="props">
<tr>
<th>
<v-checkbox :input-value="props.all" :indeterminate="props.indeterminate" primary hide-details #click.stop="toggleAll"></v-checkbox>
</th>
<th v-for="header in props.headers" :key="header.text" :class="['column sortable', pagination.descending ? 'desc' : 'asc', header.value === pagination.sortBy ? 'active' : '']" #click="changeSort(header.value)">
<v-icon small>arrow_upward</v-icon>
{{ header.text }}
</th>
</tr>
</template>
<template v-slot:items="props">
<tr :active="props.selected" #click="props.selected = !props.selected">
<td>
<v-checkbox :input-value="props.selected" primary hide-details></v-checkbox>
</td>
<td class="text-xs-center">{{ props.item.name }}</td>
<td class="text-xs-center">{{ props.item.calories }}</td>
<td class="text-xs-center">{{ props.item.fat }}</td>
</tr>
</template>
</v-data-table>
<v-container>
<v-container>
<v-layout>
<v-flex xs6 class="mt-5">
<v-btn v-if="showBtn">Hide by default but show on selected</v-btn>
</v-flex>
</v-layout>
</v-container>
</v-app>
</div>

Vue updates all button text after changing specific button

Hi everyone I'm playing around with Vue JS but for some how I cannot get what I expected. Below are my code.
Template
<div id="app">
<v-app id="inspire">
<div class="text-xs-center" v-for="x in count" :key="x">
<v-menu offset-y>
<v-btn
slot="activator"
color="primary"
dark
>
{{name}}
</v-btn>
<v-list>
<v-list-tile
v-for="(item, index) in items"
:key="index"
#click="test(item.title)"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</div>
</v-app>
</div>
Vue
new Vue({
el: '#app',
data: () => ({
name: 'default',
items: [
{ title: 'Click Me 1' },
{ title: 'Click Me 2' },
{ title: 'Click Me 3' },
{ title: 'Click Me 2' }
],
count: 10
}),
methods: {
test(title) {
this.name = title
}
}
})
What I want is that when I change a specific button text the other buttons should not be affected. But it seems my code is doing the opposite. What am I missing here? Any help, explanation would be much appreciated. Thanks
new Vue({
el: '#app',
data: () => ({
name: 'default',
items: [
{ title: 'Click Me 1' },
{ title: 'Click Me 2' },
{ title: 'Click Me 3' },
{ title: 'Click Me 2' }
],
count: 10
}),
methods: {
test(title) {
this.name = title
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.3/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.3/dist/vuetify.min.css">
<div id="app">
<v-app id="inspire">
<div class="text-xs-center" v-for="x in count" :key="x">
<v-menu offset-y>
<v-btn
slot="activator"
color="primary"
dark
>
{{name}}
</v-btn>
<v-list>
<v-list-tile
v-for="(item, index) in items"
:key="index"
#click="test(item.title)"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</div>
</v-app>
</div>
You are iterating over a normal number, in your example 10, so you are just showing 10 times the same variable name.
If you now change that variable name to something, it will change in all the buttons accordingly.
You need some way to save the different names, e.g. an array of objects like your items with all the titles.
I took your code and changed it a bit. Instead of iterating over a fixed count, I created an array of names and iterate over that array. When you click one of the buttons and change the text, instead of just changing the universal name attribute - you change the name at the position in the array.
new Vue({
el: '#app',
data: () => ({
names: [
{name: 'default 1'}, {name: 'default 2'}, {name: 'default 3'}, {name: 'default 4'}],
items: [
{ title: 'Click Me 1' },
{ title: 'Click Me 2' },
{ title: 'Click Me 3' },
{ title: 'Click Me 4' }
],
}),
methods: {
test(title, index) {
this.names[index].name = title
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.5.3/vuetify.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.5.3/vuetify.css.map">
<div id="app">
<v-app id="inspire">
<div class="text-xs-center" v-for="(x, index) in names" :key="'name' + index">
<v-menu offset-y>
<v-btn
slot="activator"
color="primary"
dark
>
{{x.name}}
</v-btn>
<v-list>
<v-list-tile
v-for="(item, i) in items"
:key="'item' + i"
#click="test(item.title, index)"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</div>
</v-app>
</div>

How to show and hide rows in v-data-table Vue.js + Vuetify

I'm new at Vue developer and I make a V-Data-Table consuming a JSON with vue-resource. That´s done already, but now I want to show/hide child rows based on user selection.
e.g.: click on '+' and the table shows the rows, click on '-'and the table hide the rows.
<template>
<v-app>
<div>
<v-flex xs12 sm6 d-flex>
<v-select
v-if="retornoDoJson"
:items="retornoDoJson.nations"
v-model="paisSelecionado"
label="Selecione Um"
v-on:change="selectionChanged"
single-line
menu-props="bottom"
></v-select>
</v-flex>
</div>
<div v-if="paisSelecionado">
<v-data-table :headers="headers" :items="retornoDoJson.top_10s[paisSelecionado]" hide-actions>
<template slot="items" slot-scope="props">
<v-btn fab dark color="indigo">
<v-icon dark>add</v-icon>
</v-btn>
<td class="parent-row">{{ props.item.name }}</td>
<td class="child-row">{{ props.item.club }}</td>
<td class="child-row">{{ props.item.phy }}</td>
<td class="parent-row">{{ props.item.age }}</td>
<td class="parent-row">{{ props.item.pas }}</td>
<td class="child-row">{{ props.item.nationality }}</td>
</template>
</v-data-table>
</div>
</v-app>
</template>
<script>
export default {
data() {
return {
paisSelecionado:undefined,
headers: [
{
text: ' ',
align: 'left',
sortable: false,
value: 'name'
},
{ text: 'name', value: 'name' },
{ text: 'club', value: 'club' },
{ text: 'phy', value: 'phy' },
{ text: 'age', value: 'age' },
{ text: 'pas', value: 'pas' },
{ text: 'nationality', value: 'nationality' }
],
retornoDoJson: []
};
},
methods: {
loadApi() {
this.$http
.get("http://localhost:8080/data.json")
.then(this.successCallback, this.errorCallback);
},
successCallback: function(resposta) {
this.retornoDoJson = resposta.data;
console.log(this.retornoDoJson);
},
selectionChanged: function() {
console.log("selecionarItem:this.selecionarPais:", this.paisSelecionado);
}
},
mounted() {
this.loadApi();
}
};
</script>
<style>
</style>
There's any way to do that?
If I understood you correctly, I hope this helps, also you can check a working example here: https://codepen.io/anon/pen/zemZoB
<template slot="items" scope="props">
<template v-if="props.item.show">
<td>{{ props.item.name }}</td>
<td class="text-xs-right">
<v-btn #click.native="props.item.show = false">
Hide
</v-btn>
</td>
</template>
<template v-else>
<v-btn #click.native="props.item.show = true">
Show
</v-btn>
</template>
</template>

Categories

Resources