How to set dates from the selected shortcut on Vue2-datepicker - vue.js

We have a calendar component based on the Vue2-datepicker library. There are some shortcuts defined on the date picker to enhance usability. Everything is working well except the time when the user selects the shortcut, the selected dates are not appearing on the datepickers.
Any helps will be appreciated.
import DatePicker from 'vue2-datepicker';
<DatePicker
lang="en"
placeholder="Range"
:shortcuts="shortcuts"
range v-model="rangefilter"
type="date"
class="input-sm"
input-class="form-control"
>
export default {
...
components: { DatePicker},
data(){
return {
shortcuts: [
{ text: 'Today', onClick: () => [new Date(), new Date()] },
{
text: 'Yesterday',
onClick: () => [new Date(Date.now() - 3600 * 1000 * 24), new Date()]
},
{
text: 'Last Week',
onClick: () => [new Date(Date.now() - 7 * 24 * 3600 * 1000), new Date()]
}],
...
}
The library version is ^3.10.4.

It is a bug in the library itself please refer to this link with an answer from the maintainer regarding the issue:
https://github.com/mengxiong10/vue2-datepicker/issues/400

Related

Update table when user clicked on pagination buttons

I have a vuetify table. Right now, I call API for all records and load all records into my table. I might not see the performance differences now because it's less than 1000. When I have 10,000,000 this might not work anymore. I need to only query for the first 10 on page loads, and my BE API already supports that. Then, I will make calls to API to get the next/previous 10 only when the user clicked on the pagination buttons.
data() {
return {
headers: [
{
text: 'Name',
align: 'start',
sortable: false,
value: 'name',
width: '20%'
},
{ text: 'Link Count', value: 'count', width: '10%' },
{ text: 'URL', value: 'details', width: '50%' },
{ text: 'Actions', value: 'id', width: '20%' }
],
items: []
}
},
and I set this.items to the response from API below :
axios.defaults.headers['Content-Type'] = 'application/json'
axios
.post(window.MTS_URL, vc_url_group)
.then((response) => {
this.items = response.data.groups
})
Can someone please get me started?
It's highly depends on your backend implementation, but there are some common points:
You should define v-data-table this way:
<v-data-table
:headers="headers"
:items="desserts"
:items-per-page="10"
:server-items-length="totalDesserts"
:options.sync="options"
></v-data-table>
So you need to operate with three variables:
desserts as a [1..10] rows on your current page,
totalDesserts as a total amount of rows (10,000,000 in your case)
options as a synchronize point of your pagination
These variables comes from an example of official docs.
After that, in order to track user click on pagination buttons, you need to define a deep watcher (it should be deep in order to react to changes in nested props):
watch: {
options: {
handler() {
this.getDessertsFromApi();
},
deep: true
},
},
And then - your API call method:
methods: {
async getDessertsFromApi() {
this.loading = true;
const { sortBy, sortDesc, page, itemsPerPage } = this.options;
// Your axios call should be placed instead
await this.simulateApiCall({
size: itemsPerPage,
page: page - 1,
})
.then((response) => {
this.desserts = response.data.content;
this.totalDesserts = response.data.totalElements;
})
.finally(() => {
this.loading = false;
});
},
}
Test this at CodePen with simulated API calls.

Disable key-bindings copy/paste in Vue

I am working for the first time in Vue. I have a form with an ID confirmation, I need to restrict the CTRL+C, CTRL+v, and right-click copy/paste inputs.
The code of the forms is something like this:
<script>
import { validationMixin } from 'vuelidate'
data () {
return {
form: {
id_number: '',
id_number_validation: '',
},
}
},
computed: {
chunkedForm () {
return chunk([
{ label: 'ID number',
model: 'id_number',
type: 'number',
event: null,
icon: 'assignment_ind' },
{ label: 'id number validation',
model: 'id_number_validation',
type: 'number',
event: null,
icon: 'assignment_ind' },
], 2)
},
today: function () {
let currenDate = new Date()
return currentDate.toISOString()
}
},
validations: {
form: {
id_number: {
numberSerializer,
required,
minLength: minLength(6),
maxLength: maxLength(11),
validDocumentNumber
},
id_number_validation: {
numberSerializer,
required,
minLength: minLength(6),
maxLength: maxLength(11),
validDocumentNumber,
sameAsDocumentNumber: sameAs('id_number')
},
},
},
</script>
I don't know if it's possible, I am searching for a Vue function that helps me to handle these key-bindings.
The code is only a sample, maybe it has some error but it gives an idea of the form structure and how I call it.
For avoiding the right click, you can just use something like #click.right.prevent which will block clicking on the element and its children.
You can disable Ctrl+C and Ctrl+V by binding the copy and paste events in the same way.
You can see a working example here, where the paragraph cannot be copied (well, unless you copy it from the source):
new Vue({
el: '#content',
data: {
},
methods: {
keydown: function(e) {
console.log(e)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="content">
<p #click.right.prevent #keydown="keydown" #copy.prevent #paste.prevent>
You shouldn't be able to copy me! <strong>Me Too!</strong>
</p>
</div>
https://codepen.io/lielfr/pen/RwaQJwm
However, a skilled user might be able to bypass these protections easily, so I'd think twice before showing a content you don't want the users to copy.

Trying to set xAxis extremes using highcharts-vue wrapper

I use the highcharts-vue wrapper (https://github.com/highcharts/highcharts-vue) to display a chart in my project. Now I'm dynamically adding new data to the already existing data.
(Originally I believe the addPoint function is used, but I already switched to pushing it to the series array.)
Now when I add one more element to data the selected/displayed range stays the same, but I wish to set it anchored to the right, so the new pushed data point appears (without having to shift the selected range manually).
In this highcharts example it's very much what I need, except for it not updating automatically at an interval, but on clicking a button.
https://www.highcharts.com/stock/demo/dynamic-update
I thought, I could work around it by using setExtremes on xAxis, but I get an error, that xAxis doesn't exist. Something like in this example.
http://jsfiddle.net/wkBwW/16/
So I think it's because I have to change the values differently or call an update differently because of the wrapped version.
<template>
<div class="chartcard">
<highcharts :constructor-type="'stockChart'" :options="chartOptions" ></highcharts>
<button v-on:click="addPoint" key="nextbutton"> add data </button>
</div>
</template>
<script>
import chartdatajson from "../assets/chart_data.json"
import {Chart} from 'highcharts-vue'
import Highcharts from 'highcharts'
import stockInit from 'highcharts/modules/stock'
stockInit(Highcharts);
let groupingUnits = [['week', [1]], ['month', [1]]];
function getOhlcAndVolume(data) {
...
}
let dataOhlcVolume = getOhlcAndVolume(chartdatajson);
export default {
name: 'ChartCard',
props: {
},
components: {
highcharts: Chart
},
methods: {
rescale: function(){
this.chartOptions.rangeSelector.selected = 0;
},
addPoint: function() {
let somedata = this.test_add_data.shift();
let ohls = somedata.slice(0,5);
let volume = [somedata[0], somedata[5]];
this.chartOptions.series[0].data.push(ohls);
this.chartOptions.series[1].data.push(volume);
// this.chartOptions.xAxis.max = somedata[0]
this.chartOptions.xAxis[0].setExtremes(1554048000000 , somedata[0])
},
},
data () {
return {
test_add_data: [[1556812800000,262,265,260.5,265,30200],[1557072000000,260,260,258,259,33688],[1557158400000,259.5,263,259,262.5,25686]],
chartOptions: {
rangeSelector: {
selected: 0
},
title: { text: 'Some Title' },
yAxis: [{...
}],
xAxis: {
},
series:
[{
type: 'candlestick',
name: 'AAPL',
data: dataOhlcVolume.ohlc,
dataGrouping: {
units: groupingUnits
}
}, {
type: 'column',
name: 'Volume',
data: dataOhlcVolume.volume,
yAxis: 1,
dataGrouping: {
units: groupingUnits
}
}]
}
}
}
}
</script>
Check this example with setting extremes in vue app: https://codesandbox.io/s/vue-template-nutgx. As you can see setExtremes() method has to be invoked on chart.xAxis instance.
You can get chart reference following this approach:
data() {
return {
chartOptions: {
chart: {
events: {
load: (function(self) {
return function() {
self.chart = this; // saving chart reference in the component
};
})(this)
}
},
...
}
};
}
This way you can reference xAxis.setExtremes() like that:
methods: {
setNewExtremes: function() {
this.chart.xAxis[0].setExtremes(1525872600000, 1528291800000);
}
}

Vuetify pagination howto change page property when rowsPerPage property changes

I'm having trouble figuring out how to change the vuetify pagination.page property back to 1, when a user changes the rowsPerPage property.
Say there are a total of 23 rows in a result set and rowsPerPage is currently set to 10. If the users goes to the 3rd (last) page and then selectes 50 rowsPerPage, vue calls my ajax query to get new data from the backend server, but it passes rowsPerPage as 50 and it passes page as 3.
Since this causes the sql offset property to be 100, which is way more than the 23 records in the table, it returns no data and so the screen re-renders with no records.
What I would like to do to fix this is, when the rowsPerPage property changes, reset the page property back to 1.
I have googled a bunch but cannot find the answer. Am I trying to solve this problem the wrong way?
Edit: Here is a sample of my rails view code:
<v-card flat>
<v-card-title class="pt-0 pb-0">
<h2>No RSP/Participating Apps</h2>
<%= render :partial => "search" %>
<%= render :partial => "rows_per_page" %>
</v-card-title>
<v-data-table
:headers="headers"
:items="results"
:pagination.sync="pagination"
hide-actions
:total-items="totalItems"
:must-sort=true
:search="pagination.search"
>
...
</v-data-table>
<div class="text-xs-center pt-2">
<v-pagination v-model="pagination.page" :length="pages"></v-pagination>
</div>
</v-card>
And my js code:
data: {
search: '',
drawer: null,
miniVariant: false,
loading: true,
totalItems: 0,
results: [],
pagination: {
rowsPerPage: 10,
},
rowsPerPageChoices: [
{ text: '2 rows per page', value: 2 },
{ text: '5 rows per page', value: 5 },
{ text: '10 rows per page', value: 10 },
{ text: '20 rows per page', value: 20 },
{ text: '30 rows per page', value: 30 }
],
},
methods: {
commonQueryParams() {
return '?sortBy=' + this.pagination.sortBy +
'&descending=' + this.pagination.descending +
'&page=' + this.pagination.page +
'&rowsPerPage=' + this.pagination.rowsPerPage +
'&onlyTotal=0' +
'&filter=' + this.search;
},
queryParams() {
return this.commonQueryParams();
},
getData() {
this.loading = true;
axios.get(this.dataApiUrl + '/' + this.dataEndPoint + this.queryParams(), {withCredentials: true})
.then(response => {
this.results = response.data.data;
this.totalItems = response.data.control_data.total;
this.loading = false;
});
},
},
computed: {
pages () {
return this.pagination.rowsPerPage ? Math.ceil(this.totalItems / this.pagination.rowsPerPage) : 0;
}
},
watch: {
pagination: {
handler () {
this.getData();
},
deep: true
},
search: _.debounce(function () {
this.getData()
}, 500),
}
So, I came up with a solution that appears to do what I want. First, I added a new data attribute:
oldRowsPerPage: 10
I set this to 10, because that is the default value for the pagination.rowsPerPage attribute.
Next, I changed the getData method to look like this:
getData() {
if (this.pagination.rowsPerPage != this.oldRowsPerPage) {
this.oldRowsPerPage = this.pagination.rowsPerPage;
this.pagination.page = 1;
}
this.loading = true;
axios.get(this.dataApiUrl + '/' + this.dataEndPoint + this.queryParams(), {withCredentials: true})
.then(response => {
this.results = response.data.data;
this.totalItems = response.data.control_data.total;
this.loading = false;
});
},
The conditional at the beginning of the method checks to see if the pagination.rowsPerPage value has been changed. If it has, we set the oldRowsPerPage attribute to the same value and then we change the pagination.page attribute to 1. This will cause the query to reset the offset so that it starts returning records from the beginning of the result set and it will also change the vuetify datatable so that it displays the first page of the result set instead of continuing to try to show whatever page the datatable was currently on when the user selected the new pagination.rowsPerPage choice.

Vue Js Components does not get destroyed?

I'm currently working on an very versatile dashboard to display various data.
For the frontend I'm using the latest nuxt and vue version.
My dashboard has many kinds of variations to display data (for example pie charts, line charts,...) these are described in components which are called dynamically.
The Problem is that when I browse from the Page "/" to another (for example "/foo") the interval gets fired again and crashes the app.
That happenes after the lifecycle hook destroyed. I tried to define the interval as an variable and stop it in the beforeDestroy hook but it did not help.
let interval= setInterval(this.fetchData.bind(null, configuration, connector), configuration.refreshTime)
/* later */
clearInterval(interval);
Do you see an error?
Thank you.
Thats the relevant code:
Template
<no-ssr>
<v-container grid-list-md>
<v-layout row wrap v-masonry transition-duration="0.5s" item-selector=".flex" column-width="#grid-sizer">
<v-flex xs1 sm1 md1 lg1 x1 id="grid-sizer"></v-flex>
<component :key="dashboardItem.id" v-for="(dashboardItem,index) in dashboardItems" :is="dashboardItem.type" :connector="dashboardItem.connector"
:type="dashboardItem.type" :configuration="dashboardItem.configuration" :id="dashboardItem.id" :index="index"></component>
</v-layout>
</v-container>
</no-ssr>
Script
import OnlyValue from '#/components/dashboardItems/OnlyValue.vue'
import TrafficLight from '#/components/dashboardItems/TrafficLight.vue'
import ChartAllHover from '#/components/dashboardItems/ChartAllHover.vue'
import PieChart from '#/components/dashboardItems/PieChart.vue'
import Section from '#/components/dashboardItems/Section.vue'
import Gauge from '#/components/dashboardItems/Gauge.vue'...
export default {
name: 'HomePage',
head () {
return {
title: "Dashboard"
}
},
computed: {
...mapGetters({
isAuthenticated: "users/isAuthentificated",
profileName: "profiles/name",
dashboardItems: "profiles/dashboardItems"
})
},
mounted() {
if (typeof this.$redrawVueMasonry === 'function') {
this.$redrawVueMasonry()
}
},
components: {
OnlyValue,
TrafficLight,
ChartAllHover,
PieChart,
Section,
Gauge
}
}
When calling a components it looks the following:
import dashboardItem from '~/mixins/dashboardItem'
export default {
name: "gauge",
mixins: [dashboardItem],
props: {
connector: {
type: String,
required: true
},
type: {
type: String,
required: true
},
configuration: {
type: Object,
required: true
},
id: {
type: Number,
required: true
},
index: {
type: Number,
required: true
}
},
data: () => ({
initOptions: {
renderer: 'svg'
},
options: {
tooltip: {
formatter: "{c}%"
},
series: [{
name: null,
type: 'gauge',
detail: {
formatter: '{value}%'
},
data: null
}]
},
isLoading: true
}),
methods: {
getData(configuration, connector) {
this.fetchData(configuration, connector)
setInterval(this.fetchData.bind(null, configuration, connector), configuration.refreshTime)
},
fetchData(configuration, connector) {
this.getSingleValue(configuration, connector)
.then(data => {
this.isLoading = false
let percent = (data.value / configuration.max) * 100
percent = Math.round(percent * 10) / 10
this.$nextTick(function () {
this.$refs.gauge.mergeOptions({
series: [{
name: data.title,
data: [{
value: percent,
name: data.title
}]
}]
})
})
this.$redrawVueMasonry()
})
.catch(e => console.log(e))
}
},
mounted () {
this.getData(this.configuration, this.connector)
}
}