I fill a set of text input fields displaying values of an associative array using d3js. The user changes some input values.
var obj = {a: 'Hello', b: 'World'}
var kk = function(d){return d.key}
var kv = function(d){return d.value}
var upd = function(c){
c.select('div.label').text(kk)
c.select('div.input input').attr("value", kv)
}
var data = d3.entries(obj)
var prop = d3.select("div.container").selectAll("div.prop").data(data, kk)
upd(prop)
var eprop = prop.enter().append("div").attr("class", "prop")
eprop.append("div").attr("class", "label")
eprop.append("div").attr("class", "input").append("input").attr("name", kk)
upd(eprop)
prop.exit().remove()
What are the best practices to
a) update the original associative array from DOM (opposite direction)
b) revert the DOM values to the original ones
My current solutions are
a) iterating over the input fields d3.select("div.container").selectAll("div.prop").select('div.input input').each(function(){obj[this.name]=this.value})
b) assigning []
as selection data and then back the original data (I have not found anything like force update or refresh)
Edit (generalized)
Input fields displaying values of an associative array is only a special case, you can imagine any d3js layout applied on a model with user interaction.
But generally:
a) accept the values from the DOM back to the source data
b) revert the DOM to the actual source data
Is there any d3js built-in support or a d3js plugin for this?
You really need to provide some code because from your question it's tough to tell how you are using d3 to manage your inputs. I'm assuming you are data-binding in some fashion so I just coded this up for fun. It's shows some of the cools ways to use enter/append
, data()
and datum()
to manage groups of input and their values:
<!DOCTYPE html>
<html>
<head>
<script data-require="[email protected]" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<button id="report">Report</button>
<button id="reset">Reset</button>
<script>
var data = [{
v: Math.random()
}, {
v: Math.random()
}, {
v: Math.random()
}, {
v: Math.random()
}, {
v: Math.random()
}, {
v: Math.random()
}];
var inputs = d3.select('body')
.append('div')
.selectAll('input')
.data(data)
.enter()
.append('input')
.attr('type','text')
.attr('value', function(d) {
return d.v;
})
.on('blur', function(d) {
d._v = d.v;
d.v = this.value;
});
d3.select('#report')
.on('click',function(){
alert(inputs.data().map(function(d){
return d.v;
}).join("\n"));
});
d3.select('#reset')
.on('click',function(){
inputs.each(function(d){
if (d._v){
this.value = d._v;
d.v = d._v;
d._v = null;
}
});
});
</script>
</body>
</html>
I found simple and built-in solutions for accept:
prop.select('div.input input').datum(function(d){obj[d.key]=(d.value=this.value);return d})
and revert:
upd(prop.datum(function(d){return(d)}))
I tried this already before, but there was an error on this line:
c.select('div.input input').attr("value", kv)
The correct code is here:
c.select('div.input input').property("value", kv)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With