Create varying amount of morris js line graphs? - javascript

I need to create a varying amount of the same morris js graphs depending on the data I pull from the database, i.e everything will stay the same except form the actual data. My code works perfectly for one graph but when I try and loop through an array to make new graphs it all messes up, any ides how to fix this?
Here is my code, I have just hardcoded values as I still need to work out how to automatically create variable and add them to an array:
<script>
var jsonVMs= [{"y":"2015-03-10 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-11 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-12 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-13 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-14 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-15 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-16 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-17 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-18 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-19 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-20 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-21 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-22 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-23 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-24 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-25 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-26 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-27 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-28 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-29 00:00:00","a":"20.00","b":"0.0000000"},{"y":"2015-03-30 00:00:00","a":"20.00","b":"0.0000000"}];
var jsonVMs1= [{"y":"2015-03-11 00:00:00","a":"3","b":"3"},{"y":"2015-03-12 00:00:00","a":"5","b":"1"},{"y":"2015-03-13 00:00:00","a":"4","b":"0"},{"y":"2015-03-14 00:00:00","a":"4","b":"0"},{"y":"2015-03-15 00:00:00","a":"4","b":"0"},{"y":"2015-03-16 00:00:00","a":"6","b":"1"},{"y":"2015-03-17 00:00:00","a":"12","b":"5"},{"y":"2015-03-18 00:00:00","a":"14","b":"5"},{"y":"2015-03-19 00:00:00","a":"14","b":"2"},{"y":"2015-03-20 00:00:00","a":"14","b":"3"},{"y":"2015-03-21 00:00:00","a":"15","b":"2"},{"y":"2015-03-22 00:00:00","a":"15","b":"2"},{"y":"2015-03-23 00:00:00","a":"15","b":"4"},{"y":"2015-03-24 00:00:00","a":"17","b":"6"},{"y":"2015-03-25 00:00:00","a":"17","b":"6"},{"y":"2015-03-26 00:00:00","a":"19","b":"9"},{"y":"2015-03-27 00:00:00","a":"17","b":"6"},{"y":"2015-03-28 00:00:00","a":"17","b":"6"},{"y":"2015-03-29 00:00:00","a":"17","b":"6"},{"y":"2015-03-30 00:00:00","a":"18","b":"6"}];
var a = [jsonVMs,jsonVMs1];
</script>
<div id="VMsDiv1" ></div>
<script type="text/javascript">
var index =0;
while (index < a.length) {
new Morris.Line({
// ID of the element in which to draw the chart.
element: 'VMsDiv1',
// Chart data records -- each entry in this array corresponds to a point on
// the chart.
data:a[index],
// The name of the data record attribute that contains x-values.
xkey: 'y',
// A list of names of data record attributes that contain y-values.
ykeys: ['a','b'],
// Labels for the ykeys -- will be displayed when you hover over the
// chart.
xLabelFormat: function(d) {
return d.getDate()+'/'+(d.getMonth()+1)+'/'+d.getFullYear();},
labels: ['Total VMs','Powered On'],
dateFormat: function(date) {
d = new Date(date);
return d.getDate()+'/'+(d.getMonth()+1)+'/'+d.getFullYear();
},
hideHover: true
});
index++
}

As per my comment, you need to separate the div where the charts will be rendered.
As such, add another div like so:
<div id="VMsDiv0" ></div>
And change the line to this:
while (index < a.length) {
new Morris.Line({
// ...
element: 'VMsDiv'+index,
// ...
});
}

Related

Chart JS prepend labels to x-axis with addData

