Description
Config.js allows developers to configure their applications in an XML block instead of hard-coding values inside their scripts or in JSON objects. The XML can be embedded inside an HTML document or in a separate XML file. The configuration block may contain strings, numbers, arrays and HTML. Furthermore, variables can be inserted into these values, allowing developers to use the XML block as a simple templating system for their applications.
Usage
For the majority of cases, using the library consists of three simple steps.
- Include the EventHelpers.js and config.js script in the <head> of your document:
<script type="text/javascript src="/path/to/EventHelpers.js"></script> <script type="text/javascript" src="/path/to/config-2.0.js"></script>
- The config block can be inserted into your HTML document by defining any HTML node with an
id
ofconfig
. The only content inside this node should be an HTML comment with your configuration values inside. Below is an example XML block that defines a configuration propertyformValidator.errors.mandatoryField
:
<div id=”config”> <!-- <config> <formValidator> <errors> <mandatoryField> The above field is mandatory </mandatoryField> </errors> </formValidator> </config> --> </div>
- Accessing the data inside the configuration block can be done with JavaScript by using the
config.getValue()
function. In the above example, the text inside themandatoryField
tag can be accessed inside JavaScript using theconfig.getValue()
function like this:
document.getElementById("message") = config.getValue("formValidator.errors.mandatoryField");
Note: any call to
config.getValue()
must be made after config.js loads the XML block into memory. If you are initializing your own code with awindow.onload
, you should replace it with a call toconfig.addLoadEvent()
.For example, replace
window.onload=myObject.init
with:
config.addLoadEvent(myObject.init)
You can put as many values inside your XML as you like. For example the values inside the XML configuration block below can be retrieved using config.getValue('
formValidator.errors.mandatoryField
')
, config.getValue('
formValidator.errors.postalCode
')
and config.getValue('
formValidator.alerts.success
')
.
<!-- <config> <formValidator> <errors> <mandatoryField> The above field is mandatory </mandatoryField> <postalCodeError> You did not format the postal code correctly. </postalCodeField> </errors> <alerts> <success> Your changes have been submitted. </success> </alerts> </formValidator> </config> --> </div>
Note the object oriented “dot” notation of the configuration properties, which corresponds to the XML’s tree structure. The usual convention I follow is:
- I code all configuration values that have to do with the same script using the same top level object (in this case, the top-level object is called
formValidator
since it is being accessed by a script called formValidator.js). - I then create properties for that top level object that describe logical categories that I want to configure (e.g. templates, errors, urls, etc).
- I finally have the third level contain the actual configuration values.
While I consider this best practice (insomuch as it works well for me), developers are of course not stuck with this convention, and can format their XML as they please, as long as there are no duplicate property names within an object.
HTML Values
Note that a developer can insert HTML inside the XML string by placing the HTML content inside a CDATA
node:
<config> <formValidator> <templates> <hasError><![CDATA[ <div id="errorMessage"> <p>There are errors with the information you are trying to submit. Please check the form below, correct the errors and resubmit.</p> </div> ]]></hasError> <submissionAlert> If this was a real form, the information above would have been submitted. Since this isn't, we won't submit any of the form information. </submissionAlert> </templates> <errors> <mandatoryField> The above field is mandatory </mandatoryField> </errors> </formValidator> </config>
Note that the CDATA node has to immediately follow the containing tag (in the above example, you’ll see there are no spaces hasError tag). This restriction will probably be relaxed in a subsequent release.
Removing Extraneous Spaces
Let’s look at this example:
<config> <formValidator> <templates> <submissionAlert> If this was a real form, the information above would have been submitted. Since this isn't, we won't submit any of the form information. </submissionAlert> </templates> </formValidator> </config>
If we tried to execute window.alert("formValidator.templates.submissionError")
, we would get an alert box that would look something like this:
Doesn’t that look ugly? This is because the formatting of the text inside it’s configuration block is indented so that it is readable to the developer when viewing the code (Note that all carriage returns are ignored by config.js, but the spaces are left intact). In order to keep the pretty XML formatting while getting rid of the extra spacing inside the alert box, we can set the removeMultispaces
attribute of it’s configuration tag to "true"
:
<config>
<formValidator>
<templates>
<submissionAlert removeMultispaces="true">
If this was a real form, the information above would
have been submitted. Since this isn't, we won't submit
any of the form information.
</submissionAlert>
</templates>
</formValidator>
</config>
This results in an alert box that is formatted as intended:
Dynamic Content:
Injecting variables into the contents of the configuration value can be achieved by using config.getScriptedValue()
. To do so, you first insert the variables into your XML using placeholders surrounded by @-symbols, which we’ll call “at values”:
<config> <formValidator> <submissionAlert> If this was a real form, the information above would have been submitted. Since this isn't, we won't submit any of the form information, including the @ccType@ Credit Card number @ccNum@. </submissionAlert> </formValidator> </config>
In this example, @ccType@
and @ccExp@
are the at-values that will contain dynamic content. The following JavaScript creates an alert containing the configuration value of formValidator.templates.submissionAlert
, complete with dynamic content.
var formToValidate = document.getElementById("formToValidate"); var alertString = config.getScriptedValue( "formValidator.templates.submissionAlert", { ccNum: formToValidate.ccNum.value, ccType: formToValidate.ccType.value });
This replaces the at-value placeholders @ccNum@
and @ccType@
with the values of the form fields ccNum
and ccType
, respectively.
External XML File
Instead of having the XML contained within an HTML comment, you can have the file loaded separately by defining the file name inside the config node like this:
<!-- xml=xml/config.xml --> </div>
Comments
If the XML is embedded in an HTML comment, then comments to the embedded XML must be put inside the XML block using JSP style <%-- --%>
comment notation:
<config>
<formValidator>
<%--
This section contains english text that will
be inserted into the page
--%>
<templates>
<hasError><![CDATA[
<div id="errorMessage">
<p>There are errors with the information you are trying
to submit. Please check the form below, correct the
errors and resubmit.</p>
</div>
]]></hasError>
<submissionAlert removeMultispaces="true">
If this was a real form, the information above would
have been submitted. Since this isn't, we won't submit
any of the form information, including the @ccType@ Credit
Card number @ccNum@.
</submissionAlert>
</templates>
</formValidator>
</config>
If the configuration XML is stored in an external file, use regular XML<!-- --!>
comment notation:
<config>
<formValidator>
<!--
This section contains english text that will
be inserted into the page
--!>
<templates>
<hasError><![CDATA[
<div id="errorMessage">
<p>There are errors with the information you are trying
to submit. Please check the form below, correct the
errors and resubmit.</p>
</div>
]]></hasError>
<submissionAlert removeMultispaces="true">
If this was a real form, the information above would
have been submitted. Since this isn't, we won't submit
any of the form information, including the @ccType@ Credit
Card number @ccNum@.
</submissionAlert>
</templates>
</formValidator>
</config>
Object Arrays
Developers can also define arrays inside configuration files as well, by using the datatype
attribute inside a tag. In the following example, the configuration property formValidator.creditCardForm.inputs
is an array containing three objects:
<config>
<formValidator>
<creditCardForm>
<inputs datatype="array">
<ccType>
<postalCode>
<isMandatory>true</isMandatory>
<%-- Note: We assume Canadian postal codes only - -%>
<pattern>
^[a-zA-Z]\d[a-zA-Z]\s*\d[a-zA-Z]\d$
</pattern>
<unmatchError>
The postal code must be in a valid format.
</unmatchError>
</postalCode>
</ccType>
<ccNum>
<isMandatory>true</isMandatory>
<pattern>^\d{16}$</pattern>
<unmatchError>
The credit card you entered is not valid.
</unmatchError>
</ccNum>
<ccExp>
<isMandatory>true</isMandatory>
<pattern>^(0[1-9]|1[1-2])/\d{2}$</pattern>
<unmatchError>
The expiry date must be in the form yy/mm.
</unmatchError>
</ccExp>
</inputs>
</creditCardForm>
</formValidator>
</config>
Each of these elements have three properties (isMandatory
, pattern
and unmatchError
). Note that each element in the array don’t have to have the same properties, in the same way that a the elements in a JavaScript array doesn’t have the same type of object. In the above example, we can access these array values with the following script:
var inputs = config.getValue("formValidator.creditCardForm.inputs"); var ccExp = inputs["ccExp"] var ccExpError = ccExp.unmatchError;
<mandatoryField> The above field is mandatory </mandatoryField>
Examples
- A very simple example can be found on my blog post Configuring JavaScript Applications.
- A more complicated example showing how to do simple client-side form validation with regular expressions. It uses code that is contained in this manual page.
- The same example as shown above using an external configuration file (i.e. not embedded in the HTML page.
Download
You can get full source here:
config.js v.2.0 and example code (zip format)
This code is released under the MIT License. Although the license doesn’t compel you to do so, I would be happy to hear how this library is being used. I would also like to see how people have improved or added to the code so I can share the changes with others. Again, this is not something the license compels you to do – just consider it a courtesy.
6 responses so far
1 syber // Apr 14, 2011 at 10:05 am
Another great article Zed! I can see this making my life – and my localization work – much tidier :)
2 zoltan // Apr 14, 2011 at 10:38 pm
@Syber: thanks for the feedback. I have found it useful for all my localization needs thus far. Plus it’s great to have a place to store HTML snippets without hard coding them in your scripts. :)
4 Gavin // Apr 5, 2012 at 11:11 am
Nice! This is exactly what i was looking for, coming from a Flex background I wasn’t sure how people configured JS applications but its nice to see a similar solution to the one i’ve employed many times in my Flash/Flex projects.
5 zoltan // May 8, 2012 at 11:06 pm
@Gavin: Thanks for the kudos! I really like this script, and I’m surprised it hasn’t gotten more attention than the other ones on this blog. Appreciate the feedback! :-)
6 kiran // Jul 28, 2015 at 2:25 am
Hello, nice article zed. Can we use Multiple XML files for configurations and decide on runtime that which file we should read the value.???
Give Feedback
Don't be shy! Give feedback and join the discussion.
Please Note: If you are asking for help using the information on this page or if you are reporting a bug with the code featured here, please include a URL that shows the problem you are experiencing along with the browser/version number/operating system combination where the issue manifests itself. Without this information, I may not be able to respond.
denotes a required field.