Introducing Props2Js
One of my principles of Maintainable JavaScript[1] is to separate your configuration data from your application logic. Configuration data is hardcoded information that your JavaScript uses to work properly. This could be anything such as a URL or a UI string. For example:
function validate(value) {
if (!value) {
alert("Invalid value");
location.href = "/errors/invalid.php";
}
}
function toggleSelected(element) {
if (hasClass(element, "selected")) {
removeClass(element, "selected");
} else {
addClass(element, "selected");
}
}
There are three pieces of configuration data in this code. The first is the string, “Invalid value”, which is displayed to the user. As a UI string, there’s a high chance that it will change frequently. The second is the URL “/errors/invalid.php”. URLs tend to change as development progresses due to architectural decisions. The third is the CSS class name “selected”. This class name is used three times, meaning that a class name change requires changes in three different places, increasing the likelihood that one will be missed.
Configuration data is best extracted from the core application logic, such as:
//Configuration data externalized
var config = {
MSG_INVALID_VALUE: "Invalid value",
URL_INVALID: "/errors/invalid.php",
CSS_SELECTED: "selected"
};
function validate(value) {
if (!value) {
alert(config.MSG_INVALID_VALUE);
location.href = config.URL_INVALID;
}
}
function toggleSelected(element) {
if (hasClass(element, config.CSS_SELECTED)) {
removeClass(element, config.CSS_SELECTED);
} else {
addClass(element, config.CSS_SELECTED);
}
}
This example stores all of the configuration data in the config object. Each property of config holds a single piece of data, and each property name has a prefix indicating the type of data (MSG for a UI message, URL for a URL, and CSS for a class name). The naming convention is, of course, a matter of preference. The important part of this code is that all of the configuration data has been removed from the functions, replaced with placeholders from the config object.
Externalizing the configuration data means that anyone can go in and make a change without fear of introducing an error in the application logic. It also means that the entire config object can be moved into its own file, so edits are made far away from the code that uses the data.
Having an external object managing your configuration data is a good start, but I’m not a fan of storing configuration data directly in JavaScript code. Because such data changes frequently, I prefer to keep it in a simpler file format – one that’s free from worries about missing a semicolon or comma. And that’s when I turned to the Java properties file[2].
Java properties files are incredibly simple. One name-value pair per line and comments begin with a #. It’s really hard to mess up this format. Here’s what the previous example’s configuration data looks like in a Java properties file:
# UI Strings
MSG_INVALID_VALUE = Invalid value
# URLs
URL_INVALID = /errors/invalid.php
# CSS Classes
CSS_SELECTED = selected
Even though I had my configuration data in a Java properties file, I had no easy way of making this data available to JavaScript.
This is why I created Props2Js[3], a simple tool that does just one thing: reads a Java properties file and outputs it in a format that JavaScript can use. Actually, it’s capable of outputting the data into three formats that JavaScript can use: JSON, JSONP, and regular JavaScript.
java -jar props2js-0.1.0.jar --to jsonp --name myfunc --output result.js source.properties
The --to option specifies the output format, either “js”, “json”, or “jsonp”. The --name option specifies either the variable name (for “js”) or the function name (for “jsonp”); this option is ignored for “json”. The --output option specifies the file to write the data into. So this line takes the Java properties file named source.properties and outputs JSONP with a callback function of myfunc to a file named result.js.
Props2Js outputs the properties file mentioned above into JSON format:
{"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",
"CSS_SELECTED":"selected"}
Here’s the JSONP output:
myfunc({"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",
"CSS_SELECTED":"selected"});
And here’s the plain JavaScript option with --name config:
var config={"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",
"CSS_SELECTED":"selected"};
Props2Js is also smart enough to know that you’re assigning to an object property if you include a dot in in the --name option. In that case, it omits the var.
Props2Js is available under an MIT License and is hosted at GitHub[3].
References
- Maintainable JavaScript 2011 by Nicholas C. Zakas
- .properties by Wikipedia
- Props2Js
Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of my employer, my colleagues, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
Both comments and pings are currently closed.