I'm building a dynamic line chart using Chart JS, and I've managed to get the function working that appends data labels to the end of the x-axis like this:
addData gif
What I cannot figure it out is how change the function so that instead of appending them to the end of the x-axis (per the gif), the function prepends the data labels to the front of the x-axis . Essentially when executed, the function adds the new data from the array to the beginning of the x-axis, not the end as per the current function.
Here's the code:
<button id="addData">Add Data</button>
document.getElementById('addData').addEventListener('click', function() {
if (myChart.data.datasets.length > 0) {
var year = YEARS[myChart.data.labels.length % YEARS.length];
myChart.data.labels.push(year);
myChart.data.datasets.forEach(function(dataset) {
dataset.data.push(myChart.data.datasets.data);
});
myChart.update();
}
});
The default labels on load.
var chartData = {
labels: ["09/10", "10/11", "11/12", "12/13", "13/14", "14/15", "15/16", "16/17", "17/18", "18/19"]
The additional labels to add to the x-axis, prior to the fist label on load which is "09/10".
var YEARS = ["08/09", "07/08", "06/07", "05/06, "04/05"];
I'm at a loss, so any help would be greatly appreciated!
Instead of using Array.push(), you should use Array.unshift() as follows:
myChart.data.labels.unshift(year);
myChart.data.datasets.forEach((dataset) => {
dataset.data.unshift(myChart.data.datasets.data);
});

how to filter dataset from labels using ChartJS

I have datasets from my chart and I want to reorganize to make it look better.
This would be my chart
OriginalChart
and this would be my chart when I remove 2 elements labels from it and the idea would be to reorganize everything and delete the elements with the red boxes that have 0 as value or in other words, to only display data with values different than 0 ...
ChartWithDataToOrganize
the idea would be simple, just filtering all data value that is equal to 0, but I don't really if there is a tool for it but also to reverse it as well when you display all data again.
labels
I achieved to see how to hide elements when clicking on the legends but how can I achieve to hide the data inside which is == 0 and then get it reversed when I unhide it and see it like the first time..
legend: {
display: true,
onClick: function(e, legendItem) {
var index = legendItem.datasetIndex;
var actualChart = this.chart;
//If the actual label is not hidden, I set it up as false, otherwise is null
var alreadyHidden = (actualChart.getDatasetMeta(index).hidden === null) ? false : actualChart.getDatasetMeta(index).hidden;
actualChart.data.datasets.forEach(function(e, i) {
var meta = actualChart.getDatasetMeta(i);
if (i === index) {//I check if the selected label is already hidden otherwise I hide it
if(!alreadyHidden){
meta.hidden = true;
}else{
meta.hidden = null;
}
}
});
actualChart.update();
},
},
I'm using ChartJs 2.8.0
on Chrome
I did not put any data entry as labels are just names and the others returnData() are just %
I managed to do it by creating a copy of the actual copy of my datasetsLabels and set up the new configuration and in case I show again the selected label I just replace the new dataSetLabel for the previous dataset that I had before.

AmCharts multiCSV dataloader with Worldmap

I have many CSV files,
I need create from them dataSetSelector
On select I need to get table
But im getting undefined on the end of my table.
And World map based on selected data
And bar chart on maps bottom
So need to get something like
Also screen must be automatically adapted by user screen size
And on mouse over each chart bar to highlight same value on map, the same on table items if possible
On press bar on chart select maps area with this value, the same on table items if possible
valueLegend min value must be min value from tables also as max value
If there are duplicates countries fields in table, then in table show only one country and highlight this row or put * to country name (and be good to show popup on mouse over with all duplicates values), on map put all duplicates into description section splitting by newline and show value with *
What I do
codepen.io
As I first day studing AmCharts, I cant get dataSetSelector and put data from multiple csv files into it. Also I cant find info how to join stock chart with map and table.
So please help to achieve my wysh.
I'll focus on AmCharts-specific stuff in 2-4 since that's the main point of the question. I am also only going to provide a solution for AmCharts version 3.x. You should be able to handle 1 and 5 from there as you can tweak the CSS to more accommodate 1 and add whatever logic you need to satisfy 5.
To get the easy parts out of the way first
dataSetSelector is a stock chart property. The only way to reproduce this functionality for maps and serial charts is to write your own <select> with <option> tags and JavaScript to trigger the desired load action on change.
Stock charts only allow for date-based data. It cannot be used for charts where the category/x axis is just a string like in your screenshot, so it is not an option for you.
Taking the above into account, you need to set and position your dropdown, table and chart/map divs and add code to link everything together.
Some basic HTML and CSS for this layout
HTML:
<div id="container">
<div id="selector-table-div">
<select id="data-selector">
<option value="path/to/csv-1">Dataset 1</option>
<option value="path/to/csv-2">Dataset 2</option>
</select>
<div id="datatable"></div>
</div>
<div id="mapdiv"></div>
<div id="chartdiv"></div>
</div>
CSS:
#mapdiv {
width: 70%;
height: 400px;
float: left;
}
#chartdiv {
width: 100%;
height: 250px;
}
#selector-table-div {
width: 20%;
height: 450px;
float: left;
}
You're on your own for making this more responsive for height. I omitted the datatable stuff and highlighted row for brevity.
In your JS, you'll want to attach a change event to trigger a page update when a different dropdown item is selected:
document
.getElementById("data-selector")
.addEventListener("change", function(e) {
updatePage(e.target.value); //update page calls AmCharts.loadFile, updates/creates the table, map and chart with new data
});
Since you're planning on using both charts and maps on the same page, you need to use amcharts.js, and ammap_amcharts_extension.js. Using amcharts.js and ammaps.js in the same page will cause bugs with both your charts and maps as both files override each other's methods. Your column chart will need serial.js:
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/ammap_amcharts_extension.js"></script>
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
<!-- other stuff omitted -->
Since you want your map to be tied to your data, you'll want to provide an easy way to map each row to a map area. Adding ISO 3166-2 country codes to your CSVs will simplify the process immensely:
country,visits,country_code
USA,2025,US
China,1882,CN
Japan,1809,JP
...
From there, you can set up your map's areas using your newly created country_code column as the MapArea id to activate the area on the map:
var areas = data.map(function(row) {
return {
id: row.country_code, //use ISO code for area ids
value: row.visits
};
});
// ...
AmCharts.makeChart("mapdiv", {
// ..
dataProvider: {
map: "worldLow",
areas: areas
}
});
To capture the min/max and assign it to the area, simply loop through the data and use Math.min/Math.max:
var minValue = Number.MAX_VALUE;
var maxValue = Number.MIN_VALUE;
data.forEach(function(row) {
minValue = Math.min(minValue, row.visits);
maxValue = Math.max(maxValue, row.visits);
});
// ...
AmCharts.makeChart("mapdiv", {
// ..
valueLegend: {
minValue: minValue,
maxValue: maxValue
// ...
}
});
You'll also want to adjust your map/chart creation code in separate functions that know when to either create a new map/chart or update an existing one:
var map, chart;
// ...
function updateMap(data) {
// ...
if (map) {
//makeChart here
}
else {
map.dataProvider.areas = areas;
map.valueLegend.minValue = minValue;
map.valueLegend.maxValue = maxValue;
map.validateData(); // update map
}
For the map, you'll also want to make sure that the map label placement code is called not only upon init, but also when the map is updated:
function updateMap(data) {
// ...
if (map) {
//makeChart here
}
else {
// data update here
}
updateLabel(); //update labels - same code as before
Creating your chart is pretty straightforward. You can add a clickGraphItem and rollOverGraphItem event to select the corresponding map area and highlight the table row on click/hover:
chart = AmCharts.makeChart("chartdiv", {
type: "serial",
dataProvider: data,
// ...
listeners: [
{
event: "clickGraphItem",
method: handleBarInteraction
}, {
event: "rollOverGraphItem",
method: handleBarInteraction
}
]
function handleBarInteraction(e) {
map.selectObject(map.getObjectById(e.item.dataContext.country_code));
var selected = document.querySelector(".selected");
if (selected) {
selected.classList.remove("selected");
}
document
.getElementById(e.item.dataContext.country_code)
.classList.add("selected");
}
Your undefined line is likely coming from an extra newline in your CSV. You can simply check the last item and pop it out of the array before creating your table, map and chart:
var data = AmCharts.parseCSV(response, {
// ...
});
if (data[data.length -1].country === undefined) {
data.pop();
}
Here's a complete codepen with all of the above plus some restructured code. Note that the labels are placed in weird places. The example you pulled the label code from defines exception latitude and longitude variables for you to set up for specific areas. You'll need to figure out those values.

Assigning selected rows as other grid datasource

I am working on setting up a scenario as following:
1) User is shown existing results on first grid
2) User can select multiple results and click an 'Edit' button which will extract the selected items from the first grid
3)Second grid will be populated with the rows the user has selected from the first grid and will allow them to make edits to the content
4)Pressing save will update the results and show the first grid with the rows updated
So far using drips and drabs of various forum threads (here and here), I have managed to accomplish the first two steps.
$("#editButton").kendoButton({
click: function () {
// extract selected results from the grid and send along with transition
var gridResults = $("#resultGrid").data("kendoGrid"); // sourceGrid
var gridConfig = $("#resultConfigGrid").data("kendoGrid"); // destinationGrid
gridResults.select().each(function () {
var dataItem = gridResults.dataItem($(this));
gridConfig.dataSource.add(dataItem);
});
gridConfig.refresh();
transitionToConfigGrid();
}
});
dataItem returns what i am expecting to see with regards to the selected item(s) - attached dataItem.png. I can see the gridConfig populating but with blank rows (gridBlankRows.png).
gridConfig setup:
$(document).ready(function () {
// build the custom column schema based on the number of lots - this can vary
var columnSchema = [];
columnSchema.push({ title: 'Date Time'});
for(var i = 0; i < $("#maxNumLots").data("value"); ++i)
{
columnSchema.push({
title: 'Lot ' + i,
columns: [{
title: 'Count'
}, {
title: 'Mean'
}, {
title: 'SD'
}]
});
}
columnSchema.push({ title: 'Comment'});
columnSchema.push({ title: 'Review Comment' });
// build the datasource with CU operations
var configDataSource = new kendo.data.DataSource({
transport: {
create: function(options) {},
update: function(options) {}
}
});
$("#resultConfigGrid").kendoGrid({
columns: columnSchema,
editable: true
});
});
I have run out of useful reference material to identify what I am doing wrong / what could be outstanding here. Any help/guidance would be greatly appreciated.
Furthermore, I will also need functionality to 'Add New' results. If possible I would like to use the same grid (with a blank datasource) in order to accomplish this. The user can then add rows to the second grid and save with similar functionality to the update functionality. So if there is any way to factor this into the response, I would appreciate it.
The following example...
http://dojo.telerik.com/EkiVO
...is a modified version of...
http://docs.telerik.com/kendo-ui/framework/datasource/crud#examples
A couple of notes:
it matters if you are adding plain objects to the second Grid's dataSource (gridConfig.dataSource.add(dataItem).toJSON();), or Kendo UI Model objects (gridConfig.dataSource.add(dataItem);). In the first case, you will need to pass back the updated values from Grid2 to Grid1, otherwise this will occur automatically;
there is no need to refresh() the second Grid after adding, removing or changing its data items
both Grid dataSources must be configured for CRUD operations, you can follow the CRUD documentation
the Grid does not persist its selection across rebinds, so if you want to preserve the selection in the first Grid after some values have been changed, use the approach described at Persist Row Selection

