Skip to content

Instantly share code, notes, and snippets.

@roryhardy
Last active December 19, 2015 03:49
Show Gist options
  • Save roryhardy/5892586 to your computer and use it in GitHub Desktop.
Save roryhardy/5892586 to your computer and use it in GitHub Desktop.
Dynamically create getter/setter functions for D3 components!
/**
* getSet creates a getter/setter function for a re-usable D3.js component.
*
* @method getSet
* @param {string} option - the name of the object in the string you want agetter/setter for.
* @param {function} component - the D3 component this getter/setter relates to.
*
* @return {mixed} The value of the option or the component.
*/
function getSet(option, component) {
return function (_) {
if (! arguments.length) {
return this[option];
}
this[option] = _;
return component;
};
}

Dynamic getter/setter function generation in JavaScript.

/**
 * Some docs here
 */
component.option = function (_) {
  if (!arguments.length) {
    return option;
  }
  
  option = _;

  return component;
};

Does the above look familiar to you? If you write re-usable D3 components, it probably does. I've found it a little tedious to write them over and over again. So I came up with a way to get around it. Check out the getSet.js file!

You can include this function at the top level of your library and use it in any component. I wrote an example on jsfiddle.net, try it out! I've included simplified version of this function at the bottom for those who only need it for one component. I also included an alternate version for anyone who doesn't want to mess with function binding.

How to use

I like allowing this function to just do its job. I should name my variables well enough that the meaning of them doesn't require heavy documentation. If I do need to document a specific variable, I'd just include it with the declaration so no one has to hunt it down. So lets say you have an opts object which contains your variables, you can do something like this:

var opts = {
  width  : 200,
  height : 50,
  color  : '#000'
};

for (var key in opts) {
  component[key] = getSet(key, component).bind(opts);
}

This will create a getter/setter function for any variable declared in that object. Anything declared outside the object is ignored.

But what if you want to document each getter/setter but still want to use the function? Sure, you can do that too!

/**
 * Some docs here
 */
component.width = getSet('width', component).bind(opts);

/**
 * Some docs here
 */
component.height = getSet('height', component).bind(opts);

/**
 * Some docs here
 */
component.color = getSet('color' component).bind(opts);

Benefits

  • Dynamic
  • Not too much additional overhead
  • Easy to use
  • Enables you to easily simulate public/private variables in JavaScript
  • Saves you time from writing getter/setter functions all the time.
  • Just try it!

Drawbacks

  • You must store the variables in an object.
  • Can lose some documentation
  • Might have to tweak it to meet your needs.
  • IE <= 8 won't care for the function binding without a polyfill.
  • The good news is that most HTML5 shims like Modernizr already have you covered.
  • If you don't include a shim, the MDN page for it has one!

Why would I need to tweak it?

You might need to tweak it since I wrote the function to create getter/setters how I use them. Yours might follow a different format. You also might need a way to upcast your variables via the functor() function. There are plenty of reasons you might want to tweak it.

How to use the alternate version

The alternate version works very similar to the normal version except that instead of binding the function to an object containing options, you pass the object in as a context. Really, you're doing the same thing, but in a different way. Try it!

You might use it like this:

var opts = {
  width  : 200,
  height : 50,
  color  : '#000'
};

for (var key in opts) {
  component[key] = getSet(opts, key, component);
}

or:

/**
 * Some docs here
 */
component.width = getSet(opts, 'width', component);

/**
 * Some docs here
 */
component.height = getSet(opts, 'height', component);

/**
 * Some docs here
 */
component.color = getSet(opts, 'color' component);

Why would I use this version?

You might use this version because you don't like the idea of binding the functions to the options object or to avoid using a shim to support IE8 or lower. Use the version that best suits your needs!

Simple version for single component use

/**
 * getSet creates a getter/setter function for a D3 component. 
 *
 * @method getSet
 * @param  {string} option - the name of the object in the string you want a getter/setter for.
 *
 * @return {function} The getter/setter function.
 */
function getSet(option) {
  return function (_) {
    if (! arguments.length) {
      return context[option];
    }

    context[option] = _;
        
    return component;
  };
}

The simple version should be declared within the component giving it direct access to your context object and the component without passing them in. You'd use it in the same way as the more generalized version.

Contributing

I'm sure there is room for improvement, so:

  • Please contribute by forking
  • Let me know of any enhancements I can make, such as
  • Function improvements
  • Efficiency gains
  • Etc.
  • Let me know of any drawbacks I've overlooked.
// This is the alternate version which doesn't do data binding.
/**
* getSet creates a getter/setter function for a D3 component.
*
* @method getSet
* @param {object} context - the context of your settings.
* @param {string} option - the name of the object in the string you want agetter/setter for.
* @param {function} component - the D3 component this getter/setter relates to.
*
* @return {function} The getter/setter function.
*/
function getSet(context, option, component) {
return function (_) {
if (! arguments.length) {
return context[option];
}
context[option] = _;
return component;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment