How do I pass custom options into chartjs - vue.js

I'm trying to be able to add notes to my charts but i'm stuck on how I would pass my individual photon object into my chartOptions variable to be used in the tooltips label function?
<template>
<swiper-slide v-for="(photon,key) in $store.state.photons" :key='key'>
<line-chart width="80vw" :dataset="dataset" :library="chartOptions" class="animated slideInLeft delay-0.01s"
v-else :data="photon.data.rbChannel" ytitle="R/B Channel" :download="true"></line-chart>
<line-chart width="80vw" :dataset="dataset" :library="chartOptions" class="animated slideInRight delay-0.01s"
v-else :data="photon.data.tempF" ytitle="Temperature F" :colors="['#ff0000']" :download="true"></line-chart>
</swiper-slide>
</template>
chartOptions variable
chartOptions: {
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
console.log(data.labels[tooltipItem.index])
console.log(tooltipItem)
return data.labels[tooltipItem.index]
}
}
},
height: '400px',
pan: {
enabled: false,
mode: 'xy',
},
zoom: {
enabled: true,
mode: 'x',
},
drag: true,
gridLines: {
zeroLineColor: "rgba(0,255,0,1)"
}
}

Related

Child-modal is empty on every second click

I am using row-selected on Bootstrap table, so that when a user select a row in a table, the data of that table is passed to a new object which is then sent as a prop to a child component, which is a modal.
The issue is that when I first click on a row, the modal opens and shows the data correctly, but if I close the modal and click the same row again, the data object is empty and nothing gets shown. I need to click it again for a third time to see the data. Then if I click a fourth time, the data is gone again.
I don't understand it, because in my showModal method, I check first to see if selectedRows are empty or not, so it shouldn't open if there was no data.
Any idea why this happens?
Parent:
<template>
<b-container>
<b-card class="mt-4 mb-4">
<h5>{{ $t('errorLogs.errorLog') }}</h5>
<b-table
:items="completedTasks"
:fields="fields"
:per-page="[10, 25, 50]"
selectable
:select-mode="'single'"
#row-selected="onRowSelected"
#row-clicked="showModal"
include-actions
sort-desc
/>
</b-card>
<error-log-entry-modal ref="errorLogEntryModal" :selected-error-log="selectedRows"/>
</b-container>
</template>
<script>
import ErrorLogEntryModal from '#/components/error-log/ErrorLogEntryModal';
export default {
components: {
ErrorLogEntryModal,
},
data() {
return {
errors: null,
tasksCompleted: null,
selectedRows: []
};
},
computed: {
fields() {
return [
{
key: 'done',
label: '',
thStyle: 'width: 1%',
template: {
type: 'checkbox',
includeCheckAllCheckbox: true,
},
},
{
key: 'priority',
label: this.$t('errorLogs.priority'),
formatter: type => this.$t(`model.errors.types.${type}`),
sortable: true,
},
{
key: 'creationDateTime',
label: this.$t('creationDateTime'),
formatter: date => moment(date).locale(this.$i18n.locale).format('L'),
sortable: true,
},
{
key: 'stackTraceShort',
label: this.$t('errorLogs.stackTrace'),
sortable: true,
},
{
key: 'message',
label: this.$t('message'),
sortable: true
},
]
},
completedTasks(){
if(this.errors){
return this.errors.filter(log => log.done)
}
},
},
methods: {
load(){
errorService.getErrorLogs().then(result => {
result.data.forEach(log => log.stackTraceShort = log.stackTrace.substring(0,30));
this.errors = result.data
})
},
submit(){
this.$bvModalExt
.msgBoxYesNo(
this.$t('archiveErrorLog'),
{
title: this.$t('pleaseConfirm'),
okVariant: 'success'
}
).then(value => {
if (value) {
errorService.updateStatusOnErrorEntryLog(this.errors)
}
})
},
onRowSelected(fields){
this.selectedRows = fields
},
showModal(){
if (this.selectedRows) {
this.$refs.errorLogEntryModal.show()
}
},
},
created() {
this.load()
},
};
</script>
child:
<template>
<b-modal
modal-class="error-log-modal"
v-model="showModal"
ok-only
size="xl">
<b-row class="lg-12" v-for="log in selectedErrorLog">
<b-col class="ml-2 mr-2 mb-4">
<h4>{{ $t('errorLogs.errorMessage') }}</h4>
{{ log.message }}
</b-col>
</b-row>
<b-row class="lg-12">
<b-col class="ml-2 mr-2"><h4>{{ $t('errorLogs.stackTrace') }}</h4></b-col>
</b-row>
<b-row class="lg-12" v-for="log in selectedErrorLog">
<b-col class="ml-2 mr-2" style="word-break: break-word; background-color: #F5C9C1;">
{{ log.stackTrace }}
</b-col>
</b-row>
</b-modal>
</template>
<script>
export default {
props: {
selectedErrorLog: Array
},
data() {
return {
showModal: false,
};
},
methods: {
show(){
this.showModal = true
},
}
};
</script>

