Introduction
This article demonstrates a few of the features of the HTML Component Framework. It is based on the "Hello World" example from the WIKI of the framework's Google code hosted site (http://code.google.com/p/htmlcomponentframework/). The example shows how to place a component in-line within an HTML page, fire an event at it and receive an event raised by it.
Background
Whilst developing web applications, I often faced the following two problems:
- The HTML would get very large as it had to contain the user interface controls in a single monolithic file.
- I did not get any re-use out of the useful and common user interface controls.
To alleviate both of these, I realized that I needed to be able to define the user interface controls, or groups of controls, separately in what I would call an "HTML component".
I had previously written ASP.NET server controls and used JSF and did not like mixing HTML and JavaScript within the server-side code; it was messy and difficult to read. I also found that these technologies broke apart the design-development cycle: the carefully crafted HTML from the design team would need to be altered to get it into the format required by the server; any design changes to the original HTML would then require repeating the process again. Obviously, a component written in one technology is not easily ported to another technology and cannot be tested without running the server. In fact, I believe that these technologies make web application development unnecessarily complicated and the only gain is the automatic management of AJAX invocations (although this is not that difficult to do manually with other frameworks, e.g.: jQuery/YUI AJAX for the client side; Jayrock, Jabsorb for .NET and Java server-side).
I had also used client-side controls supplied from jQuery UI and YUI and found that although useful, they were not elegant to extend or modify due to the way that their HTML was wrapped up inside the JavaScript - again, they broke apart the design-development cycle like the server-side counterparts mentioned above.
For these reasons, I developed a framework that would allow me to create individual components that kept HTML within .html files and JavaScript within .js files. These components could then be tested with just a browser (requiring no compilation or server), re-used for different applications and therefore aid and simplify web application development.
The "Hello" Component Example
The purpose of this example, although very simple and contrived, is to demonstrate the main features of the HTML Component Framework. Refer to the diagram below for a pictorial explanation of the example:
The main "hello" web page contains the label "Name to greet:", a text box in which to enter a name and a "greet" button. It also contains a "hello" component which will be used to display the greeting. The "hello" component contains the greeting and a "hide greeting" button. Initially the greeting within the "hello" component is hidden.
The logic is to have the following steps:
- When the "greet" button is pressed, the label, text box and button are hidden and a
"greet"
event is fired at the "hello" component carrying the name to greet. - On receipt of the event, the component copies the value of the name within the event into the greeting and reveals the greeting.
- When the "hide greeting" button is pressed, the greeting is hidden again and a
"greetingHidden"
event is raised to the main web page. - On receipt of the event, the label, text box and button are revealed so that the process can be repeated.
Using the Code
The code consists of four files:
- HelloComponent.html - The HTML of the "hello" component
- HelloComponent.js - The JavaScript of the "hello" component
- Hello.html - The HTML of the "hello" web page
- Hello.js - The JavaScript of the "hello" web page
Double-click on the file "Hello.html" to launch the example in the default browser.
"Hello" Web Page
Hello.html
The <head>
node contains references to the HTML Component Framework CSS and JavaScript and also the "Hello" web page JavaScript. It also contains two meta tags to be used by the framework:
- The first meta tag describes the location of the event handling code to be used by browsers that don't support HTML5 web messaging. The
name
attribute indicates that this tag contains the event handling code URL and the content
attribute contains the URL; here the event handling code hosted by the Google code servers is used.
<meta name="eventUrl"
content="http://htmlcomponentframework.googlecode.com/svn/examples/Event.html"/>
- The second meta tag describes the component that is to be used in-line within the web page. The
scheme
attribute defines the content of this tag to be in JSON format. The name
attribute indicates that a component is to be included for use within the page. The content
attribute contains JSON that describes the component; it consists of the following properties:
name
- The name that will be used to reference the class of the component url
- The location of the component's HTML
<meta scheme="JSON" name="includeComponent"
content='{"name":"HelloComponent","url":"HelloComponent.html"}' />
An instance of the component is placed in-line within the web page as follows:
<div id="helloComponentId" title="HelloComponent" class="cf_component_marker">
<input type="hidden" name="loadedCallback" value="helloComponentLoaded" />
</div>
The id
attribute assigns a unique identifier to this instance of the component and will be used by the JavaScript to identify this instance. The title
attribute indicates which component is being placed by using its class reference name; in this case it is the "HelloComponent
" included by the meta tag described above. The class
attribute is used to indicate to the framework that this is a component by using the cf_component_marker
marker class (N.B. additional style classes may also be used to style the <div>
element as desired).
Within the <div>
element, a hidden <input>
field has been included to instruct the framework to invoke a function when the component has finished loading. This is necessary so that additional setup can be performed such as attaching event handlers to the component. The name
attribute indicates that the value is a function to be invoked when the component has loaded. The value
attribute contains the helloComponentLoaded
function name that will be explained below.
Hello.js
This JavaScript uses jQuery to ensure, once the web page has loaded, that the HTML Component Framework is started and a "click
" event handler is attached to the "greet" button:
jQuery(window).load(function()
{
ComponentFramework.loadAndRender();
jQuery("#greetButton").click(greet);
});
The following function is invoked by the framework after the component has loaded, by referencing it in a hidden <input>
field as described above. It gets a reference to the component, using the identifier defined in the HTML, and associates the greetingHidden
function to the "greetingHidden"
event that can be raised from the component.
function helloComponentLoaded()
{
ComponentFramework.getComponent("helloComponentId").addEventListener
("greetingHidden", greetingHidden);
}
The greetingHidden
function is defined below. It uses jQuery to reveal the greeting input area.
function greetingHidden()
{
jQuery("#greetingInput").show();
}
The following function is invoked when the "greet" button is pressed (it was attached in the first few lines of JavaScript, above). It uses jQuery to hide the greeting input area, gets a reference to the component and fires the "greet"
event at it. It includes an object with the event that has a property called name
with a value read from the input text box.
function greet()
{
jQuery("#greetingInput").hide();
ComponentFramework.getComponent("helloComponentId").fireEvent
("greet", {name: jQuery("#name").val()});
}
"Hello" Component
HelloComponent.html
The <head>
node of the "Hello" component, like the "Hello" web page, contains references to the HTML Component Framework CSS and JavaScript and also the "Hello" component JavaScript. It also uses a meta tag to point to the location of the event handling code to be used by browsers that don't support HTML5 web messaging. Additionally, it has the following meta tag to indicate to the framework that it is an HTML component:
<meta name="HTMLComponent" />
The events that this component can receive and raise are defined using meta tags. This ensures a tight contract of event definitions, for security, and also ensures that any data passed with events conforms to a specific schema. Attempting to fire or raise events that aren't defined or sending data that doesn't match the schema causes such events to be ignored by the framework. This component has two events, "greet"
and "greetingHidden"
:
- The following defines the
"greet"
event that the component can accept. The scheme
attribute indicates that the content of this tag will be a partial JSON schema (for more information about JSON schemas, see: http://groups.google.com/group/json-schema/web/json-schema-proposal-working-draft and http://tools.ietf.org/html/draft-zyp-json-schema-02). The name
attribute indicates that this is an event that can be accepted by the component. The partial JSON schema is an object that has a property named after the event (in this case "greet"
) and the property's value is a JSON schema. Here, the JSON schema defines an object that must be carried by the event that has a single property, "name"
, that must have a string
value.
<meta scheme="PartialJSONSchema" name="acceptsEvent" content='{"greet":
{"type": "object","properties": {"name": {"type": "string"}}}}' />
- The following defines the
"greetingHidden"
event that the component can raise. It is defined in exactly the same way as for events that it can accept, except that the name
attribute indicates that this is an event raised by the component. In this case, the event is defined as not carrying any object.
<meta scheme="PartialJSONSchema" name="raisesEvent"
content='{"greetingHidden": {"type": "null"}}' />
The rest of the HTML is written as it would be for a normal web page. For this component, it defines a region
to write the greeting to and a button to hide the greeting.
HelloComponent.js
The component JavaScript defines a class that has the same name as the HTML (without the file extension, i.e., "HelloComponent"
). The HTML Component Framework will automatically create an instance of this object when the component is loaded.
The following is the complete code for the "Hello" component. The constructor assigns the hideGreeting
function to the "click
" event handler of the "hide greeting" button. It then defines a privileged function of the name greet
; this matches the name of the "greet"
event that the component can accept - on receipt of this event, the framework will invoke this function. Note that the top level properties of the object carried in the event are split into parameters of the function, in this case the name string
parameter. The greet
function writes "Hello" + name
to the HTML and reveals the greeting. The final, private
, function is hideGreeting
that was assigned to the click
event of the "hide greeting" button. It hides the greeting and raises the greetingHidden
event to the component's container (the "Hello" web page).
function HelloComponent()
{
jQuery("#hideGreetingButton").click(hideGreeting);
this.greet = function(name)
{
jQuery("#helloContainer").text("Hello " + name);
jQuery("#greetingPanel").show();
}
function hideGreeting()
{
jQuery("#greetingPanel").hide();
ComponentFramework.raiseEvent("greetingHidden");
}
}
Points of Interest
I have found from using the HTML Component Framework that it helps to separate functionality of web applications into manageable and easily understood components. This separation has enabled me to concentrate on specific parts of the application and therefore develop in a modular fashion. The ability to test the components directly in the browser has proved invaluable - it is possible to write a web application that runs entirely client-side by mocking the AJAX invocations. This means that client-side and server-side development can be done independently if the AJAX interfaces are agreed upon.
The framework can be used to wrap and extend user controls from other libraries (e.g. jQuery UI or YUI) to help make the development of an application more consistent; however, I have recently been looking at template frameworks to help with the dynamic content of components. Having looked at Closure Tools, I decided that I found its compile stage disruptive to the design-development cycle and so have begun working with jQuery Templates and they are proving to be quite promising and complimentary to the philosophy of the HTML Component Framework.
I am happy with the pre-HTML5 event handling that I coded for the framework and should thank the many resources on the web that describe how to send messages between iframes cross-domain. It was interesting to use HTML5 web messaging to achieve this in the browsers that support it.
Of particular interest with this framework is the ability to use components from different domains and raise events between them. This should mean that if someone hosts their own component, then others can use it without having to copy any code. The owner can therefore immediately roll out fixes and updates by just changing their code on the host.