Set state of nested Array object? - javascript

How can I setstate of value object inside datasets.I tried to set the state by below approach but got the error.
this.setState({ datasets: { ...this.state.datasets, value: labels} });
//code for state
this.state = {
labels: ['January', 'February', 'March',
'April', 'May'],
datasets: [
{
label: 'Rainfall',
backgroundColor: 'rgba(75,192,192,1)',
borderColor: 'rgba(0,0,0,1)',
borderWidth: 2,
value: [65, 59, 80, 81, 56],
}
]
}

In your state, datasets is an array. However, in the setstate call you are destructuring it as an object. You should do this to get it to work:
this.setState({ datasets: [ {...this.state.datasets[0], value: labels }] });

Not sure I'm totally clear on what you are trying to do, but I think the issue is that state.datasets is an array of objects, and you aren't addressing a particular element in the array. You may want to try something along these lines:
this.setState( state => {
let newData = state.datasets[0]
newData.value = labels
return newData
}

Related

How do I mutate Chart.js data using React state?

In my project I wish to toggle between two sets of data: total hours worked each day in current week, and total hours worked each month in the current year. I am able to achieve Chart.js label manipulation in my button handlers, but I am unable to modify the state's data property. What am I missing here? Thank you for helping.
Note: The commented code produce this error statement when uncommented:
Uncaught TypeError: nextDatasets.map is not a function
LineChart.js
const [userData, setUserData] = useState({
labels: Object.keys(daysData),
datasets: [
{
label: "Hours worked",
data: Object.values(daysData),
backgroundColor: "red",
borderColor: "black",
borderWidth: 2,
},
],
});
const onShowWeeklyHandler = () => {
setUserData({ labels: Object.keys(daysData) });
// setUserData({ datasets: { data: Object.keys(daysData) } });
};
const onShowMonthlyHandler = () => {
setUserData({ labels: Object.keys(monthsData) });
// setUserData({ datasets: { data: Object.keys(monthsData) } });
};
You may be encountering issues because you are trying to shallow merge state with the useState hook. React does not support this. See this answer for more info.
If that does not solve your issue, have you looked into react-chartjs-2? You might be able to achieve what you want doing something like the following.
In your parent component (the one that contains the chart):
import { Bar } from "react-chartjs-2"; // import whatever chart type you need
...
const [userData, setUserData] = useState({
labels: Object.keys(daysData),
datasets: [
{
label: "Hours worked",
data: Object.values(daysData),
backgroundColor: "red",
borderColor: "black",
borderWidth: 2,
},
],
});
Then your handlers can be:
const onShowWeeklyHandler = () => {
setUserData({
...userData,
datasets: {
...userData.datasets,
labels: Object.keys(daysData),
data: Object.values(daysData),
},
});
};
const onShowMonthlyHandler = () => {
setUserData({
...userData,
datasets: {
...userData.datasets,
labels: Object.keys(monthsData),
data: Object.values(monthsData),
},
});
};
Finally:
return <Bar data={userData.datasets} />; // adapt for whatever chart type you need

Using Numeral.js to round JSON numbers in %

I am fetching json data from a API and I have to display some of the numbers as %. The json data displays them as 0.00. I tried this method, but it didn't work. I want to use a url to fetch my data, and then use Numeral.js to make the filtered data I got in %. What am I doing wrong? I create a template for my graph. I then make a fetch request to get my data and filter it, so I get the values I need. Then I take that value and try to format it. The new value I want to put on the graph.
const data = {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
datasets: [
{
label: "ADOPTION",
data: [18, 12, 6, 9, 12, 3, 9],
backgroundColor: "rgba(73, 117, 197, 1)",
borderColor: "rgba(73, 117, 197, 1)"
},
{
label: "Target",
data: [0.654, 0.654, 0.751],
backgroundColor: "rgba(236, 123, 46, 1)",
borderColor: "rgba(236, 123, 46, 1)"
}
]
};
// config for graph
const config = {
type: "line",
data: data,
options: {
plugins: {
title: {
display: true,
text: 'data'
},
},
scales: {
y: {
beginAtZero: true
}
}
}
};
// render init block
const myChart = new Chart(
document.getElementById("data"),
config
);
// Fethc block for the graph
async function fetchJSON() {
const url="link";
const response = await fetch(url);
//* loads waiting to complete the request
const datapoints = await response.json();
return datapoints;
}
fetchJSON().then((datapoints) => {
const month = datapoints.map(function (index) {
return index.PERIOD_NAME; //*reffers to jSon word from file
});
console.log(month);
myChart.config.data.labels = month;
myChart.update();
});
fetchJSON().then((datapoints) => {
const total = datapoints.map(function (index) {
return index.ADOPTION //*reffers to jSon word from file
});
var string = numeral(datapoints).format('0.000%');
console.log(string);
myChart.config.data.datasets[0].data = total;
myChart.update();
});
<div class="col-sm-12 col-md-4 col-lg-4">
<canvas id="data"></canvas>
</div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/chart.js"></script>
I'm guessing datapoints is an array. Numeral takes numbers or strings that it trys to convert into a number. You can use datapoints.map(val => numeral(val).format('0.00%')) to format the datapoints elements.

Trying to reactively update chart data, however, the chart does not reflect the changes. Vue.js

Sadly, I am facing some Vue.js reactivity issues yet again. Currently my code renders the chart once on mounted() and then it re-renders the chart every time the data updates thanks to a watcher. This does work (aside from the fact that I'm not sure how to remove the previously rendered charts), however, I am not sure if this is the correct way of doing the update considering I am using Vue. Having the already rendered chart update on data update seems like a better option to me instead of rendering a whole new chart and deleting the old one.
I tried changing the data using this.data = the new data, however, I know that this will not make the object reactive. I also tried using this.$set as is explained here - https://v2.vuejs.org/v2/guide/reactivity.html , however, I am not achieving anything with it. I might not be using it correctly though. I also tried changing just one property of the object which also wasn't affecting the chart.
<template>
<div id="app">
<button #click="updateChart()">X</button>
<canvas id="myChart"></canvas>
</div>
</template>
<script>
export default {
data: function() {
return {
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3]
}],
}
}
},
methods: {
updateChart(){
this.data = {
labels: ['Red', 'Red', 'Red', 'Red', 'Red', 'Red'],
datasets: [{
label: 'EYOOOOOOOOOOOOOO',
data: [6, 7, 8, 9, 10, 11]
}],
}
/*this.$set(this.data, {
labels: ['Red', 'Red', 'Red', 'Red', 'Red', 'Red'],
datasets: [{
label: 'Please Work',
data: [6, 7, 8, 9, 10, 11]
}],
}))*/
/*this.$set(this.data, 'labels', ['Red', 'Red', 'Red', 'Red', 'Red', 'Red'])*/
},
renderChart(data, options){
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: data,
options: options
});
}
},
mounted() {
this.renderChart(this.data, this.options)
},
watch: {
data: function() {
this.renderChart(this.data, this.options)
}
}
}
Try using a deep watcher on data.
{
watch: {
data: {
handler: function(value) {
this.renderChart(this.data, this.options)
},
deep: true,
}
}
}
BTW it is confusing to have a data property called data... I suggest changing it to chartData.
In Vue, only the top level object properties will trigger reactive changes. deep will cause nested objects (in your case labels and datasets) to also trigger an update. Most cases do not need deep, so to eliminate the overhead you need to add it if you want it.
Make sure you do not add any new properties to an existing data object without going through Vue.set. That is the next most common issue when the UI does not update to data changes.
From: https://v2.vuejs.org/v2/guide/reactivity.html
Sometimes you may want to assign a number of properties to an existing
object, for example using Object.assign() or _.extend(). However, new
properties added to the object will not trigger changes. In such
cases, create a fresh object with properties from both the original
object and the mixin object:
Try updating your this.data by doing this:
this.data = Object.assign({}, {
labels: ['Red', 'Red', 'Red', 'Red', 'Red', 'Red'],
datasets: [{
label: 'Please Work',
data: [6, 7, 8, 9, 10, 11]
}],
})

