Reputation: 1287
I am using chart.js to make a form a user can fill in and draw chart. The user can fill in fields for x & y labels w/font-size at one point. I save the input as objects into variables and then want to merge the xOptions and yOptions objects into chartOptions object which I can then assign to the chart.js options object. The problem is when I use Object.assign instead of merging it overwrites the first object with the second??? I've read the documentation at MDN over and I cant see where I am going wrong. Any guidance much appreciated. See snippet for details.
$("#form").on("submit", function(e) {
e.preventDefault();
let chartOptions = {};
let xOptions = {};
let yOptions = {};
// First grab form data off the page
const formData = $('form').serializeArray();
formData.forEach(function(value, index) {
if (formData[index].name.includes('Axis')) {
// We have an Axis label or font size form value we need
// to put in the chartOptions object
switch (formData[index].name) {
case 'xAxisLabel':
xOptions = {
scales: {
xAxes: [{
scaleLabel: {
display: true,
labelString: formData[index].value,
fontSize: formData[index + 1].value
}
}]
}
};
break;
case 'yAxisLabel':
yOptions = {
scales: {
yAxes: [{
scaleLabel: {
display: true,
labelString: formData[index].value,
fontSize: formData[index + 1].value
}
}]
}
};
break;
case 'xAxisFont':
// do nothing
break;
case 'yAxisFont':
// do nothing
break;
}
}
})
chartOptions = Object.assign(chartOptions, xOptions, yOptions);
console.log(xOptions);
console.log(yOptions);
console.log(chartOptions);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<form id="form" role="form">
<div class="row">
<div class="col-xs-8">
<div class="form-group ">
<label for="x_axis_label">Label for X-axis</label>
<input type="text" name="xAxisLabel" class="form-control input-lg" id="x_axis_label" placeholder="X-axis label">
</div>
</div>
<div class="col-xs-4">
<div class="form-group">
<label for="fontsize_x">Font Size</label>
<input type="text" name="xAxisFont" class="form-control input-lg" id="fontsize_x" placeholder="i.e. 16">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-8">
<div class="form-group">
<label for="y_axis_label">Label for Y-axis</label>
<input type="text" name="yAxisLabel" class="form-control input-lg" id="y_axis_label" placeholder="Y-axis Label">
</div>
</div>
<div class="col-xs-4">
<div class="form-group">
<label for="fontsize_y">Font Size</label>
<input type="text" name="yAxisFont" class="form-control input-lg" id="fontsize_y" placeholder="i.e. 16">
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-5">
<button type="submit" class="btn btn-primary btn-lg" id="render_btn">
<span class="glyphicon glyphicon-stats" aria-hidden="true"></span>
Render Graph
</button>
</div>
</div>
</form>
Update: in reading up on shallow & deep copying I wanted to document here that another solution would have been to use the $.extend method from jQuery to perform deep copy in JavaScript object. e.g.
$.extend(true, {} , xOptions, yOptions);
Upvotes: 1
Views: 1306
Reputation: 783
AFAIK, there is no way on Javascript to do that automatically. Instead, you can create a new object and include the attributes of the original ones. The problem arises when some attributes are repeated, what should be the value in the merged object? It is up to the application logic (the sum of both, the maximum, etc).
An approach would be:
let result = {};
let keys = new Set(Object.keys(obj1))
Object.keys(obj2).map(x => keys = keys.add(x))
keys.forEach(x => {
result[x] = (obj1[x] || 0) + (obj2[x] || 0);
})
You can take a look at this thread for a similar question: JavaScript/Coffeescript sum objects
Upvotes: 1
Reputation: 101728
The point that you're missing is that Object.assign
does a shallow copy of the properties of the provided objects.
Both xOptions
and yOptions
are objects with a single property: scales
, so the one on yOptions
(which comes after xOptions
in the parameter list) wins out.
The relevant line in the MDN documentation is here:
Properties in the target object will be overwritten by properties in the sources if they have the same key. Later sources' properties will similarly overwrite earlier ones.
To solve this, you'll either need a deep merge (which isn't as simple as using Object.assign
) or, if you're only concerned about the scales
property, you can merge that instead of the objects themselves.
chartOptions.scales = Object.assign(
{},
chartOptions.scales,
xOptions.scales,
yOptions.scales
);
Upvotes: 3