How to remove active classes from all buttons before making another one active?

I'm new to vue, I still don't understand everything, tell me. I have buttons that I display through v-for, I need to get the active class of only one button when pressed, all the others need to be turned off, tell me, preferably visually, how can I do it better?
I am using the method activeBtn, but this doesn't turn off the active class from the previous buttons
activeBtn(event, index) {
this.buttons[index].isActive = !this.buttons[index].isActive;
<script>
data() {
return {
buttons: [
{
label: "A",
isActive: false,
type: "border-left",
name: "BorderLeftComonent",
},
{
label: "A",
isActive: false,
type: "text-balloon",
name: "TextBalloonComponent"
},
{
label: "A",
isActive: false,
type: "dashed",
name: "DashedComponent"
},
],
};
},
methods: {
activeBtn(event, index) {
this.buttons[index].isActive = !this.buttons[index].isActive;
}
</script>
<template>
<div id="btn-box">
<button
v-for="(button, index) in buttons"
:key="index"
:class="button.isActive ? 'on' : 'off'"
#click="component = button.name, activeBtn($event, index)">
<div :class="`btn btn-${button.type}`">{{ button.label }}</div>
</button>
</div>
</template>
Since you only want to get one active button at any one point, it doesn't make sense to manage the active state inside each button.
Instead, you should manage it at group level by storing the currently selected button's id. A button would then be active when its id matches the currently selected id.
Here's an example:
new Vue({
el: '#app',
data: () => ({
buttons: [
{
id: "button-1",
label: "A",
type: "border-left",
name: "BorderLeftComonent"
},
{
id: "button-2",
label: "A",
type: "text-balloon",
name: "TextBalloonComponent"
},
{
id: "button-3",
label: "A",
type: "dashed",
name: "DashedComponent"
}
],
activeButtonId: "button-1"
}),
methods: {
activate(id) {
this.activeButtonId = id;
}
}
})
.on {
background-color: red
}
.off {
background-color: blue
}
.on, .off {
color: white
}
<script src="https://unpkg.com/vue#2/dist/vue.min.js"></script>
<div id="app">
<div>
<button
v-for="{id, type, label} in buttons"
:key="id"
:class="activeButtonId === id ? 'on' : 'off'"
#click="activate(id)"
>
<div :class="`btn btn-${type}`" v-text="label" />
</button>
</div>
</div>

Google Maps showing grey box in Vue modal

I have a <b-modal> from VueBootstrap, inside of which I'm trying to render a <GmapMap> (https://www.npmjs.com/package/gmap-vue)
It's rendering a grey box inside the modal, but outside the modal it renders the map just fine.
All the searching I've done leads to the same solution which I'm finding in some places is google.maps.event.trigger(map, 'resize') which is not working. Apparently, it's no longer part of the API [Source: https://stackoverflow.com/questions/13059034/how-to-use-google-maps-event-triggermap-resize]
<template>
<div class="text-center">
<h1>{{ title }}</h1>
<div class="row d-flex justify-content-center">
<div class="col-md-8">
<GmapMap
ref="topMapRef"
class="gmap"
:center="{ lat: 42, lng: 42 }"
:zoom="7"
map-type-id="terrain"
/>
<b-table
bordered
dark
fixed
hover
show-empty
striped
:busy.sync="isBusy"
:items="items"
:fields="fields"
>
<template v-slot:cell(actions)="row">
<b-button
size="sm"
#click="info(row.item, row.index, $event.target)"
>
Map
</b-button>
</template>
</b-table>
<b-modal
:id="mapModal.id"
:title="mapModal.title"
#hide="resetInfoModal"
ok-only
>
<GmapMap
ref="modalMapRef"
class="gmap"
:center="{ lat: 42, lng: 42 }"
:zoom="7"
map-type-id="terrain"
/>
</b-modal>
</div>
</div>
</div>
</template>
<script>
// import axios from "axios";
import { gmapApi } from 'gmap-vue';
export default {
name: "RenderList",
props: {
title: String,
},
computed: {
google() {
return gmapApi();
},
},
updated() {
console.log(this.$refs.modalMapRef);
console.log(window.google.maps);
this.$refs.modalMapRef.$mapPromise.then((map) => {
map.setCenter(new window.google.maps.LatLng(54, -2));
map.setZoom(2);
window.google.maps.event.trigger(map, 'resize');
})
},
data: function () {
return {
items: [
{ id: 1, lat: 42, long: 42 },
{ id: 2, lat: 42, long: 42 },
{ id: 3, lat: 42, long: 42 },
],
isBusy: false,
fields: [
{
key: "id",
sortable: true,
class: "text-left",
},
{
key: "text",
sortable: true,
class: "text-left",
},
"lat",
"long",
{
key: "actions",
label: "Actions"
}
],
mapModal: {
id: "map-modal",
title: "",
item: ""
}
}
},
methods: {
// dataProvider() {
// this.isBusy = true;
// let promise = axios.get(process.env.VUE_APP_LIST_DATA_SERVICE);
// return promise.then((response) => {
// this.isBusy = false
// return response.data;
// }).catch(error => {
// this.isBusy = false;
// console.log(error);
// return [];
// })
// },
info(item, index, button) {
this.mapModal.title = `Label: ${item.id}`;
this.mapModal.item = item;
this.$root.$emit("bv::show::modal", this.mapModal.id, button);
},
resetInfoModal() {
this.mapModal.title = "";
this.mapModal.content = "";
},
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1 {
margin-bottom: 60px;
}
.gmap {
width: 100%;
height: 300px;
margin-bottom: 60px;
}
</style>
Does anyone know how to get the map to display properly in the modal?
Surely, I'm not the first to try this?
Had this problem, in my case it was solved by providing the following options to google maps:
mapOptions: {
center: { lat: 10.365365, lng: -66.96667 },
clickableIcons: false,
streetViewControl: false,
panControlOptions: false,
gestureHandling: 'cooperative',
mapTypeControl: false,
zoomControlOptions: {
style: 'SMALL'
},
zoom: 14
}
However you can probably make-do with just center and zoom.
Edit: Try using your own google maps components, follow this tutorial:
https://v2.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html#Base-Example
You can use the package described in the tutorial to load the map, dont be scared by the big red "deprecated" warning on the npm package page.
However for production, you should use the package referenced by the author, which is the one backed by google:
https://googlemaps.github.io/js-api-loader/index.html
The only big difference between the two:
The 'google' object is not returned by the non-deprecated loader, it is instead attached to the window. See my answer here for clarification:
'google' is not defined Using Google Maps JavaScript API Loader
Happy coding!

How can I make vue-awesome-swiper loop normally and display the pagination?

Recently,I used vue-awesome-swiper in my project.Here are my codes from the HomeSwiper.vue file:
//HTML
<template>
<swiper :options="swiperOption" ref="mySwiper">
<swiper-slide v-for="(item,index) in banners" :key="index">
<a :href="item.link">
<img :src="item.image" alt="">
</a>
</swiper-slide>
<div class="swiper-pagination" v-for="(item,index) in banners" :key="index" slot="pagination">
</div>
</swiper>
</template>
// JS
import {swiper,swiperSlide} from 'vue-awesome-swiper'
require("swiper/dist/css/swiper.css");
export default {
name:'HomeSwiper',
props:{
banners:{
type:Array,
default(){
return []
}
}
},
components:{
swiper,
swiperSlide
},
data() {
const that = this
return {
imgIndex: 1,
swiperOption: {
notNextTick: true,
loop: true,
initialSlide: 0,
autoplay: {
delay: 1500,
stopOnLastSlide: false,
disableOnInteraction: true
},
speed: 800,
direction: "horizontal",
grabCursor: true,
on: {
slideChangeTransitionStart: function() {
that.imgIndex= this.realIndex - 1;
},
},
pagination: {
el: ".swiper-pagination",
clickable: true,
type: "bullets"
}
}
}
},
computed: {
swiper() {
return this.$refs.mySwiper.swiper;
}
}
}
The code snippet looks normally.But I met 2 strange problems:
I found the images can't loop normally,which always stop when reached the last image even though I have set loop to true in the script.
The pagination can't display
So I went to github issues for help.And Somenone who met the same problems solve these by using v-if="banners.length!=0" in swiper element.But I am confused why these 2 problems are something to do with banners'length?Can someone explain this?
It seems like you are missing the <v-content> tag from your view.
Your entire view should be wrapped with a <v-content> to ensure proper sizing. You can see the documentation here: https://vuetifyjs.com/en/components/application/
Template:
<template>
<v-content>
<Swiper :options="swiperOption">
<Swiper-Slide v-for="(item,index) in srcs" :key="index">
<img class="slideImage" :src="require(`#/static/vuetifypro-pages/${ item }`)" :key="index" alt="">
</Swiper-Slide>
<div class="swiper-pagination swiper-pagination-white" slot="pagination"></div>
</Swiper>
</v-content>
</template>
Script:
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper';
export default {
name:'HomeSwiper',
components:{
Swiper,
SwiperSlide
},
data() {
return {
srcs: {
1: 'Homepage_blur.jpg',
2: 'Login_bg.jpg',
3: 'home_huddle.jpg',
},
swiperOption: {
notNextTick: true,
loop: true,
initialSlide: 0,
autoplay: {
delay: 1500,
disableOnInteraction: true
},
speed: 800,
grabCursor: true,
pagination: {
el: ".swiper-pagination",
clickable: true,
type: "bullets"
}
}
}
},
}
</script>

Change background of v-data-table row on event from child component

I have an expanding data table in my parent component and a child component inside the expanded row with a button. I would like to change the background color of the associated row when I click the button inside the child component. I'm not sure how to target the row to add the css class on event.
ScanGrid(parent):
<template>
<v-flex v-if="items.length === 0">
<ScanAdd #selectBatch="showScan" />
</v-flex>
<v-card v-else class="ma-5">
<v-card-text>
<v-layout align-center>
<v-data-table
:headers="headers"
:items="items"
item-key="StorageName"
show-expand
single-expand
:expanded="expanded"
hide-default-footer
#click:row="clickedRow"
>
<template
#isDeleted="deleteRow"
v-if="groupBy === 'barCode'"
v-slot:expanded-item="{ item }"
>
<td :colspan="12">
<ScanGridCode :item="item" />
</td>
</template>
<template v-else v-slot:expanded-item="{ item }">
<td :colspan="12">
<ScanGridDef :item="item" />
</td>
</template>
</v-data-table>
</v-layout>
</v-card-text>
</v-card>
</template>
<script>
import { API } from "#/api";
import ScanAdd from "./ScanAdd";
import ScanGridCode from "./ScanGridCode";
import ScanGridDef from "./ScanGridDef";
export default {
name: "ScanGrid",
props: {
items: {
type: Array,
required: true
}
},
components: {
ScanGridCode,
ScanGridDef,
ScanAdd
},
methods: {
deleteRow(value) {
this.isDeleted = value;
},
showScan(value) {
this.selectedId = value;
this.addScanBatch(value);
this.$emit("processingBatch", true);
this.processingBatch = true;
},
async addScanBatch(Id) {
const selectedItems = await API.getPhysicalInventoryBatch(Id);
if (selectedItems.data.Id === this.selectedId) {
this.items = selectedItems.data.Locations;
}
},
clickedRow(value) {
if (
this.expanded.length &&
this.expanded[0].StorageName == value.StorageName
) {
this.expanded = [];
} else {
this.expanded = [];
this.expanded.push(value);
}
}
},
data: () => ({
isDeleted: false,
groupBy: "barCode",
expanded: [],
items: [],
toDelete: "",
totalResults: 0,
loading: true,
headers: [
{
text: "Localisation",
sortable: true,
value: "StorageName",
class: "large-column font-weight"
},
{
text: "Paquets scannés",
sortable: true,
value: "ScannedProduct",
class: "large-column font-weight"
},
{
text: "Paquets entrants",
sortable: true,
value: "Incoming",
class: "large-column font-weight"
},
{
text: "Paquets sortants",
sortable: true,
value: "Outgoing",
class: "large-column font-weight"
},
{
text: "Paquets inconnus",
sortable: true,
value: "Unknown",
class: "large-column font-weight"
}
]
})
};
</script>
ScanGridCode(child):
<template>
<div class="codeContainer">
<div class="cancelLocation">
<v-flex class="justify-center">
<v-btn class="ma-5" large color="lowerCase" tile #click="deleteLocation"
>Annuler le dépôt de cette localisation</v-btn
>
</v-flex>
</div>
</div>
</template>
<script>
export default {
name: "ScanGridCode",
props: {
item: {
type: Object,
required: true
}
},
methods: {
deleteLocation() {
this.item.IsDeleted = true;
this.$emit("IsDeleted", true);
}
},
data: () => ({
IsDeleted: false,
groupBy: 0,
headersGroupCode: [
{
text: "Code barre",
sortable: true,
value: "SerialNumber",
class: "large-column font-weight-light"
},
{
text: "De",
sortable: true,
value: "FromLocation",
class: "large-column font-weight-light"
},
{
text: "Vers",
sortable: true,
value: "ToLocation",
class: "large-column font-weight-light"
}
]
})
};
</script>
I use Vuetify 2.1.7 and Vue 2.6.10. When I click on the button I call deleteLocation function. I assume I need to $emit a value to my parent but after that I don't know how to target the tr to change its style.
Since you're using Vuex, I would suggest using some variable such as store.state.selectedRow to keep track of whether or not a row has been selected (or in cases where there are more than one row, which row has been selected). Then you can have a computed property myProperty = this.$store.state.selectedRow in your Vue component which will automatically reflect the single source of truth, and your conditional class can be bound to this myProperty. This means you don't need to worry about emitting on events.
The approach to emitting the event is what should be done. So I am assuming you will emit from deleteLocation function.
Since you need a custom styling on rows you need to add the items slot and add your logic there
<template v-slot:item="{ item, select}">
<tr :class="key === coloredRow ? 'custom-highlight-row' : ''">
<td :colspan="12">
<ScanGridCode #changeColor="changeColor(key)" :item="item" />
</td>
//add this method to your script element
changeColor(idx) {
this.coloredRow = idx;
}