Related
I don't understand my console log is printing all the data. Do i have to return the data to something for the data to update on the chart? Maybe its something with nuxt? I have tried async mounted and async fetch with the same result. I have tried putting a $ infront of this.$chartData.
<script>
import { Pie } from 'vue-chartjs/legacy'
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale } from 'chart.js'
import EthplorerService from '../../services/EthplorerService'
import CoinGeckoService from '../../services/CoinGeckoService'
ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale)
export default {
name: 'PieChart',
components: {
Pie,
},
data() {
return {
chartData: {
labels: [],
datasets: [
{
data: [],
backgroundColor: ['#0074D9', '#FF4136', '#2ECC40', '#39CCCC', '#01FF70', '#85144b', '#F012BE', '#3D9970', '#111111', '#AAAAAA'],
},
],
},
chartOptions: {
responsive: true,
maintainAspectRatio: false,
},
}
},
async created() {
const limit = 10
try {
this.loading = true
const coinData = await CoinGeckoService.getCoinData('chainlink')
const contractAddress = coinData.data.platforms.ethereum
const { data } = await EthplorerService.getTopTokenHolders(contractAddress, limit)
console.log(data.holders.map((x) => x.address))
console.log(data.holders.map((x) => x.share))
this.chartData.labels = data.holders.map((x) => x.address)
this.chartData.datasets.data = data.holders.map((x) => x.share)
this.loading = false
return this.chartData
} catch (e) {
this.loading = false
console.log(e)
}
},
}
</script>
Turns out i was missing the array position [0]. And I was missing the v-if.
this.chartData.datasets[0].data = data.holders.map((x) => x.share)
Followed the examples here and here
<template>
<Pie
v-if="!loading"
:chart-options="chartOptions"
:chart-data="chartData"
:chart-id="chartId"
:dataset-id-key="datasetIdKey"
:plugins="plugins"
:css-classes="cssClasses"
:styles="styles"
:width="width"
:height="height"
/>
</template>
<script>
import { Pie } from 'vue-chartjs/legacy'
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement, CategoryScale } from 'chart.js'
import EthplorerService from '../../services/EthplorerService'
import CoinGeckoService from '../../services/CoinGeckoService'
ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale)
export default {
name: 'PieChart',
components: {
Pie,
},
props: {
chartId: {
type: String,
default: 'pie-chart',
},
datasetIdKey: {
type: String,
default: 'label',
},
width: {
type: Number,
default: 400,
},
height: {
type: Number,
default: 400,
},
cssClasses: {
default: '',
type: String,
},
styles: {
type: Object,
default: () => {},
},
plugins: {
type: Array,
default: () => [],
},
},
data() {
return {
loading: true,
chartData: {
labels: [],
datasets: [
{
data: [],
backgroundColor: ['#0074D9', '#FF4136', '#2ECC40', '#39CCCC', '#01FF70', '#85144b', '#F012BE', '#3D9970', '#111111', '#AAAAAA'],
},
],
},
chartOptions: {
responsive: true,
maintainAspectRatio: false,
},
}
},
async mounted() {
const limit = 10
try {
this.loading = true
const coinData = await CoinGeckoService.getCoinData('chainlink')
const contractAddress = coinData.data.platforms.ethereum
const { data } = await EthplorerService.getTopTokenHolders(contractAddress, limit)
console.log(data.holders.map((x) => x.address.slice(1, 5)))
console.log(data.holders.map((x) => x.share))
this.chartData.labels = data.holders.map((x) => x.address.slice(2, 7))
this.chartData.datasets[0].data = data.holders.map((x) => x.share)
this.loading = false
} catch (e) {
this.loading = false
console.log(e)
}
},
}
</script>
so my problem is that I can't seem to make use of the "Mentions" functionality of tiptap inside a vuetify-nuxt project.
The original example can be found here
More useful info:
Example implementation from the tiptap github here
Similar question asked and not answered fully here
Similar question asked in the vuetify integration library here
To do that I'm trying to combine documentation examples.
I guess it goes wrong around the time I try to use "tippy", which is the css library used to display the popup that actually lists users to pick from (after the #), but I can't seem to understand the real issue.
So when I type # the keydown/up event listeners are functioning, but the tippy seems to not bind the popup successfully (it's not displayed), and the following error occurs:
Editor.vue?6cd8:204 Uncaught TypeError: Cannot read property '0' of undefined
at VueComponent.enterHandler (Editor.vue?6cd8:204)
at onKeyDown (Editor.vue?6cd8:175)
at Plugin.handleKeyDown (extensions.esm.js?f23d:788)
at eval (index.es.js?f904:3298)
at EditorView.someProp (index.es.js?f904:4766)
at editHandlers.keydown (index.es.js?f904:3298)
This is my tippy.js nuxt plugin:
import Vue from "vue";
import VueTippy, { TippyComponent } from "vue-tippy";
Vue.use(VueTippy, {
interactive: true,
theme: "light",
animateFill: false,
arrow: true,
arrowType: "round",
placement: "bottom",
trigger: "click",
// appendTo: () => document.getElementById("app")
});
Vue.component("tippy", TippyComponent);
This is the component in which I'm trying to show the editor and the suggestions/mentiosn functionality:
<template>
<div>
<div class="popup">
aaaa
</div>
<editor-menu-bar v-slot="{ commands }" :editor="editor">
<div class="menubar">
<v-btn class="menubar__button" #click="commands.mention({ id: 1, label: 'Fred Kühn' })">
<v-icon left>#</v-icon>
<span>Mention</span>
</v-btn>
</div>
</editor-menu-bar>
<tiptap-vuetify v-model="localValue" :extensions="extensions" :native-extensions="nativeExtensions" :toolbar-attributes="{ color: 'grey' }" #init="onInit" />
</div>
</template>
<script>
// import the component and the necessary extensions
import {
TiptapVuetify,
Heading,
Bold,
Italic,
Strike,
Underline,
Code,
CodeBlock,
Image,
Paragraph,
BulletList,
OrderedList,
ListItem,
Link,
Blockquote,
HardBreak,
HorizontalRule,
History,
} from "tiptap-vuetify";
// TESTING
import { EditorMenuBar, Editor } from "tiptap";
import { Mention } from "tiptap-extensions";
import tippy, { sticky } from "tippy.js";
export default {
components: { TiptapVuetify, EditorMenuBar },
props: {
value: {
type: String,
default: "",
},
},
data: () => ({
editor: null,
extensions: null,
nativeExtensions: null,
// TESTING
query: null,
suggestionRange: null,
filteredUsers: [],
navigatedUserIndex: 0,
insertMention: () => {},
popup: null,
}),
computed: {
localValue: {
get() {
return this.value;
},
set(value) {
this.$emit("input", value);
},
},
// TESTING
hasResults() {
return this.filteredUsers.length;
},
showSuggestions() {
return this.query || this.hasResults;
},
},
created() {
this.extensions = [
History,
Blockquote,
Link,
Underline,
Strike,
Italic,
ListItem,
BulletList,
OrderedList,
[
Heading,
{
options: {
levels: [1, 2, 3],
},
},
],
Bold,
Link,
Code,
CodeBlock,
Image,
HorizontalRule,
Paragraph,
HardBreak,
];
this.nativeExtensions = [
// https://github.com/ueberdosis/tiptap/blob/main/examples/Components/Routes/Suggestions/index.vue
new Mention({
// a list of all suggested items
items: async () => {
await new Promise((resolve) => {
setTimeout(resolve, 500);
});
return [
{ id: 1, name: "Sven Adlung" },
{ id: 2, name: "Patrick Baber" },
{ id: 3, name: "Nick Hirche" },
{ id: 4, name: "Philip Isik" },
{ id: 5, name: "Timo Isik" },
{ id: 6, name: "Philipp Kühn" },
{ id: 7, name: "Hans Pagel" },
{ id: 8, name: "Sebastian Schrama" },
];
},
// When # is pressed, we enter here
onEnter: ({ items, query, range, command, virtualNode }) => {
this.query = query; // the field that the # queries? currently empty
this.filteredUsers = items;
this.suggestionRange = range;
this.renderPopup(virtualNode); // render popup - failing
this.insertMention = command; // this is saved to be able to call it from within the popup
},
// probably when value after # is changed
onChange: ({ items, query, range, virtualNode }) => {
this.query = query;
this.filteredUsers = items;
this.suggestionRange = range;
this.navigatedUserIndex = 0;
this.renderPopup(virtualNode);
},
// mention canceled
onExit: () => {
// reset all saved values
this.query = null;
this.filteredUsers = [];
this.suggestionRange = null;
this.navigatedUserIndex = 0;
this.destroyPopup();
},
// any key down during mention typing
onKeyDown: ({ event }) => {
if (event.key === "ArrowUp") {
this.upHandler();
return true;
}
if (event.key === "ArrowDown") {
this.downHandler();
return true;
}
if (event.key === "Enter") {
this.enterHandler();
return true;
}
return false;
},
// there may be built-in filtering, not sure
onFilter: async (items, query) => {
await console.log("on filter");
},
}),
];
},
methods: {
// TESTING
// navigate to the previous item
// if it's the first item, navigate to the last one
upHandler() {
this.navigatedUserIndex =
(this.navigatedUserIndex + this.filteredUsers.length - 1) %
this.filteredUsers.length;
},
// navigate to the next item
// if it's the last item, navigate to the first one
downHandler() {
this.navigatedUserIndex =
(this.navigatedUserIndex + 1) % this.filteredUsers.length;
},
enterHandler() {
const user = this.filteredUsers[this.navigatedUserIndex];
if (user) {
this.selectUser(user);
}
},
// we have to replace our suggestion text with a mention
// so it's important to pass also the position of your suggestion text
selectUser(user) {
this.insertMention({
range: this.suggestionRange,
attrs: {
id: user.id,
label: user.name,
},
});
this.editor.focus();
},
renderPopup(node) {
if (this.popup) {
return;
}
// ref: https://atomiks.github.io/tippyjs/v6/all-props/
this.popup = tippy(".page", {
getReferenceClientRect: node.getBoundingClientRect, // input location
appendTo: () => document.body, // must be issue
interactive: true,
sticky: true, // make sure position of tippy is updated when content changes
plugins: [sticky],
content: this.$refs.suggestions,
trigger: "mouseenter", // manual
showOnCreate: true,
theme: "dark",
placement: "top-start",
inertia: true,
duration: [400, 200],
});
},
destroyPopup() {
if (this.popup) {
this.popup[0].destroy();
this.popup = null;
}
},
beforeDestroy() {
this.destroyPopup();
},
/**
* NOTE: destructure the editor!
*/
onInit({ editor }) {
this.editor = editor;
},
},
};
</script>
How can I get the "suggestions" item display in the aforementioned setting?
I have a weird issue. I am displaying data from a neo4j database onto the highcharts. I am able to retrieve the data, but when i try to put it onto the charts, some of it doesnt display. Here is my gauge code
CustomGauge.vue
<template>
<highcharts :options="chartOptions"></highcharts>
</template>
<script>
import { Chart } from "highcharts-vue";
export default {
name: "CustomGuage",
components: {
highcharts: Chart
},
props: ["data", "title", "range1", "range2", "min", "max"],
data() {
return {
chartOptions: {
chart: {
type: "gauge",
// plotBackgroundColor: null,
// plotBackgroundImage: null,
// plotBorderWidth: 0,
// plotShadow: false,
//marginBottom: 170,
},
credits: {
enabled: false
},
title: {
text: this.title,
align: "left"
},
pane: {
startAngle: -150,
endAngle: 150,
size: 200,
background: {
borderWidth: 0
}
},
// the value axis
yAxis: {
min: this.min,
max: this.max,
// tickPixelInterval: 30,
// tickWidth: 2,
// tickColor: "#666",
plotBands: [
{
from: 0,
to: this.range1,
color: "#55BF3B" // green
},
{
from: this.range1,
to: this.range2,
color: "#DDDF0D" // yellow
},
{
from: this.range2,
to: 1000,
color: "#DF5353" // red
}
]
},
series: [
{
data: this.data
// tooltip: {
// valueSuffix: " km/h"
// }
},
// // {
// // data: this.target,
// // dataLabels: {
// // enabled: true,
// // format: "Target: {y}%",
// // verticalAlign: "bottom",
// // borderWidth: 0
// // //useHTML: true,
// // },
// }
]
}
};
},
watch: {
data(newVal) {
this.chartOptions.series[0].data = newVal;
}
}
};
I define my chart like this
<CustomGuage :title="gaugeTitle1" :data="gaugeData1" :min="gauge1min" :max="gauge1max" :range1="gauge1Range1" :range2="gauge1Range2" />
I initialize it in data() like this -
gaugeTitle1: [],
gaugeData1: [],
gauge1Range1: [],
gauge1Range2: [],
gauge1min: [],
gauge1max: [],
Using the neo4j-vuejs connector, i retrieve the data like this -
const session19 = this.$neo4j.getSession();
// KPI 1
session19
.run(
"match (n:proj) where exists(n.min) return n.name as title,n.min as min,n.max as max,n.range1
as range1,n.range2 as range2,n.target AS target, n.current as data"
)
.then((res) => {
// KPI 1-------------------------
this.data1 = res.records[0].get("data");
var a = JSON.parse(this.data1);
this.gaugeData1.push(a);
console.log(a)
this.min1 = res.records[0].get("min");
var b = JSON.parse(this.min1);
this.gauge1min = b;
console.log(this.gauge1min)
this.max1 = res.records[0].get("max");
var c = JSON.parse(this.max1);
this.gauge1max = c;
console.log(this.gauge1max)
this.title1 = res.records[0].get("title");
this.gaugeTitle1.push(this.title1)
console.log(this.gaugeTitle1);
})
.then(() => {
session.close();
});
The retrieval of data works fine, i checked in the console.The weird part is if i comment/uncomment or change something in CustomGauge.vue, the charts displays perfectly, displays everything perfectly from the database.But once i refresh the page, it is gone. Could someone help me out. thanks for your help in advance
Probably a reactivity issue.
Instead of
this.chartOptions.series[0].data = newVal;
Try
this.$set(this.chartOptions.series[0], 'data', newVal)
I'm trying that the percentages in a pie-chart refresh when clicking in a legend to hide data.
So far, I can display the chart with percentages, but they don't change if I hide one of the legends.
This is the chart: initial chart
This is how looks after the click: after click
We expect that instead 55.6%, it shows 100%.
This is my code so far:
<script>
import {Pie} from "vue-chartjs";
import ChartJsPluginDataLabels from 'chartjs-plugin-datalabels';
export default {
extends: Pie,
ChartJsPluginDataLabels,
props: {
data: Array,
bg: Array,
labels: Array
},
data() {
return {
}
},
computed: {
chartData() {
return this.data
},
bgData() {
return this.bg
},
total() {
return this.data.reduce((a, b) => a + (b || 0), 0)
}
},
methods: {
renderPieChart() {
this.renderChart({
labels: this.labels,
datasets: [
{
label: "Data One",
backgroundColor: this.bgData,
data: this.chartData,
hoverBackgroundColor: "#f78733"
}
]
}, {
responsive: true,
plugins: {
datalabels: {
formatter: (value) => {
let sum = this
.$refs.canvas.getContext('2d').dataset._meta[1].total; //use this.total to fix percentages
let percentage = (value * 100 / sum).toFixed(1) + "%";
return percentage;
},
color: '#fff'
}
}
})
console.log()
},
updateSelected(point, event) {
const item = event[0]
this.selected = {
index: item._index,
value: this
.chartData
.datasets[0]
.data[item._index]
}
}
},
watch: {
bg: function () {
this.renderPieChart();
},
data: function () {
this.renderPieChart();
}
},
}
</script>
In order to obtain the expected result, you should define plugins.datalabels.formatter as follows:
formatter: (value, context) => {
return (value * 100 / context.dataset._meta[0].total).toFixed(1) + "%";
}
new Chart(document.getElementById("myChart"), {
type: "pie",
data: {
labels: ['Savings', 'House'],
datasets: [{
label: "Data One",
backgroundColor: ['#4e9258', '#64e986'],
data: [9, 7],
hoverBackgroundColor: "#f78733"
}]
},
options: {
plugins: {
datalabels: {
formatter: (value, context) => {
return (value * 100 / context.dataset._meta[0].total).toFixed(1) + "%";
},
color: '#fff'
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels"></script>
<canvas id="myChart" height="100"></canvas>
i am using vue-bootstrap4-table in my application i have a custom input though which i search and populate the data,now i need to build a feature in which their is a cross button inside the search field and on clicking on it it should reset the table to empty state here is my code
<template>
<auto-complete
#autocomplete-result-selected="setCustomer"
placeholder="Enter Customer name"
:selected="selectedCustomer"
:styles="{width: 'calc(100% - 10px)'}"
index="locations"
attribute="name"
>
<template slot-scope="{ hit }">
<span>
{{ hit.customer.company && hit.customer.company + ' - ' }}{{ hit.customer.fname }}
{{ hit.customer.lname }}
</span>
</template>
</auto-complete>
<i class="fas fa-times" #click="clearTable()" v-show="selectedCustomer"></i>
</div>
</div>
</template>
<script>
import http from "../../helpers/api.js";
import AutoComplete from "../autocomplete/Autocomplete";
import axios from "axios";
import VueBootstrap4Table from "vue-bootstrap4-table";
export default {
components: {
"auto-complete": AutoComplete,
VueBootstrap4Table
},
computed: {},
data() {
return {
value: "",
selectedCustomer: "",
selectedFirstName: "",
selectedLastName: "",
selectedFields: [
{ name: "Invoice", value: "invoices" },
{
name: "Estimate",
value: "workorder_estimates"
}
],
filters: [
{ is_checked: true, value: "invoices", name: "Invoice" },
{ is_checked: true, value: "workorder_estimates", name: "Estimate" }
],
selectedFilters: [],
estimateChecked: false,
invoiceChecked: false,
config: {
card_mode: false,
show_refresh_button: false,
show_reset_button: false,
global_search: {
placeholder: "Enter custom Search text",
visibility: false,
case_sensitive: false
},
pagination: true,
pagination_info: false,
per_page: 10,
rows_selectable: true,
server_mode: true,
preservePageOnDataChange: true,
selected_rows_info:true
},
classes: {},
rows: [],
columns: [
{
label: "TYPE",
name: "type"
},
{
label: "ID",
name: "distinction_id"
},
{
label: "CUSTOMER NAME",
name: "full_name"
},
{
label: "SERVICE DATE",
name: "service_date"
},
{
label: "ADDRESS",
name: "address1"
},
{
label: "CREATE DATE",
name: "created_at"
}
],
queryParams: {
sort: [],
filters: [],
global_search: "",
per_page: 10,
page: 1
},
selected_rows: [],
total_rows: 0
};
},
methods: {
onNameSearch() {
this.selectedFilters = ["invoices", "workorder_estimates"];
this.fetchData();
},
clearTable(){
this.rows=[];
console.log(this.config.selected_rows_info);
this.config.selected_rows_info=false;
},
onChangeQuery(queryParams) {
console.log(queryParams);
this.queryParams = queryParams;
this.fetchData();
},
onRowClick(payload) {
console.log(payload);
},
setCustomer(selectedResult) {
this.selectedCustomer = selectedResult.customer.company
? `${selectedResult.customer.company + " - "}${
selectedResult.customer.fname
} ${selectedResult.customer.lname}`
: `${selectedResult.customer.fname} ${selectedResult.customer.lname}`;
this.selectedFirstName = selectedResult.customer.fname;
this.selectedLastName = selectedResult.customer.lname;
},
changeCheck(event, index, value) {
var checked = event.target.checked;
switch (value) {
case "invoices":
if (checked) {
this.selectedFields.push({ name: "Invoice", value: "invoices" });
this.invoiceChecked = true;
var data = this.filters[index];
data.is_checked = true;
Vue.set(this.filters, data, index);
} else {
var index = this.selectedFields.findIndex(
item => item.value === value
);
this.selectedFields.splice(index, 1);
this.invoiceChecked = false;
var data = this.filters[index];
data.is_checked = false;
Vue.set(this.filters, data, index);
}
break;
case "workorder_estimates":
if (checked) {
this.selectedFields.push({
name: "Estimate",
value: "workorder_estimates"
});
var data = this.filters[index];
data.is_checked = true;
Vue.set(this.filters, data, index);
} else {
var index = this.selectedFields.findIndex(
item => item.value === value
);
this.selectedFields.splice(index, 1);
this.estimateChecked = false;
var data = this.filters[index];
data.is_checked = false;
Vue.set(this.filters, data, index);
}
break;
}
},
removeFilter(index, value) {
switch (value) {
case "workorder_estimates":
this.selectedFields.splice(index, 1);
this.estimateChecked = false;
var i = this.filters.findIndex(item => item.value === value);
var data = this.filters[i];
data.is_checked = false;
Vue.set(this.filters, data, i);
break;
case "invoices":
this.selectedFields.splice(index, 1);
this.invoiceChecked = false;
var i = this.filters.findIndex(item => item.value === value);
var data = this.filters[i];
data.is_checked = false;
Vue.set(this.filters, data, i);
break;
}
},
updateFilters() {
this.selectedFilters = [];
this.selectedFields.forEach(element => {
this.selectedFilters.push(element.value);
});
if(this.selectedFilters.length == 0){
this.selectedFilters = ['invoices', 'workorder_estimates'];
}
this.fetchData();
},
async fetchData() {
var final = [];
try {
var result = await http.post("/estimate-invoice-search", {
type: this.selectedFilters,
search: {
value: this.selectedFirstName + " " + this.selectedLastName
},
per_page: this.queryParams.per_page,
page: this.queryParams.page,
sort: this.queryParams.sort
});
this.total_rows = result.recordsFiltered;
result.data.forEach(element => {
element.full_name = element.first_name + " " + element.last_name;
final.push(element);
});
this.rows = final;
} catch (error) {
console.log(error);
}
}
},
mounted() {}
};
</script>
now the method named clearTable here i want to reset my table to the point lie we see on page refresh in the method i used this.rows=[]; this clears all the rows which is exactly what i want but the text which shows the number of rows is still their and i cant seem to remove it please view the below image for clarification
i read the documentation on link but cant seem to find a solution for hiding the text, is their any way?
It looks like you're using total_rows as the variable for the number of rows in your template here:
<span>{{total_rows}}</span> Result(s)
The only spot in code that you set this value is in fetchData() where you set:
this.total_rows = result.recordsFiltered;
You can either:
1) Make total_rows a computed property (recommended) that returns the length of rows (I believe rows is always the same length as total_rows from your code)
-or-
2) Just set this.total_rows = 0; in your clearTable() function