on the below code snippet in custom tooltip under tooltip on click event, the class variable is not accessible when I try with this it showing values related to ChartElement only
#Output() valuechange = new EventEmitter<any>();
options: ChartOptions = {
responsive: true,
maintainAspectRatio: false,
tooltips: {
displayColors: false,
mode: 'nearest',
intersect: false,
enabled: false,
custom(tooltipModel: any) {
// tooltip element
let tooltipEl: any = document.querySelector('#chartjs-tooltip');
// create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.style.width = '124px';
tooltipEl.style.color = 'white';
tooltipEl.style.borderRadius = '6px';
tooltipEl.style.backgroundColor = 'rgba(55, 71, 79, 0.8)';
tooltipEl.innerHTML = '<div style ="display:flex;flex-direction:column">sample tooltip</div>';
document.body.appendChild(tooltipEl);
}
tooltipEl.onclick = () => {
// NOT ABLE TO Access this to emit event
// this.valuechange.emit('test');
console.log('hi); // working
};
}
}
To make this equal to the class, write it as an arrow function:
custom: () => {
console.log(this); // should be the class
}
But sometimes you need a handle of this and that where this is the class and that is the Chart object.
Create a utility function:
export const thisAsThat = (callBack: Function) => {
const self = this;
return function () {
return callBack.apply(self, [this].concat(Array.prototype.slice.call(arguments)));
};
}
Then:
import { thisAsThat } from './where/thisAsThat/is/located';
....
custom: thisAsThat((that: any, otherArgument: any) => {
console.log(this); // the class
console.log(that); // the chart object
})
TypeScript: How to use both fat arrow and this?
Related
I'm trying to customize chart tooltip text with Blazorise.Charts which is based on ChartJs.
I know how to do this with JS:
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
plugins: {
tooltip: {
callbacks: {
label: function(context) {
return ..;
}
}
}
}
}
});
But I'm not sure how to achieve the same with C#:
<LineChart #ref="LineChart" TItem="long" id="#ChartId" Class="mw-100" OptionsObject="GetOptionsObject()"/>
private object GetOptionsObject()
{
return new
{
Plugins = new
{
Tooltips = new{
Padding = 10,
UsePointStyle = true,
Position = "nearest",
XAlign = "center",
YAlign = "bottom",
Callbacks = new {
Label = ...// stuck here
}
}
}
}
Can i define some delegate function and would it work with the object? Is there some other way to manage tooltips in Blazorise Charts?
I recently started playing around with js, and specifically - with this graphing library.
I ran some of their example code, and I got somewhat of a noob HTML / js question.
this is their example code:
When I run this code, the graph appears on the left of the div.
I wanted to know how I can align this object to the mid of the div.
I don't really know if this is a "HTML / CSS" task - or rather a "js task" which means I should obtain this behaviour via the object API (tbh, I tried looking into the API and I saw no alignment options).
Sorry if this is a really noobie question, I tried solving it, but I had no success.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Tutorial Demo</title>
</head>
<body>
<div id="mountNode"></div>
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
<script>
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
// Default properties for all the nodes
defaultNode: {
labelCfg: {
style: {
fill: '#fff',
},
},
},
// Default properties for all the edges
defaultEdge: {
labelCfg: {
autoRotate: true,
},
},
// The node styles in different states
nodeStateStyles: {
// The node style when the state 'hover' is true
hover: {
fill: 'lightsteelblue',
},
// The node style when the state 'click' is true
click: {
stroke: '#000',
lineWidth: 3,
},
},
// The edge styles in different states
edgeStateStyles: {
// The edge style when the state 'click' is true
click: {
stroke: 'steelblue',
},
},
// Layout
layout: {
type: 'force',
linkDistance: 100,
preventOverlap: true,
nodeStrength: -30,
edgeStrength: 0.1,
},
// Built-in Behaviors
modes: {
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
},
});
const main = async () => {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
);
const remoteData = await response.json();
const nodes = remoteData.nodes;
const edges = remoteData.edges;
nodes.forEach((node) => {
if (!node.style) {
node.style = {};
}
node.style.lineWidth = 1;
node.style.stroke = '#666';
node.style.fill = 'steelblue';
switch (node.class) {
case 'c0': {
node.type = 'circle';
node.size = 30;
break;
}
case 'c1': {
node.type = 'rect';
node.size = [35, 20];
break;
}
case 'c2': {
node.type = 'ellipse';
node.size = [35, 20];
break;
}
}
});
edges.forEach((edge) => {
if (!edge.style) {
edge.style = {};
}
edge.style.lineWidth = edge.weight;
edge.style.opacity = 0.6;
edge.style.stroke = 'grey';
});
graph.data(remoteData);
graph.render();
// Mouse enter a node
graph.on('node:mouseenter', (e) => {
const nodeItem = e.item; // Get the target item
graph.setItemState(nodeItem, 'hover', true); // Set the state 'hover' of the item to be true
});
// Mouse leave a node
graph.on('node:mouseleave', (e) => {
const nodeItem = e.item; // Get the target item
graph.setItemState(nodeItem, 'hover', false); // Set the state 'hover' of the item to be false
});
// Click a node
graph.on('node:click', (e) => {
// Swich the 'click' state of the node to be false
const clickNodes = graph.findAllByState('node', 'click');
clickNodes.forEach((cn) => {
graph.setItemState(cn, 'click', false);
});
const nodeItem = e.item; // et the clicked item
graph.setItemState(nodeItem, 'click', true); // Set the state 'click' of the item to be true
});
// Click an edge
graph.on('edge:click', (e) => {
// Swich the 'click' state of the edge to be false
const clickEdges = graph.findAllByState('edge', 'click');
clickEdges.forEach((ce) => {
graph.setItemState(ce, 'click', false);
});
const edgeItem = e.item; // Get the clicked item
graph.setItemState(edgeItem, 'click', true); // Set the state 'click' of the item to be true
});
};
main();
</script>
</body>
</html>
just use flexbox.
#mountNode{
display:flex;
justify-content:center;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Tutorial Demo</title>
</head>
<body>
<div id="mountNode"></div>
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
<script>
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
// Default properties for all the nodes
defaultNode: {
labelCfg: {
style: {
fill: '#fff',
},
},
},
// Default properties for all the edges
defaultEdge: {
labelCfg: {
autoRotate: true,
},
},
// The node styles in different states
nodeStateStyles: {
// The node style when the state 'hover' is true
hover: {
fill: 'lightsteelblue',
},
// The node style when the state 'click' is true
click: {
stroke: '#000',
lineWidth: 3,
},
},
// The edge styles in different states
edgeStateStyles: {
// The edge style when the state 'click' is true
click: {
stroke: 'steelblue',
},
},
// Layout
layout: {
type: 'force',
linkDistance: 100,
preventOverlap: true,
nodeStrength: -30,
edgeStrength: 0.1,
},
// Built-in Behaviors
modes: {
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
},
});
const main = async () => {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
);
const remoteData = await response.json();
const nodes = remoteData.nodes;
const edges = remoteData.edges;
nodes.forEach((node) => {
if (!node.style) {
node.style = {};
}
node.style.lineWidth = 1;
node.style.stroke = '#666';
node.style.fill = 'steelblue';
switch (node.class) {
case 'c0': {
node.type = 'circle';
node.size = 30;
break;
}
case 'c1': {
node.type = 'rect';
node.size = [35, 20];
break;
}
case 'c2': {
node.type = 'ellipse';
node.size = [35, 20];
break;
}
}
});
edges.forEach((edge) => {
if (!edge.style) {
edge.style = {};
}
edge.style.lineWidth = edge.weight;
edge.style.opacity = 0.6;
edge.style.stroke = 'grey';
});
graph.data(remoteData);
graph.render();
// Mouse enter a node
graph.on('node:mouseenter', (e) => {
const nodeItem = e.item; // Get the target item
graph.setItemState(nodeItem, 'hover', true); // Set the state 'hover' of the item to be true
});
// Mouse leave a node
graph.on('node:mouseleave', (e) => {
const nodeItem = e.item; // Get the target item
graph.setItemState(nodeItem, 'hover', false); // Set the state 'hover' of the item to be false
});
// Click a node
graph.on('node:click', (e) => {
// Swich the 'click' state of the node to be false
const clickNodes = graph.findAllByState('node', 'click');
clickNodes.forEach((cn) => {
graph.setItemState(cn, 'click', false);
});
const nodeItem = e.item; // et the clicked item
graph.setItemState(nodeItem, 'click', true); // Set the state 'click' of the item to be true
});
// Click an edge
graph.on('edge:click', (e) => {
// Swich the 'click' state of the edge to be false
const clickEdges = graph.findAllByState('edge', 'click');
clickEdges.forEach((ce) => {
graph.setItemState(ce, 'click', false);
});
const edgeItem = e.item; // Get the clicked item
graph.setItemState(edgeItem, 'click', true); // Set the state 'click' of the item to be true
});
};
main();
</script>
</body>
</html>
I was define my chart as below (MainChart.vue).
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
// const brandPrimary = '#20a8d8'
export default {
extends: Line,
mixins: [reactiveProp],
props: ['options', 'chartData', 'height'],
mounted () {
this.renderChart(this.chartData, this.options)
var elements = 1
}
}
I tested this code and confirmed that it worked well.
<line-chart :chartData="myChartData"></line-chart>
but, I tried rendering chart dynamically, it is not working.
import lineChart from './MainChart';
// ...
let chartClass = Vue.extend(lineChart)
let chartInstance = new chartClass({
propsData: {
chartData: myChartData
}
})
chartInstance.$mount()
console.log(chartInstance.$el)
console.log(chartInstance.$el.querySelector("canvas").toDataURL('image/png'))
console.log(chartInstance.$refs.canvas)
console.log(chartInstance.$refs.canvas.toDataURL('image/png'))
Console messages:
I checked from the console and found that nothing was drawn in the canvas area.
How can I do render my chart dynamically?
Similar questions:
Is it possible to print a chart with vue-chartjs?
To get full image data, you have to wait until the chart is finished. Using 'Promise' is helpful.
async function addChart(d, i, w, h) {
var canvas = document.createElement("canvas")
canvas.width = 765
canvas.height = 382
//canvas.style.width = "765px"
//canvas.style.height = "382px"
//canvas.style.display = "none"
canvas.id = "dynamicChart"
document.body.appendChild(canvas)
var ctx = document.getElementById("dynamicChart").getContext('2d');
var draw = () => new Promise((resolve, reject) => {
new Chart(ctx, {
type: 'bar',
data: d,
options: {
responsive: false
}
})
setTimeout(() => resolve(), 100)
})
await draw()
let imageData = document.getElementById("dynamicChart").toDataURL("image/png")
console.log(imageData)
addImage(imageData, i, w, h)
document.body.removeChild(canvas)
}
// ...
await addChart(myChartData, 0, 400, 300)
If you want draw multiple chart for in the loop, try this:
let chartFunctions = []
myList.forEach((item) => {
chartFunctions.push(async function() {
await addChart(myChartData, 3, 160, 80)
})
}
for(let k in chartFunctions) {
await chartFunctions[k]()
}
Console messages:
I want to call both a javascript function and a typescript function from the same event. It is a onClick event in a chart. I am relatively new to typescript and angular, so i dont know if what i am doing is even possible.
The problem is: I need to call a javascript function for getting a activated bar in the chart, and the typescript function to open a dialog in the angular component.
onClick: function(evt){
console.log(this);//<-- returns chart
bar: () => {console.log(this)}; //<-- here I try to get this as component
bar(); // <--doesnt work
//console.log(document.getElementById('myChart'));
}
Maybe bette if i show the whole thing.
public barChartOptions = {
scaleShowVerticalLines: false,
responsive: true,
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
onHover: console.log('onHover'),
onClick: function(evt){
//console.log(evt); Mouse Event
console.log(this);
const getFirst = array => console.log(this);
console.log(getFirst);
//bar: () => {console.log(this)};
//bar();
//console.log(document.getElementById('myChart'));
},
/*
onClick : (evt, datasets) => {
//let self = this;
//console.log(self);
if(datasets.length > 0){
this.openDialog();
console.log(this);
console.log(this.barChart);
}
},*/
scales: {
xAxes: [{
stacked: true
}],
yAxes: [{
stacked: true
}]
},
legend: {
display: true,
position: 'right'
},
tooltips: {
enabled: true,
mode: 'point'
}
};
this is my html template:
my-bar-dialog works!
<div>
<div style="display: block">
<canvas baseChart
id="myChart"
[datasets]="barChartData"
[labels]="barChartLabels"
[options]="barChartOptions"
[legend]="barChartLegend"
[chartType]="barChartType">
</canvas>
</div>
</div>
<button mat-raised-button (click)="openDialog()">Pick one</button>
<button (click)="openDialog()">Pick one</button>
Now, the thing is i have two different "this":
1)
onClick: function(evt){
let that = this;
let bar=()=> {console.log(that.this)};
bar();
},
2)
onClick : (evt, datasets) => {
if(datasets.length > 0){
console.log(this);
}
},
1 returns a char, 2 returns the component.
But i need both of them in the same Event/function as i need to call chartjs api functions and i need to call a function from my component.
And here my component
import { Component, OnInit, Inject } from '#angular/core';
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '#angular/material/dialog';
import { BarChartService } from '../bar-chart.service';
import { barChartClass } from '../barChartClass';
declare var foo: Function;
#Component({
selector: 'app-my-bar-dialog',
templateUrl: './my-bar-dialog.component.html',
styleUrls: ['./my-bar-dialog.component.css'],
})
export class MyBarDialogComponent implements OnInit {
client: string;
tenant: string;
constructor(public dialog: MatDialog, private barChartService: BarChartService) {
foo();
}
//First BarChart
barChart: barChartClass;
public barChartLabels: any;
public barChartType: any;
public barChartLegend: any;
public barChartData: any;
getBarChart(): void {
this.barChartService.getMockBarChart().subscribe(
barChart => this.barChart = barChart
);
this.barChartData = this.barChart.barChartData;
this.barChartLabels = this.barChart.barChartLabels;
this.barChartType = this.barChart.barChartType;
this.barChartLegend = this.barChart.barChartLegend;
}
public barChartOptions = {
scaleShowVerticalLines: false,
responsive: true,
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
onHover: console.log('onHover'),
onClick: function(evt){
//console.log(evt); Mouse Event
//console.log(this);
let that = this;
//bar: () => {console.log(this)};
let bar=()=> {console.log(that.this)};
bar();
//bar();
//console.log(document.getElementById('myChart'));
},
/*
onClick : (evt, datasets) => {
//let self = this;
//console.log(self);
if(datasets.length > 0){
this.openDialog();
console.log(this);
console.log(this.barChart);
}
},*/
scales: {
xAxes: [{
stacked: true
}],
yAxes: [{
stacked: true
}]
},
legend: {
display: true,
position: 'right'
},
tooltips: {
enabled: true,
mode: 'point'
}
};
openDialog(): void {
const dialogRef = this.dialog.open(DialogData, {
width: '250px',
data: {client: this.client, tenant: this.tenant}
});
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
this.client = result;
});
}
ngOnInit() {
this.getBarChart();
}
}
#Component({
selector: 'dialog-data',
templateUrl: 'dialog-data.html',
styleUrls: ['dialog-data.css']
})
export class DialogData {
constructor(
public dialogRef: MatDialogRef<DialogData>,
#Inject(MAT_DIALOG_DATA) public data: DialogData) {}
onNoClick(): void {
this.dialogRef.close();
}
}
What you're doing with the bar and the colon in the function is that you're trying to describe its type rather to declare it. So if you want to actually declare the function, do this:
onClick: function(evt) {
console.log(this);//<-- returns chart
let bar = () => { console.log(this) };
bar();
//console.log(document.getElementById('myChart'));
}
if you want to describe and declare it, do this:
onClick: function(evt) {
console.log(this);//<-- returns chart
let bar: () => void = () => { console.log(this) }; //<-- here I try to get this as component
bar(); // <--doesnt work
//console.log(document.getElementById('myChart'));
}
before using the chart in component assign this to some other variable like
var that=this
then in your chart
onClick: function(evt){
console.log(this);//<-- returns chart
let bar= () => {console.log(that)}; //<-- that should have your component refrence
}
Stackblitz demo
I'm trying to add a constructor, which I call ctor, to Project, so I can add to it some properties when it's instantiated.
Here's the (absolutely ghastly) hack I'm using on Item to make this tick:
const canvas = document.querySelector('canvas')
paper.setup(canvas)
paper.Item.inject({
ctor: function(args) {
console.log('Item constructed!')
}
})
// All Items are supposed to call `_initialize` so we "hook" there.
const Item_initialize = paper.Item.prototype._initialize
const Item_ctor = paper.Item.prototype.ctor
paper.Item.prototype._initialize = function(...args) {
const initializer = Item_initialize.apply(this, args)
if (Item_ctor) Item_ctor.apply(this)
return initializer
}
const path = new paper.Path.Line({
strokeColor: 'black',
strokeWidth: 5,
from: 100,
to: 50
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-core.js"></script>
<canvas></canvas>
Unfortunately the above trick doesn't work on Project:
const canvas = document.querySelector('canvas')
paper.setup(canvas)
paper.Project.inject({
ctor: function(args) {
// Not called
console.log('Project constructed!')
}
})
const Project_initialize = paper.Project.prototype._initialize
const Project_ctor = paper.Project.prototype.ctor
paper.Project.prototype._initialize = function(...args) {
const initializer = Project_initialize.apply(this, args)
if (Project_ctor) Project_ctor.apply(this)
return initializer
}
const project = new paper.Project(canvas)
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-core.js"></script>
<canvas></canvas>
What am I missing here?