-
Notifications
You must be signed in to change notification settings - Fork 87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Shorthand the creation of reusable properties. #67
Comments
+1, that'd remove a bunch of boilerplate code. |
This is definitely a common situation: needing to create getter/setter functions. The problem with making boilerplate ones is as follows:
var chart = d3.select("svg")
.chart.("SomeChart")
.height(100)
.width(100); What this means is that any internal changes that need to happen based on these settings will either need to be passed in by the user. As a result, I wouldn't recommend the callback approach, but rather broadcast an event like: this.trigger("change:" + prop); which would allow subscribing to it in a chart's initialize method and possibly on an initialized chart as well.
margins: function(newMargins) {
var chart = this;
if (arguments.length === 0) {
return chart.state.margins;
}
// keep existing attributes, but overwrite the ones that were passed in.
chart.state.margins = _.extend(chart.state.margins, newMargins);
chart.trigger("change:margin", chart.state.margins);
if (chart.data) { chart.draw(chart.data); }
return this;
}, In this example, I'm actually changing the colors of a chart by taking in an array of colors but then updating a scale! colors: function(val){
if(arguments.length > 0){
this.state.colors = d3.scale.ordinal()
.range(val);
this.trigger("change:colors", this.state.colors);
} else {
return this.state.colors;
}
if (chart.data) { chart.draw(chart.data); }
return this;
}, Most importantly, I'll let @jugglinmike chime in on this. |
Sanitizing values is another thing to consider. Some of the charting logic might only work if certain properties are definitely functions, numbers, etc. That could be handled by passing a function to the proposed d3.chart("SomeChart", {
initialize: function(options) {
// Configurable properties.
this.prop("width", null, true);
this.prop("height", null, true);
},
// Create re-usable methods that act as getters/setters.
prop: function(name, sanitize, redraw) {
var chart = this;
// default to the identity function if no sanitization function is provided
sanitize = sanitize || function(_) { return _; };
// rebind the sanitization function so it has access to the chart
sanitize = sanitize.bind(chart);
this[name] = function(val) {
if (arguments.length === 0) {
return chart["_" + name];
}
// Reassign the value.
chart["_" + name] = sanitize(val);
chart.trigger("change:" + name, chart["_" + name]);
if (redraw && chart.data) { chart.draw(chart.data); }
return chart;
};
}
}); This same sanitization function could also be used to implement the custom logic from the margins and colors examples. this.prop("margins", function(margins) { return _.extend(this.state.margins, margins); }, true);
this.prop("colors", function(val) { return d3.scale.ordinal().range(val); }, true); |
@tbranyen Thanks for bringing this up. Irene and I have discussed this a bit, @iros I agree about using events rather than a callback. One minor thing: chart.margins({ top: 23 }); // triggers `change:margins`
chart.margins({ top: 23 }); // triggers `change:margins`
@krishantaylor Regarding the initialize: function() {
this.on("change:colors", function() {
if (this.data) {
this.draw(this.data);
}
});
} ..or better yet: redraw: function() {
if (this.data) {
this.draw(this.data);
}
},
initialize: function() {
this.on("change:colors", this.redraw, this);
} Regarding the
"sanitize" has a specific meaning, but in that implementation, it is more of a |
@jugglinmike I like moving the redraw to the second option you posted. And you're right on the naming of my |
Iros, to me, having a draw call in a setter doesn't seem like the best idea. I suppose it can make things easier for less advanced users, but I always called update (draw) manually after setting something that would change thing immediately. One main reason for this is if I was setting more than 1 thing that, in your case, would call update/draw. Instead of multiple calls to update/draw, I can just call it once at the end. |
I've noticed in much of the documentation and examples the common definition of reusable properties, such as: width, height, tickFormat, etc. These functions are bulky and not particularly dry in my opinion.
The following proof of concept illustrates the concept:
Is this a common enough task to warrant a convenience method within d3.chart itself? I think it could easily cut down on a significant amount of common repeated methods.
The text was updated successfully, but these errors were encountered: