Code does nothing more than define a set of instructions for a computer to execute. Data is frequently passed around and modified by those instructions, ultimately producing a result. The problem comes when the data must change. There’s a risk of creating an error any time you edit source code, and editing code just to change some data value introduces unnecessary risk for something that shouldn’t affect the surrounding instructions. Well-designed applications keep vital data outside of the main source code to ensure worry-free editing.
Configuration data is any hardcoded value in an application. Consider the following example:
// Configuration data embedded in code
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 good 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.
These are all considered configuration data, because they are hardcoded values that may change in the future. The following are all examples of configuration data:
URLs
Strings that are displayed in the UI
Repeated unique values
Settings (i.e., items per page)
Any value that may change
The key point to remember about configuration data is that it changes, and you don’t want to be modifying your JavaScript source code because someone changed his mind about a message to display on the home page.
The first step in separating configuration data from code is to externalize the configuration data, which means getting the data out of the middle of your JavaScript code. Here’s the previous example with the configuration data externalized:
// 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 and
replaced with placeholders from the config object.
Externalizing the configuration data means that anyone can go in and
make a change without 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.
Configuration data is best stored in a separate file to create a clean separation between it and application logic. A good starting point is to have a separate JavaScript file for configuration data. Once the configuration data is in a separate file, it opens up more possibilities for managing that data. A worthwhile option is moving your configuration data into a non-JavaScript file.
Even though you’re writing a JavaScript application, JavaScript isn’t a great way to store configuration data. That’s because the syntax is still that of a programming language, so you need to be sure you haven’t introduced syntax errors. If you end up concatenating JavaScript files together, a syntax error in a single line breaks the overall application. Configuration data truly belong in files that are hard to format incorrectly, and once you have that file, it is trivial to convert the configuration data into a JavaScript format automatically.
One of my favorite formats for configuration data is a Java properties file. Java properties files are simple
name-value pairs in which each pair takes a single line (unless you put in
a multiple-line sequence) in the form name=value. It doesn’t matter if there are
spaces around the equals sign, so even that syntax isn’t hard to get
right. Comments are indicated by preceding the line with a # character. Here’s an example:
# UI Strings MSG_INVALID_VALUE = Invalid value # URLs URL_INVALID = /errors/invalid.php # CSS Classes CSS_SELECTED = selected
This properties file contains the same properties as the config object from the
previous example. Notice how much simpler the file layout is. There are no
quoted strings, which means that you don’t have to worry about proper
escaping or forgetting to close a string. There are also no semicolons or
commas to worry about. You can simply put in your data and not worry about
JavaScript syntax at all.
The next step is to convert this file into something that’s usable by JavaScript. There are generally three formats in which you want your configuration data. The first is JSON, which is useful when embedding your data into another file or setting up data for retrieval from the server. For instance:
{"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",
"CSS_SELECTED":"selected"}The second is JSONP (JSON with padding), which returns the JSON structure wrapped in a function:
myfunc({"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",
"CSS_SELECTED":"selected"});Because JSONP is valid JavaScript, you can concatenate this code into other files to give them access to the data.
The last option is plain JavaScript, in which you assign the JSON object to a variable to use later, as in:
var config={"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",
"CSS_SELECTED":"selected"};As with JSONP, the plain JavaScript version can be combined with other JavaScript files easily once produced.
For these common use cases, I have created a tool called Props2Js that reads Java properties files and outputs the data into one of these three formats. Props2Js is free and open source, available at https://github.com/nzakas/props2js/. It works like this:
java -jar props2js-0.1.0.jar --to jsonp --name myfunc
--output result.js source.propertiesThe --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.
Using a tool like Props2Js allows you to keep configuration data in a simpler file format and then easily convert your configuration data into a format that is usable by JavaScript later.