Show No data message when chart has no data

I am trying to show no data message when chart has no data.
Is it correct?
var ctx = document.getElementById('mycanvas').getContext('2d');
var chart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["aa", "bb", "cc", "dd"],
datasets: [{
label: "My First dataset",
backgroundColor: ['red', 'blue', 'green', 'yello'],
data: data.length ? data: [{ label: "No Data", value: 100 }],
}]
},
options: {
legend: {
position: 'right',
labels: {
boxWidth: 12
}
},
tooltips: { bodyFontSize: 12 }
}
});
I am not getting No data message when data length is 0.
Easiest is to use conditional rendering.
Assuming you're using AngularJS (you referenced tag), you can use ngIf directive. If data array is empty, then don't display chart.
<canvas id="myChart" ng-if="data != 0"></canvas>
<span ng-if="data == 0">No data</span>
Else, you can solve it in your script, by checking before or after draw the data length. I recommend you this snippet.
Other solution following your wish could be to adapt drawn data to received data.
if($scope.requestedLeaveTypeCount.length > 0){
data = {
labels: ["EL", "AL", "PL", "CL"],
datasets: [{
label: "My First dataset",
backgroundColor: ['#F0CB8C', '#A9D5D4', '#E8A3D7', '#CFA3FD'],
data: $scope.requestedLeaveTypeCount,
}]
}
} else {
data = {
labels: ["No data"],
datasets: [{
labels:'No data',
backgroundColor: ['#D3D3D3'],
data: [100]
}]
}
}
https://plnkr.co/edit/QXljAeeM4Ul3Y47EPcbq?p=preview

How to get the dataset value in table using Jquery

I was trying to get the label and data value from the following
var barChartData = {
labels: Months,
datasets: [{
label: 'Dataset 1',
backgroundColor: "#09a",
data: [5, 10, 15, 20, 25, 30, 35]
}]
};
I tried using alert(JSON.stringify(barChartData.datasets.data)); but I got output as undefined. Please help me to find out this .
Like Sachin K wrote in the comment.
You forget that datasets is an array containing an object.
Therefor you need
alert(JSON.stringify(barChartData.datasets[0].data))
With the [0] you specify that you want the value from the first element in the array (array's are zero based)
Try this approach..
var barChartData = {
labels: 'Months',
datasets: [{
label: 'Dataset 1',
backgroundColor: "#09a",
data: [5, 10, 15, 20, 25, 30, 35]
}]
};
//For multiple dataset
var data = []; label = [];
barChartData.datasets.map(function(dt) {
data.push(dt.data);
label.push(dt.label);
})
//Single datasets
var data1 = barChartData.datasets[0].data;
var label1 = barChartData.datasets[0].label;
console.log(data, label, data1, label1);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Categories

Resources