Hello World 1 - this is a very
simple example that demonstrates how to use the BTemplate component to populate a screen.
ComponentGateway Background
First of all, it's important to at least vaguely understand what is going on behind the
scenes in the ComponentGateway
servlet. In a nutshell, the ComponentGateway:
- determines the appropriate ViewCapabilities settings
- create an empty root component
- gives our HelloWorld servlet a chance to add in any components
- renders the component hierarchy
- renders the DOM page returned by our HelloWorld servlet
Looking at the HelloWorld1 Servlet
Ok, so let's take a look at
the actual servlet. Here's the basic flow:
We start by loading the XML generated DOM object:
XMLObject page =
xmlcFactory.create(HelloWorld1HTML.class);
Next we look for the specific node we wish to bind our component to:
Node node =
page.getDocument().getElementById("HelloWorld");
this grabs a reference to the <html> element that is identified by
the "HelloWorld" id in the XMLC template :
<html
id="HelloWorld">
Once we have a reference to the node we can now create a View. The code
looks something like this.
TemplateView tv = new
DefaultTemplateView(node);
The next step is to actually create the model:
TemplateModel tm = new
HelloWorldModel();
Now we can actually create the template component and bind it to the
view and the model:
BTemplate templateComp = new
BTemplate(tm);
templateComp.setView(tv);
In this case we are implementing the model just like we would in Swing, by
using an inner class. We'll look at the specifics a littler further on. For now, just
remember that the model's responsibility is to provide data for the component.
By binding a component to a view, we are saying that the component is
going to assume responsibility for rendering everything that exists below the particular
node that backs the view.
Ok, we're almost done! The final step is to add our template component
to the root component, and then return the DOM page that the ComponentGateway should
render once the component hierarchy has been rendered. The code looks like this:
root.add(templateComp);
return page;
That's it! The ComponentGateway will invoke render() on the root
component, which in turn will cause our template component to render. Once that finishes,
the ComponentGateway will render the DOM structure that we provided. The
resulting page is
returned to the client browser.
Implementing the Model
Alright, now let's look at what it takes to implement the model. BTemplate uses the
TemplateModel interface. Note that unlike Swing components (and unlike the rest of the
Barracuda components too!), the template component can take any number of models. When a
model is added to the component, it queries the model to determine its name. This is
important so that when the component processes directives in a template, it can tell which
model it should query to actually retrieve the data.
We should also note that the Model does not have to be implemented using an inner
class; we just do it that way because it's convenient and that's how you usually do it in
Swing.
The TemplateModel interface defines a number of methods, but by extending from
AbstractTemplateModel we only need to implement two:
public String getName()
public Object getItem(String key, ViewContext vc)
For the getName() method, we return the String "HelloWorld". Now, when the
component encounters a directive in the template, it can map the directive to the model
name HelloWorld and ask it for data.
When we implement the getItem() method, we simply look at the requested key and return
the appropriate data. The BTemplate is smart enough to handle several kinds of data coming
back from the model: Nodes, BComponents, and Strings. There may be occasions however where
you'd like to return a complex block of HTML or another BComponent, but in most cases, it
will be simplest just to return Strings (as we do here).
A Quick Look at Directives
Now is a good chance to look at the actual HTML template, since when dealing with a
BTemplate, the template is responsible for telling the component what data is needed
where. So how do we identify which parts of the template need to by dynamically populated?
Well, in HTML we can use directives embedded in element's class attribute field. A
directive is any String that follows a well defined format:
Dir::<command>.<model>.<key>.<data>
So let's look
at the template, what directives do we see? In the case of the HelloWorld template,
there are several:
<title
class="Dir::Get_Data.HelloWorld.Title">[Title]</title>
<h2 class="Dir::Get_Data.HelloWorld.Title">[Title]</h2>
<p class="Dir::Get_Data.HelloWorld.Descr">[Descr]</p>
<p class="Dir::Get_Data.HelloWorld.Hello">[Hello]</p>
From this we can see that all the directives use the same command:
"Get_Data". This tells the template component that it needs to try and get data
for the key in question and bind it to the particular node containing the directive. So,
as each directive is encountered the component looks up the model specified (in this case
"HelloWorld") and then queries the model for the key. The model returns the data
and the component places it in the template.
Note that if we want to rearrange the page, all we have to do is update the template
and then recompile using XMLC; it doesn't matter how much we change the page -- we don't
have to modify the component or our model unless the designer wants to actually include
new keys which are not currently supported.
Pretty neat, huh?
Miscellaneous Notes
There are a couple of other things worth noting. First, there are three or four other
directives that will be addressed in subsequent tutorials. Second, if you don't like the
idea of putting directives into your HTML, not to worry: they can be stored separately
(either in a file or programmatically). Third, if you don't like the idea of directives at
all, have no fear: you can accomplish the same thing using other components (although none
of them are quite as easy to use as BTemplate!). |