I am creating project with vue.js and plot.ly javascript graph library.
How can I bind in "pts" to vue's data's "TestSentences"?
Here is my code ,
thank you to everyone who contributed
My goal is to create an interactive dashboard using this variable. In this way, I can change the data by clicking anywhere on the chart.
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="app">
<div id="grafik"></div>
</div>
<!-- Vue-->
<script>
var app = new Vue({
el: '#app',
data: {
TestSentences: "",
},
methods: {
grafikCiz() {
var trace1 = {
x: [1, 2, 3],
y: ["book", "pencil", "bag"],
mode: 'markers',
marker: {
color: ['#6886c5', '#f40552', '#1b1b2f'],
size: [10, 20, 30]
}
};
var data = [trace1];
var layout = {
height: 400,
width: 400,
};
Plotly.newPlot('grafik', data, layout);
},
},
mounted: function () {
this.grafikCiz();
},
});
</script>
<!-- Vue -->
<script>
var my_graph = document.getElementById('grafik');
my_graph.on('plotly_click', function (data) {
for (var i = 0; i < data.points.length; i++) {
pts = 'x = ' + data.points[i].x + '\ny = ' + data.points[i].y + '\n\n';
};
alert('Closest point clicked:\n\n' + pts);
});
</script>
Use plolty wrapper for vue.js https://github.com/David-Desmaisons/vue-plotly
You can add ref to the component
<vue-plotly v-show="display" :data="graphData" :layout="calculatedLayoutSizes" id="3dPlot"
:display-mode-bar="false" ref="crazyPlotly"></vue-plotly>
then use the ref within your mount point or similar method
this.$refs.crazyPlotly.$on('click', d => {
console.log(d);
});
"d" is an obj with values like x and y datapoint, index...etc
source: https://github.com/statnett/vue-plotly/issues/23
As Alagappan A already pointed out, https://github.com/David-Desmaisons/vue-plotly can make working with plotly in javascript much easier. For me it was sufficient to just:
<vue-plotly :data="data" :layout="layout" #click="temp"> </vue-plotly>
which can directly be utilized in a method:
methods: {
temp (value) {
console.log(value)
}
}
Related
With the concept of VueJS is a kind of framework of JS, then it should have no problem inserting the vanilla JS into it. The following is the attempt to insert the pure JS code into vue-cli project(webpack structure).
[Before Start] I tried the example code as the following link(official document)
URL: https://codepen.io/Tobyliao/pen/ZEWNvwE?editable=true%3Dhttps%3A%2F%2Fdocs.bokeh.org%2F
it works.
[Question] As I tried to imlement into Vue project. It fails. picture1 is the directory structure.
I tried to place the src url include into ./public/html's tag as following:
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-api-2.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-api-2.2.1.min.js"></script>
Create a componet in './src/components/BokehPlot.vue'
inside the code, I insert
<template>
<h1>Measurement Plotting</h1>
</template>
<script src='./main.js'>
export default {
}
</script>
Then finally place all the Bokeh code into './src/component/main.js'. It is the pure JS code I want to import into the structure.
[Result]
I can see the plot in the background, but it kept on showing the error message like picture2.
You have many options here, I went ahead and simply made a mixin to utilize the component lifecycle that Vue provides. source
Here are the relevant parts:
BokehPlot.vue
<template>
<h1>治具量測</h1>
</template>
<script>
import Chart from "#/mixins/Chart";
export default {
mixins: [Chart],
};
</script>
Chart.js
export default {
data() {
return {
plot: null,
xdr: null,
ydr: null
};
},
beforeMount() {
// create some ranges for the plot
this.xdr = new Bokeh.Range1d({ start: -1, end: 100 });
this.ydr = new Bokeh.Range1d({ start: -0.5, end: 20.5 });
// make the plot
this.plot = new Bokeh.Plot({
title: "BokehJS Plot",
x_range: this.xdr,
y_range: this.ydr,
plot_width: 400,
plot_height: 400,
background_fill_color: "#F2F2F7"
});
},
mounted() {
this.loadData();
},
methods: {
loadData() {
// create some data and a ColumnDataSource
let x = Bokeh.LinAlg.linspace(-0.5, 20.5, 10);
let y = x.map(function (v) {
return v * 0.5 + 3.0;
});
let source = new Bokeh.ColumnDataSource({ data: { x: x, y: y } });
// add axes to the plot
let xaxis = new Bokeh.LinearAxis({ axis_line_color: null });
let yaxis = new Bokeh.LinearAxis({ axis_line_color: null });
this.plot.add_layout(xaxis, "below");
this.plot.add_layout(yaxis, "left");
// add grids to the plot
let xgrid = new Bokeh.Grid({ ticker: xaxis.ticker, dimension: 0 });
let ygrid = new Bokeh.Grid({ ticker: yaxis.ticker, dimension: 1 });
this.plot.add_layout(xgrid);
this.plot.add_layout(ygrid);
// add a Line glyph
let line = new Bokeh.Line({
x: { field: "x" },
y: { field: "y" },
line_color: "#666699",
line_width: 2
});
this.plot.add_glyph(line, source);
Bokeh.Plotting.show(this.plot);
}
}
};
Many decisions to still make, but hopefully that will get you pointed down the right path.
See working example:
https://codesandbox.io/s/bokehjs-forked-4w20k?fontsize=14&hidenavigation=1&theme=dark
I am trying to display a chart by passing to it data from a controller. I am using chart.js
Model:
public class DatapointLine
{
public DatapointLine(double x, double y)
{
this.X = x;
this.Y = y;
}
// setting the name to be used when serializing to JSON.
[DataMember(Name = "x")]
public Nullable<double> X = null;
//setting the name to be used whenserializing to JSON.
[DataMember(Name = "y")]
public Nullable<double> Y = null;
}
Controller:
public JsonResult BarChart()
{
List<DatapointLine> dataPoints = new List<DatapointLine>{
new DatapointLine(10, 22),
new DatapointLine(20, 36),
new DatapointLine(30, 42),
new DatapointLine(40, 51),
new DatapointLine(50, 46),
};
ViewBag.DataPoints = JsonConvert.SerializeObject(dataPoints);
return Json(dataPoints, JsonRequestBehavior.AllowGet);
}
Script:
<script type="text/javascript">
$(function() {
var data = getData();
AutoFollow(data);
});
function getData() {
var dateValue = [];
var countValue = [];
$.ajax({
url: "/Supernethome/BarChart",
dataType: 'json',
async: false
}).done(function(data) {
data.forEach(function(data) {
dateValue.push(data.X);
countValue.push(data.Y);
});
});
return {
dateValue: dateValue,
countValue: countValue
};
}
$(document).ready(function () {function AutoFollow(data) {
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx,
{
type: 'bar',
data: {
labels: data.dateValue,
datasets: [
{
label: 'AutoFollow',
data: data.countValue,
backgroundColor: "rgba(153,255,51,1)"
}, {
label: 'Manual',
data: [30, 29, 5, 5, 20, 3, 10],
backgroundColor: "rgba(255,153,0,1)"
}
]
}
});
}
});
I am generating the views for the charts in partial views and then referencing the partial views in a main view.
I am getting the following errors:
chartjs.init.js:3 Uncaught TypeError: Cannot read property 'getContext' of null
at HTMLDocument.<anonymous> (chartjs.init.js:3)
at f (jquery.js:1026)
at Object.fireWith [as resolveWith] (jquery.js:1138)
at Function.ready (jquery.js:427)
at HTMLDocument.xt (jquery.js:97)
The error is eating a lot of time,Need Help.
Looks like two different errors to me...
Error 1 : Graph container element not found
Checkout this thread as the issue sounds the same:
morris.js Graph container element not found
Error 2 : Cannot read property 'getContext'
This looks like a red herring. This exception is not being thrown by morris.js but by chartjs. However it may be that the exception thrown by this code is stopping the morris.js code from being executed successfully. As such it's worth testing the code in isolation i.e load up a view with nothing in it except the required morris scripts/assets and your inline script. No additional scripts or JavaScript libraries. Something like this :
Example Test View
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css">
</head>
<body>
<div id="mychart"></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
<script>
$(document).ready(function () {
$.getJSON("/Supernethome/BarChart", function (data) {
new Morris.Area({
element: 'mychart',
data: data,
xkey: 'X',
ykeys: ['Y'],
pointSize: 2,
hideHover: 'auto',
resize: true
});
});
});
</script>
</body>
</html>
I'm converting an established site over to VueJS but hit a stumbling block on the best way to achieve this.
It's using D3-Funnel (https://github.com/jakezatecky/d3-funnel) to draw a funnel chart but how do I pass VueJS data variables to the charts constructor?
<script>
const data = [
{ label: 'Step 1', value: this.step1 },
{ label: 'Step 2', value: this.step2 },
.......
];
const options = {
block: {
dynamicHeight: true,
minHeight: 15,
},
};
const chart = new D3Funnel('#funnel');
chart.draw(data, options);
</script>
So I need to pass vue data variables into the values. My first thought is to move this into it's own function in the VueJS methods object and use the variables there using this.
Is there a better way of achieving this?
---------- Edit -------------
As per comments people wanted to see how I achieved this currently in vue. As already mentioned above I just created a function in the vue methods object and then call it.
methods : {
drawChart(){
const data = [
{ label: 'Step 1', value: 99999 },
{ label: 'Step 2', value: 9999 },
.......
];
const options = {
block: {
dynamicHeight: true,
minHeight: 15,
},
};
const chart = new D3Funnel('#funnel');
chart.draw(data, options);
}
},
mounted(){
this.drawChart();
}
Data is coming from an API and put into the vue data object.
data:{
step1: 0,
step2: 0,
....
},
methods:{
getData(){
axois.post......
response{
this.step1 = response.data.step1
this.step2 = response.data.step2
....
}
}
}
As I understand it you are trying to pass information down to a component and use it. If you are using single file components and webpack you can do something like this which is put together with examples listed on the vue website.
You can also take a look at this guys approach
App.vue
...
<my-d3-component :d3data="d3Data"></my-d3-component>
...
<script>
import d3Component from 'path/to/component'
var app = new Vue({
el: '#app',
data: {
d3Data: {}
},
components: {
'my-d3-component': d3Component
}
})
</script>
d3Component.vue
<template>
d3 html stuff goes here
</template>
<script>
export default {
props: ['d3Data'],
data() {
return {}
},
mounted: {
const options = {
block: {
dynamicHeight: true,
minHeight: 15,
},
};
const chart = new D3Funnel('#funnel');
chart.draw(this.d3Data, options);
}
}
</script>
This question has been asked many times and I went through most of them but non of them helped me finding a solution.
I am generating couple of bar charts using a for loop as a part of reporting functionality.
I am using node.js with Express Handlebars.
My page looks like:
<div class="row report-charts">
<div class="col-md-12">
{{#buildings}}
<div class="col-md-6">
<h4>{{Name}}</h4>
<canvas id="{{idBuildings}}" width="200" height="80"></canvas>
</div>
{{/buildings}}
</div>
</div>
My js code looks like:
$('.case-report-btn').click(function(){
$.ajax({
type: 'post',
url: '/reports/cases/filter',
data : {
StartDate : $('.start-ms-time-hidden').val(),
EndDate : $('.end-ms-time-hidden').val(),
ReportKey : $('.cases-filter-type').val()
},
dataType: 'json',
success: function(res) {
$('.report-charts').show();
for(key in res) {
var innerObj = res[key]; //gives the inner obj
var ctx = document.getElementById(key); //the idBuildings
var labels = [];
var data = [];
var buildingName = innerObj.Name;
for(innerKey in innerObj) {
if(innerKey != 'Name' && innerKey != 'Total') {
labels.push(innerKey);
data.push(innerObj[innerKey]);
}
}
var options = {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: buildingName,
data: data,
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
fixedStepSize: 1
}
}]
}
}
}
var myChart = new Chart(ctx, options);
}
$('#pleaseWaitDialog').modal('hide');
},
error: function(err) {
$('#pleaseWaitDialog').modal('hide');
bootbox.alert('Error: ' + err);
}
});
});
So basically, I am using for loop to generate multiple charts on the page. Inside the loop I declared the chart variable, every time I change the report parameters and hit the button, the new chart is generated. But when I hover over it, the old one still shows up.
Now I am not sure where I should be putting the myChart.destroy() or myChart.clear() methods. I also tried moving the myChart declaration outside the for loop but it didn't help either.
Any suggestions on how to handle this?
I think there are a few ways to do it. You can update your chart data if the chart already exist. Here two functions you can use:
function removeData(chart) {
chart.data.labels.pop();
chart.data.datasets.forEach((dataset) => {
dataset.data.pop();
});
chart.update();
}
function addData(chart, label, data) {
chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) => {
dataset.data.push(data);
});
chart.update();
}
First you have to remove all your data and then add the new data.
If you want to destroy the chart and create it again you have to save your variable as global. To do this you have yo declare your variable like window.myChart and then before create the new chart, something like this:
if (window.myChart) window.myChart.destroy();
window.myChart = new Chart(ctx, options);
Another way you can try is removing your canvas and creating another one. Something like this:
$('#your_canvas').remove();
$('#your_canvas_father').append('<canvas id="your_canvas"></canvas>');
I have a very specific need that cannot realy be solved with standard data-binding.
I've got a leaflet map that I want to bind with a vue view-model.
I succeeded to display geojson features kinda bounds to my view, but I'm struggling at displaying a popup bound with vue.js
The main question is : "How to open a popup (possibly multiple popups at the same time) and bind it to a view property "
For now I've come to a working solution, but this is aweful :
map.html
<div id="view-wrapper">
<div id="map-container"></div>
<div v-for="statement in statements" id="map-statement-popup-template-${statement.id}" style="display: none">
<map-statement-popup v-bind:statement="statement"></map-statement-popup>
</div>
</div>
<!-- base template for statement map popup -->
<script type="text/template" id="map-statement-popup-template">
{{ statement.name }}
</script>
map.js
$(document).ready(function() {
var map = new L.Map('map-container');
map.setView(new L.LatLng(GLOBALS.MAP.STARTCOORDINATES.lng, GLOBALS.MAP.STARTCOORDINATES.lat), GLOBALS.MAP.STARTZOOM);
var osm = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
osm.addTo(map);
//Initialize map dynamic layers
var mapLayers = {};
//View-model data-bindings
var vm = new Vue({
el: '#view-wrapper',
data: {
statements: []
},
methods: {
getStatements: function() {
return $.get('api/statements');
},
updateStatements: function() {
var that = this;
return that.getStatements().then(
function(res) {
that.statements = res.data;
}
);
},
refreshStatements: function() {
mapLayers.statements.layer.clearLayers();
if(this.statements && this.statements.length){
var geoJsonStatements = geoJsonFromStatements(this.statements);
mapLayers.statements.layer.addData(geoJsonStatements);
}
},
handleStatementFeature: function(feature, layer) {
var popupTemplateEl = $('#map-statement-popup-template-' + feature.properties.statement.id);
layer.bindPopup(popupTemplateEl.html());
var statementIndex = _.findIndex(this.statements, {statement:{id: feature.properties.statement.id}});
if(feature.geometry.type === 'LineString') {
this.statements[statementIndex].layer = {
id: L.stamp(layer)
};
}
},
openStatementPopup: function(statement) {
if(statement.layer) {
var featureLayer = mapLayers.statements.layer.getLayer(statement.layer.id);
featureLayer.openPopup();
}
}
},
created: function() {
var that = this;
//Set dynamic map layers
var statementsLayer = L.geoJson(null, {
onEachFeature: this.handleStatementFeature
});
mapLayers.statements = {
layer: statementsLayer
};
map.addLayer(mapLayers.statements.layer);
this.updateStatements().then(this.refreshStatements);
this.$watch('statements', this.refreshStatements);
},
components: {
'map-statement-popup': {
template: '#map-statement-popup-template',
props: {
statement: null
}
}
}
});
function geoJsonFromStatementsLocations(statements){
var geoJson = {
type: "FeatureCollection",
features: _.map(statements, function(statement) {
return {
type: "Feature",
geometry: {
type: "LineString",
coordinates: statement.coordinates
},
properties: {
statement: statement
}
};
});
};
return geoJson;
}
});
This seems pretty aweful to me, because I have to loop over statements with a v-for, render a div for my custom element for every statement, hide it, then use it in the popup, grabbing it with a dynamic id technique.
I would like to do something like this :
map.html
<div id="view-wrapper">
<div id="map-container"></div>
</div>
<!-- base template for statement map popup -->
<script type="text/template" id="map-statement-popup-template">
{{ statement.name }}
</script>
map.js
$(document).ready(function() {
[...]
//View-model data-bindings
var vm = new Vue({
el: '#view-wrapper',
data: {
statements: []
},
methods: {
handleStatementFeature: function(feature, layer) {
var popupTemplateEl = $('<map-statement-popup />');
var scope = { statement: feature.properties.statement };
var compiledElement = this.COMPILE?(popupTemplateEl[0], scope);
layer.bindPopup(compiledElement);
}
},
components: {
'map-statement-popup': {
template: '#map-statement-popup-template',
props: {
statement: null
}
}
}
});
function geoJsonFromStatementsLocations(statements){
var geoJson = {
type: "FeatureCollection",
features: _.map(statements, function(statement) {
return {
type: "Feature",
geometry: {
type: "LineString",
coordinates: statement.coordinates
},
properties: {
statement: statement
}
};
});
};
return geoJson;
}
});
... but I couldn't find a function to "COMPILE?" based on a defined scope. Basically I want to :
Create a custom element instance
Pass it a scope
Compile it
EDIT : Actually, I could find $compile function. But it's often used to compile appended child to html. I don't want to append it THEN compile it. I'd like to compile it then let leaflet append it for me.
Would this work for you? Instead of using a component, you create a new element to be passed to bindPopup, and you new Vue on that element, with your data set appropriately.
new Vue({
el: 'body',
data: {
popups: [1, 2, 3],
message: "I'm Dad",
statements: []
},
methods: {
handleFeature: function(id) {
const newDiv = document.createElement('div');
const theStatement = {
name: 'Some name for ' + id
};
newDiv.innerHTML = document.getElementById('map-statement-popup-template').innerHTML;
new Vue({
el: newDiv,
data: {
statement: theStatement
},
parent: this
});
// Mock call to layer.bindPopup
const layerEl = document.getElementById(id);
this.bindPopup(layerEl, newDiv);
},
bindPopup: function(layerEl, el) {
layerEl.appendChild(el);
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div class="leaflet-zone">
<div v-for="popup in [1,2,3]">
<button #click="handleFeature('p-' + popup)">Bind</button>
<div id="p-{{popup}}"></div>
</div>
</div>
<template id="map-statement-popup-template">
{{ statement.name }} {{$parent.message}}
</template>
I think you could do the same thing with $compile, but $compile is poorly (really un-) documented and intended for internal use. It is useful for bringing a new DOM element under control of the current Vue in the current scope, but you had a new scope as well as a new DOM element, and as you noted, that binding is exactly what Vue is intended to do.
You can establish a parent chain by specifying the parent option as I have updated my snippet to do.