Hello World 1b - this example
demonstrates how a model can pass back more than just Strings. In this case we also return
Nodes and BComponents.
Returning a Node
In general, this example is very similar to the HelloWorld1
servlet. There are, however, a couple of key differences.
First of all, notice that in this example we are using two templates: one for the Body and
one for the Footer.
If you examine the source for
HelloWorld1b, you will see how the model loads and returns a complex Node for the
footer. The actual code looks somethign like this:
else if (key.equals("Footer")) {
XMLObject footer = xmlcFactory.create(HelloWorld1b_footerHTML.class);
Node footerNode = footer.getElementById("Footer");
return vc.getElementFactory().getDocument().importNode(footerNode,
true);
}
Let's talk about what is actually going on here. Basically, when the component
encounters the directive requesting the footer data, the code listed above gets invoked.
We start by loading the Footer page and grabbing a reference to the actual Footer node
(specified by the "Footer" id).
Now, before we return the node, we must first import it into the current document
(since the node is currently a child of the Footer page). To do this, we get a copy of the
ElementFactory from the ViewContext. The element factory provides the necessary link to
the document we are actually working on; we invoke importNode() and then just return the
result.
So what happened in the component to make it work? Well, the component requested the
data from the model when it encountered the following node in the template:
<p
class="Dir::Get_Data.HelloWorld.Footer" align="center">[Footer goes
here]</p>
When the model returns data of type Node, it discards the template node and uses the
models node instead. Pretty simple!
Returning a BComponent
We also return a BComponent in this example, which you seen in final form as an email
link in the upper right hand of the page. Although here the component returned is
intentionally quite simple (the BLink
component is just a simple extension of BAction) it
demonstrates the basic functionality quite nicely.
Let's start by looking at the model source. The line in question looks something like
this:
else if (key.equals("Header"))
return new BLink("Email Christian",
"mailto:...@lutris.com");
Here we can see that when the component requests data for the Header key, we
instantiate a BLink component and return that. The first parameter represents the text of
the link; the second param represents the URL.
When the BLink is rendered, it makes sure everything gets put into the proper
format and displayed correctly. Now, we could obviously return the string representation
of the email link if we desired, but its generally a lot easier to let the component take
care of that for us, especially when the components are intelligent enough to render
themselves properly in multiple *ML formats.
Now, you may be thinking, "Dang, this is cool! What actually goes on behind the
scenes to make this work?" (If you're not thinking this, skip to the next section).
Ok, you're still reading so I assume you actually want to know. Basically, when a
component finds that the data returned from its model implements BComponent, it says
"Oh, I have an object that knows how to render itself in the DOM. Great, that means I
don't have to worry about it." All the parent component has to do is make sure of two
things:
first, the views for the returned component must be part of the DOM so
that when the page is rendered the changes made by the component will be reflected (in the
case of our BLink, there really is no view yet so the parent component creates a default
node using the ViewContext and adds it in)
- second, the returned component must be added to the parent component hierarchy so that
it too will actually get a chance to render. The component does this by adding it in as a
child component, while keeping track of it at the same time so that it can be removed once
the render process is completed.
As long as the second condition is met, the component returned from the model will get
rendered (along with any other children the parent component might contain), allowing it
to update the portions of the DOM it is bound to. Assuming the first condition is also met
(that the DOM it is updating is part of the master template we're working on), then the
data contained in the child component will ultimately be reflected in the page that gets
returned to the user.
Whew! If that description leaves you still feeling a little fuzzy, don't worry! All you
really need to know is that a Model can return Node, BComponent, or String data and the
components should always be able to handle it. Very cool. |