Putting an html tag into a google chart using google visualization

I'm using google charts in a website and want to make the column names into links rather than strings.
But when I put the tags into the chart it displays them as strings.
I have set {allowHtml:true} but still no luck.
It displays the column name as Visit W3Schools rather than Visit W3Schools and is a string not a link.
The code I am using is as follows:
<script type="text/javascript">
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.arrayToDataTable([
['Job State', 'Job Numbers'],
['Visit W3Schools', #Model.jobCount],
['Visit W3Schools', #Model.liveJobCount],
['Visit W3Schools', #Model.draftJobCount],
['Visit W3Schools', #Model.closedJobCount]
]);
// Create and draw the visualization.
new google.visualization.ColumnChart(document.getElementById('visualization')).
draw(data,{allowHtml:true},
{title:"Current Jobs Statuses",
width:600, height:400,
hAxis: {title: "Job Type"}}
);
}
google.setOnLoadCallback(drawVisualization);
</script>
You are using wrong format of quotation marks and slash. You have to use \" and / instead.
var data = google.visualization.arrayToDataTable([
['Job State', 'Job Numbers'],
['<a href=\"http:\/\/www.w3schools.com\">Visit W3Schools<\/a>', #Model.jobCount],
['<a href=\"http:\/\/www.w3schools.com\">Visit W3Schools<\/a>', #Model.liveJobCount],
['<a href=\"http:\/\/www.w3schools.com\">Visit W3Schools<\/a>', #Model.draftJobCount],
['<a href=\"http:\/\/www.w3schools.com\">Visit W3Schools<\/a>', #Model.closedJobCount]
]);
The solution to this is to use a "click" event handler on the chart, and parse the event information for targetID to filter out all clicks other than those on axis labels:
google.visualization.events.addListener(chart, 'click', function (e) {
// match the targetID of the clicked element to an hAxis label
// and capture the index of the label if matched
var match = e.targetID.match(/hAxis#\d+#label#(\d+)/);
if (match) {
var rowIndex = parseInt(match[1]);
var axisLabel = data.getValue(rowIndex, 0);
// do something with the rowIndex and/or axisLabel
}
});
Here's a jsfiddle with example code you can play with to test this: http://jsfiddle.net/asgallant/fwGmS/

Categories

Resources