14 Comments
How about using nodeJS to create those JavaScript utilities?! (especially now that it is available on Windows)
Julien Lecomte on December 20th, 2011 at 8:07 am
I had been using almost the same, just with more separated, and readable data:
var config = {
MSG: {
invalid_value: "Invalid value",
enter_text: "Please enter text"
},
URLS: {
login: "/user/login",
register: "/user/register"
},
CSS: {
selected: "sel",
error: "error"
}
}
to use:
alert(config.MSG.ivalid_value);
I think it’s more readable than using underscore separators.
ksgy on December 20th, 2011 at 8:26 am
Awesome! I have the perfect use for this right away
Eric Wendelin on December 20th, 2011 at 8:27 am
I certainly agree that your configuration data shouldn’t be sprinkled throughout the code, but I don’t think that this approach will work in a large scale JavaScript application, which may have 100s of KLOCs, and potentially thousands of configurations.
I tend to use a single, central localisation system for messages and strings displayed to the user (MooTools’ “Locale” class is great for this), as I find (as you note!) that strings change the most frequently. The remaining configuration of each widget/class/module/item/whatever I keep in class (or widget etc.) itself. Using a system like MooTools’ Class options means that these configurations can change depending on context, and that their flow is clear.
All that being said, we run Java on the server, and I still haven’t come up with a reasonable and compatible solution for the Java side of things.
barryvan on December 20th, 2011 at 4:07 pm
@barryvan – What you describe sounds a lot like what I’m describing. You can use Props2js to have multiple different configuration files: one for URLs, one for localization, one for configuration settings. It’s really just a tool to help with a larger system rather than locking you into one way of doing things. Create some files in the way you want, convert to JavaScript, use how you want.
And as you may know, I have a lot of experience with large-scale web applications.
It sounds like you’re talking about a lot of backend configuration information. I’m just talking about the pieces of data you make available to JavaScript, which should be a much smaller subset of your application’s overall configuration. That’s precisely why I created this tool, not so you can take all of your app configuration and push it to the browser, but so you can pick out specific pieces that JavaScript needs access to.
Nicholas C. Zakas on December 21st, 2011 at 8:20 am
@Julien – I thought about using NodeJS. However, several things prevented me from pursuing that. First, I’d have to write a Java properties parser, which I had no interest in doing. Second, Java is still far more ubiquitous than NodeJS. Being able to package up a JAR file and give it to someone is easier than guiding them through NodeJS setup, then npm setup.
Nicholas C. Zakas on December 21st, 2011 at 8:23 am
Hi Nikolas.
This is a question related to the configuration file.
How does the notion of a “config” file go along with modular architecture? (i’m referring to your great slide about scalable javascript).
Meaning – if I create a module and it has references to a “global”/”out-of-my-sandbox” variables, it has a dependency on a global “config” – and it breaks some of the model concepts presented in your slide.
Thanks.
Oren on December 21st, 2011 at 11:57 am
@ncz: Yes, I think we’re on the same page! I think that the difference is that where you might use a prefix (like “MSG_”), I’d prefer some way to use nested JS objects, particularly for localised strings. The difficulty, of course, is that Java .properties files don’t support nesting. Perhaps this could be an enhancement for props2js: the ability to take a .properties file like this:
primary_alpha_one = Aprimary_alpha_two = B
primary_beta = C
secondary_alpha_four = D
and turns it into
{primary: {
alpha: {
one: 'A',
two: 'B'
},
beta: C
},
secondary: {
alpha: {
four: 'D'
}
}
}
Of course, adding js2props would then become extremely handy, and somewhat less trivial. I’ll try to have a bit more of a think about it after the Christmas break. I’m half-tempted to consider an intermediary format that can be used at build time to produce both the client and server portions, excluding values as appropriate. Hmm…
I suppose that part of the problem is where you should draw the line. Should any value (URL, CSS class) be stored in a common area if it’s used more than twice? Thrice? Maybe just once? It’s not an easy predicament from which to extricate oneself: on the one hand, it’s very easy to end up tangled in an ad-hoc nightmare; on the other, it’s also easy to end up in configuration (cf. abstraction) hell. Sigh — I wish there were a panacea for these problems!
(I realise you have a lot of experience with large-scale web-apps. Apologies if I inferred that you didn’t — it certainly wasn’t my intention!)
@Oren: I think that the goal of this project isn’t to provide you with a single place to store each module’s configuration. Even when you’re using a modular structure, there are pieces that are common to the majority of modules: things like the string “OK” or “Cancel” (localised), for example. Taking the CSS example from Nicholas’s post, your modules could be built to either use the value of CSS_SELECTED if both the config object and that value are available, or use its own if not. That way, your modules remain self-contained, but have the ability to function more consistently within the application as a whole.
barryvan on December 21st, 2011 at 5:06 pm
@barryvan – I think you’re taking my example too literally. It’s just an example, I’m not trying to suggest particular naming conventions. I have thought about adding the ability to nest properties in Props2Js, such that anytime it finds a dot in the property name, it creates a new object. It’s certainly worth exploring. And yes, if you use a CSS class, or any other value, two times or more, it’s important to extract it. Chances are that you’ll need to change that value at some point in time and you don’t want to miss any.
@oren – Your modules should never have access to globals or anything out of their sandbox. You would either create a configuration object that’s local to the module or request access to a configuration object through the sandbox.
Nicholas C. Zakas on December 22nd, 2011 at 7:33 am
Your solution is interesting, but I belilve it might be done better.
e.g:
var config={“MSG_INVALID_VALUE”:”Invalid value”,”URL_INVALID”:”/errors/invalid.php”,
“CSS_SELECTED”:”selected”};
`config’ is object variable so it might be changed.
What if you use something like this:
function CONST(key)
{
var config={“MSG_INVALID_VALUE”:”Invalid value”,”URL_INVALID”:”/errors/invalid.php”,
“CSS_SELECTED”:”selected”};
return config[key];
}
alert(CONST(‘MSG_INVALID_VALUE’));
I this case we’ve got realy readonly values.
advantages:
[const-like variables]
+ _readonly values_ which sound nearly like “const”
+ self commented code [in fact your code was self commented as well]
disadvantage:
- object is defined every time we want to read a value – solution below:
var CONST = (function() {
var __c = {a : 1, b :2};
return function(key)
{
return__c[key];
}
})();
CONST(‘a’);
Patryk yarpo Jar on December 23rd, 2011 at 2:53 am
@Patryk – I think you’re missing a key point. Props2Js is part of a configuration solution, not the entire solution. If you want the
configobject to be in a function, then you just need to take the output and stick it into the JavaScript file yourself. Props2Js is designed to fit into a build system, and any build system can easily take the output and insert it anywhere into any file.Nicholas C. Zakas on December 23rd, 2011 at 7:19 am
Thank you for sharing it. However, I have a question on extracting class names.
I personally think these class names ARE the APIs between DOM and JavaScript. Is it really necessary to abstract them out? Besides, if you have this configuration
CSS_SELECTED = selectedand later you are told that the class name should beactive, then you end up with something likeCSS_SELECTED = active. Now they are inconsistent, and it’s likely that when a developer really wants aselectedclass, she will useCSS_SELECTEDwithout knowing the actually value now doesn’t correspond to the const name.Another problem is naming. When the number of class names that need to be abstracted grows large, you will have a long list of const names like “globalNav”, “featureHeading”, “footerCopyright”, etc. Now you have to be careful about name collision. and things start to get ugly with this name soup.
I’ve done things like that before and I just gave up. Would like to know how you will approach it.
Gavin Huang on December 23rd, 2011 at 7:29 pm
@Gavin – The point about abstracting CSS class names is that you don’t need to know the actual class names when you’re writing JavaScript. Whether
CSS_SELECTEDis “selected” or “active” makes absolutely no difference to the JavaScript you’re writing. In fact, you probably never really know the value of any variable in your JavaScript – those details are irrelevant to the functionality. Using a placeholder is a much more maintainable solution.As for the other issues- if you abstract your CSS class names into the same location, or otherwise prefix the class names, you shouldn’t need to worry about naming collisions. This is more of an architectural issue than anything else.
Nicholas C. Zakas on December 26th, 2011 at 8:11 am
@Julien Lecomte
“especially now that it is available on Windows”
Absolutely not a deal-breaker.
Sammy on December 26th, 2011 at 12:15 pm
Comments are automatically closed after 14 days.