5. Other View Element Types in Screen Widgets – Apache OFBiz Development

Chapter 5. Other View Element Types in Screen Widgets

After the last two weighty chapters on the biggest components of OFBiz widgets, screen and form, we now look at other widget types that are nonetheless crucial.

We will first look at menu widgets. An application cannot live without menus. users need menus to navigate an application.

We then look at OFBiz widget's integration with FreeMarker, a templating engine that generates XHTML code. OFBiz widgets themselves cannot intermingle directly with XHTML code—that is to say, an OFBiz widget will not work if it is placed directly inside the template engine code. Although this insulation makes for clean code that describes OFBiz widgets neatly, there is much XHTML that cannot be produced using OFBiz widgets.

Although OFBiz widgets are still a work in progress, despite being already full-fledged in several vital areas, it is unlikely that OFBiz widgets will be further extended to reinvent everything good in XHTML. OFBiz widgets already do cover a huge and commonly useful majority of XHTML constructs.

Still, there are parts of XHTML that cannot be easily translated into a simpler structure that is OFBiz widgets, especially parts that describe complex user-interfaces.

As such, we combine OFBiz widgets with FreeMarker, gaining the powerful ability to leverage on the simplicity of OFBiz widgets (80-90 percent of the time) as well as the vast creativity possible with XHTML.

In this chapter, we will be looking at:

  • Menu widgets

  • Creating our first menu widget

  • Embedding or including menu widgets into screen widgets

  • Creating sub-menus with menu widgets

  • Preprocessing for menu widgets, much like the <actions> element for screen widgets.

  • OFBiz's widget (View component) integration with FreeMarker

  • Using FreeMarker files as decorator templates

  • Displaying dynamically created list variables

  • Re-using existing screen widgets

Menu Widgets

Menu widgets are a type of View element inside a screen widget. They provide a convenient means to display a menu with menu items.

Thus, so far, our previous examples and experiments have not been organized into a neat end-user menu. Let us do that now.

Creating Our First Menu Widget

In the folder ${component:learning}\widget\learning, create a file LearningMenus.xml and enter into it this:

<?xml version="1.0" encoding="UTF-8"?>
<menus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation= "http://www.ofbiz.org/dtds/widget-menu.xsd">
<menu name="TopMenu">
</menu>
</menus>

We have just created our first menu widget, but there are no menu items in the menu yet. We'll add some menu items soon. For now, we need to create a common decorator screen widget to include this menu into our screen widgets. In the file LearningScreens.xml, create a screen widget CommonLearningDecorator:

<screen name="CommonLearningDecorator">
<section>
<widgets>
<decorator-screen name="xhtml-decorator">
<decorator-section name="title">
<decorator-section-include name="title"/>
</decorator-section>
<decorator-section name="body">
<include-menu name="TopMenu"
location="component://learning/widget/ learning/LearningMenus.xml"/>
<decorator-section-include name="body"/>
</decorator-section>
</decorator-screen>
</widgets>
</section>
</screen>

Including Our Menu Widget in Our Screen Widgets

Let us add our first screen widget SimplestScreen as the first menu item in our menu widget TopMenu. In the menu widget TopMenu, add the following menu item:

<menu-item name="SimplestScreen" title="Simplest Screen">
<link target="SimplestScreen"/>
</menu-item>

Understanding the Menu Item Attributes

The name attribute is important. It is used to identify the menu item. A menu item can be displayed as "selected" or not. If displayed as "selected", it indicates to the end-user that the page being viewed falls under this menu item.

The title attribute contains the text to show for the menu item.

The sub-element <link> indicates the request to send to the webapp when the menu item is clicked. By default, the <link> element's attribute url-mode (not specified in example) is intra-app, which assumes the request path (SimplestScreen in this case) should be tagged on to the webapp's request path (/learning/control/ in this case). More details on this will be explained later in the book.

Including the Menu Widget via a Decorator Screen Widget

Now we must include our menu widget in our first screen widget SimplestScreen. In the file LearningScreens.xml, create a new screen widget SimplestScreenWithMenu to wrap the decorator CommonLearningDecorator around the screen widget SimplestScreen:

<screen name="SimplestScreenWithMenu">
<section>
<actions><set field="tabButtonItem" value="SimplestScreen"/></actions>
<widgets>
<decorator-screen name="CommonLearningDecorator">
<decorator-section name="body">
<include-screen name="SimplestScreen"/>
</decorator-section>
</decorator-screen>
</widgets>
</section>
</screen>

and change the view map SimplestScreen in the file controller.xml to this:

<view-map name="SimplestScreen" type="screen" page="component://learning/widget/ learning/LearningScreens.xml#SimplestScreenWithMenu"/>

In the <actions> element, we had assigned to a variable tabButtonItem a value of SimplestScreen. The value is the name of the menu item we just created above. This tells the menu widget to highlight the menu item named SimplestScreen.

The default variable name that the menu widget looks out for is tabButtonItem. This can be overridden by specifying in the menu widget (<menu> element) the attribute selected-menuitem-context-field-name.

Fire an OFBiz http request SimplestScreen to webapp learning to see the menu widget in action. At this point, there is only one menu item, so it's not easy to see that it is indeed highlighted. Let us add another menu item.

Add Screen Widget "ConditionalScreen" to the Menu

In the menu widget TopMenu, add the following menu item:

<menu-item name="ConditionalScreen" title="Conditional Screen">
<link target="ConditionalScreen"/>
</menu-item>

In the file LearningScreens.xml, create a new screen widget ConditionalScreenWithMenu to wrap the decorator CommonLearningDecorator around the screen widget ConditionalScreen:

<screen name="ConditionalScreenWithMenu">
<section>
<actions><set field="tabButtonItem" value="ConditionalScreen"/></actions>
<widgets>
<decorator-screen name="CommonLearningDecorator">
<decorator-section name="body">
<include-screen name="ConditionalScreen"/>
</decorator-section>
</decorator-screen>
</widgets>
</section>
</screen>

and finally we change the view map ConditionalScreenWithMenu in the file controller.xml to this:

<view-map name="ConditionalScreen" type="screen" page="component://learning/widget/learning/ LearningScreens.xml#ConditionalScreenWithMenu"/>

This example relies on the screen widget ConditionalScreen which was created at the beginning of Chapter 3.

Return to the browser window and refresh. We now have two menu items to choose from. Click on them to see the difference between highlighted and non-highlighted menu items.

Clicking on menu item Simplest Screen will show the screen SimplestScreen, highlight the menu item Simplest Screen, and show the other menu items (Conditional Screen only, for now) as non-highlighted:

The screen widget ConditionalScreen accepts a request parameter show to display two different scenarios. We will look at sub-menus next.

Sub-Menus and Conditional-Menu Items

Menus can be arranged in any number of levels. Let us now look at creating our first sub-menu. First, we change the screen widget ConditionalScreenWithMenu to include a new menu as a sub-menu. In the screen widget ConditionalScreenWithMenu, insert into the <decorator-screen> element an <include-menu> element like this:

<decorator-screen name="CommonLearningDecorator">
<decorator-section name="body">
<include-menu name="ConditionalScreenSubMenu" location="component://learning/widget/ learning/LearningMenus.xml"/>

That is how we include our sub-menu ConditionalScreenSubMenu, it is as simple as that. The decorator screen CommonLearningDecorator displays the top-level menu. The sub-menu included inside the <decorator-screen> element will be displayed below the top-level menu.

We now create the sub-menu. In the file LearningMenus.xml, create a new menu widget ConditionalScreenMenu:

<menu name="ConditionalScreenSubMenu">
<menu-item name="showWidgets" title="Show &amp;lt;widgets&amp;gt;">
<condition>
<if-compare field-name="parameters.show" operator="not-equals" value="widgets"/>
</condition>
<link target="ConditionalScreen?show=widgets"/>
</menu-item>
<menu-item name="showFailWidgets" title="Show &amp;lt;fail-widgets&amp;gt;">
<condition>
<if-compare field-name="parameters.show" operator="equals" value="widgets"/>
</condition>
<link target="ConditionalScreen"/>
</menu-item>
</menu>

We have two scenarios, to show the <widgets> element or the <fail-widgets>. We put the two scenarios under two menu items.

Note how we used a <condition> element inside each <menu-item> element to conditionally show those menu items. If the condition fails, the menu item is not shown. In this case, we show the menu item to activate the scenario not in effect at the moment. If the scenario in effect is where the <widgets> element is shown, we show the menu item to activate the <fail-widgets> element. And vice versa if the other scenario is in effect.

In the above example, the attribute name of the <menu-item> elements don't matter because they are not referenced anywhere. We cheated by putting the two possible scenarios under the display of a single menu item that serves as a toggle.

When the <widgets> element is shown, the Show <fail-widgets> menu item is presented, allowing the user the view the alternative:

Conversely, when the <fail-widgets> element is shown, the Show <widgets> menu item is presented instead:

Pre-processing Actions for Menu Widgets

The <menu> element can contain an <actions> element. If it is present, it must appear first in the list of elements under a <menu> element.

Let us look at an example that requires pre-processing actions in menu widgets. The screen widget NestedSections has four possible scenarios requiring four menu items.

In the menu widget TopMenu, add the following menu item:

<menu-item name="NestedSections" title="Nested Sections">
<link target="NestedSections"/>
</menu-item>

In the file LearningMenus.xml, create a new menu widget NestedSectionsMenu:

<menu name="NestedSectionsMenu">
<actions>
<script location="component://learning/script/org/ofbiz/learning/nestedSectionsMenu.bsh"/>
</actions>
<menu-item name="OuterTrueInnerTrue" title="Outer true -- Inner true">
<link target="NestedSections?outer=true&amp;inner=true"/>
</menu-item>
<menu-item name="OuterTrueInnerFalse" title="Outer true -- Inner false">
<link target="NestedSections?outer=true&amp;inner=false"/>
</menu-item>
<menu-item name="OuterFalseInnerTrue" title="Outer false -- Inner true">
<link target="NestedSections?outer=false&amp;inner=true"/>
</menu-item>
<menu-item name="OuterFalseInnerFalse" title="Outer false -- Inner false">
<link target="NestedSections?outer=false&amp;inner=false"/>
</menu-item>
</menu>

In the file LearningScreens.xml, create a new screen widget NestedSectionsWithMenu to wrap the decorator CommonLearningDecorator around the screen widget NestedSections:

<screen name="NestedSectionsWithMenu">
<section>
<actions><set field="tabButtonItem" value="NestedSections"/></actions>
<widgets>
<decorator-screen name="CommonLearningDecorator">
<decorator-section name="body">
<include-menu name="NestedSectionsMenu" location="component://learning/widget/ learning/LearningMenus.xml"/>
<include-screen name="NestedSections"/>
</decorator-section>
</decorator-screen>
</widgets>
</section>
</screen>

and change the view map NestedSections in the file controller.xml to this:

<view-map name="NestedSections" type="screen" page="component://learning/widget/learning/ LearningScreens.xml#NestedSectionsWithMenu"/>

In the menu widget NestedSectionsMenu, we have an <actions> element containing a reference to a script file nestedSectionsMenu.bsh. In the folder ${component:learning}\webapp\learning\WEB-INF\actions\learning, create a new file nestedSectionsMenu.bsh and enter into it this:

String outer = request.getParameter("outer");
String inner = request.getParameter("inner");
String fieldName = "tabButtonItem";
if ("true".equalsIgnoreCase(outer)) {
if ("true".equalsIgnoreCase(inner))
context.put(fieldName, "OuterTrueInnerTrue");
else
context.put(fieldName, "OuterTrueInnerFalse");
}
else {
if ("true".equalsIgnoreCase(inner))
context.put(fieldName, "OuterFalseInnerTrue");
else
context.put(fieldName, "OuterFalseInnerFalse");
}

Notice that we are using the same variable name tabButtonItem that was also used in the top-level menu TopMenu. However, this is really a different variable. The context of the parent, which is the screen widget NestedSectionsWithMenu, does have a similarly named variable tabButtonItem that was assigned a value of NestedSections. When that screen widget includes the menu widget NestedSectionsMenu, the screen widget's context is saved in a stack ("pushed into a stack" in technical speak) before creating a new context for the menu widget.

The NestedSections screen is finally clean and easy to experiment with. Prior to this, we've had to manually send GET parameters outer and inner to play with this screen. Now, we just need to click on the menu items:

The above example can be rewritten with a sub-menu of two menu items serving as toggles for the two request parameters outer and inner. However, this can make the menu widget code large and cumbersome. Each toggle button must be coded with four conditional <menu-item> elements, each serving one of the four possible scenarios. In the case above, a total of eight <menu-item> elements must be written. If you're feeling like a lot of typing, you can try this out. Use the menu widget ConditionalScreenMenu as a guiding example.

Finally, as an exercise, try to put the screen widget CompoundedScreen under the menu widget TopMenu as well. Here are a few tips. The screen widget CompoundedScreen needs to be changed to use the decorator CommonLearningDecorator instead of xhtml-decorator directly. The screen widget also needs an <actions> element. The menu widget TopMenu needs a new <menu-item> added to it.

FreeMarker

FreeMarker is a very powerful and versatile template engine written in Java, which for the purposes of the following examples produces XHTML. We can learn everything there is to know about using FreeMarker from the FreeMarker Manual (http://freemarker.org/docs/index.html). The use of FreeMarker in OFBiz is widespread and incredibly useful; it is also discussed in following chapters.

As Decorator Templates

When used in the context of a screen widget, FreeMarker files gain the ability to include screens and include sections, which are equivalent to screen widget's <include-screen> and <decorator-section-include> elements.

An example of rendering a screen widget within a FreeMarker file was encountered in Chapter 3 when we tidied up the mess we had made in the core OFBiz code in the party component. The file ${webapp:party}\party\editcontactmech.ftl used the construct:

${screens.render("component://learning/widget/learning/ LearningScreens.xml#\editcontactmech.extend")}

We now look at how FreeMarker files can be used as decorator templates similar to the decorator screen widgets. Recall that we were using a single header.ftl file before we split it up into two files, header1.ftl and header2.ftl, in order to accommodate the variable title in the XHTML <title> element. This was done in the section called Multiple Content Slots in Chapter 3. Edit the file header.ftl and replace the XHTML <title> element with this:

<title>${sections.render("title")}</title>

and copy over from header1.ftl and insert above the XHTML <title> element this:

<script language="javascript" src="/images/selectall.js" type="text/javascript"></script>
<link rel="stylesheet" href="/images/maincss.css" type="text/css"/>

In the file LearningScreens.xml, change the decorator xhtml-decorator to use the single FreeMarker decorator template. Replace this:

<include-screen name="header1"/>
<decorator-section-include name="title"/>
<include-screen name="header2"/>

with this:

<platform-specific><html>
<html-template-decorator location="component://learning/webapp/ learning/includes/header.ftl">
<html-template-decorator-section name="title">
<decorator-section-include name="title"/>
</html-template-decorator-section>
</html-template-decorator>
</html></platform-specific>

Note how similar the structure of a FreeMarker decorator is to a screen widget decorator's. The <html-template-decorator> element is equivalent to a <decorator-screen> element. The <html-template-decorator-section> element is equivalent to a <decorator-section> element. The FreeMarker code ${sections.render("title")} is equivalent to <decorator-section-include name="title"/>.

Fire an OFBiz http request CompoundedScreen to webapp learning. There should be no visible difference.

In OFBiz the <html-template-decorator> element is only used once. Although this may appear from time to time, its use is not encouraged. To include the sections in the decorator template just use the render(sectionName) method sections object, for example: ${sections.render("main"). This leads to clearer and more efficient code.

Displaying Dynamically Created List Variables

One area where FreeMarker wins over static XHTML is where lists of variables need to be displayed. This is used very often in OFBiz. Here is an example. Edit ${webapp:learning}\includes\header.ftl, and replace this:

<script language="javascript" src="/images/selectall.js" type="text/javascript"></script>
<link rel="stylesheet" href="/images/maincss.css" type="text/css"/>

with this:

<#if layoutSettings.javaScripts?has_content>
<#list layoutSettings.javaScripts as javaScript>
<script language="javascript" src="<@ofbizContentUrl>${javaScript}</@ofbizContentUrl>" type="text/javascript"></script>
</#list>
</#if>
<#if layoutSettings.styleSheets?has_content>
<#list layoutSettings.styleSheets as styleSheet>
<link rel="stylesheet" href="<@ofbizContentUrl>${styleSheet}</@ofbizContentUrl>" type="text/css"/>
</#list>
<#else>
<link rel="stylesheet" href="<@ofbizContentUrl>/images/maincss.css </@ofbizContentUrl>" type="text/css"/>
</#if>

In the file LearningScreens.xml, edit the screen widget xhtml-decorator to add this:

<actions>
<set field="layoutSettings.styleSheets[]" value="/images/maincss.css"/>
<set field="layoutSettings.javaScripts[]" value="/images/selectall.js"/>
</actions>

Iterating Through the List

The above actions set the location as a String for one style sheet and one JavaScript file into an array, which is then iterated through by the FreeMarker template. We could have placed the location of as many files as we liked into these arrays, and the XHTML would have been created accordingly.

For example, in the above actions we could have put:

<set field="layoutSettings.styleSheets[]" value="/images/maincss1.css"/>
<set field="layoutSettings.styleSheets[]" value="/images/maincss2.css"/>
<set field="layoutSettings.styleSheets[]" value="/images/maincss3.css"/>

and the FreeMarker code in the template:

<#list layoutSettings.styleSheets as styleSheet>
<link rel="stylesheet" href="<@ofbizContentUrl>${styleSheet}</@ofbizContentUrl>"
type="text/css"/>
</#list>

will ultimately output the following XHTML:

<link rel="stylesheet" href="/images/maincss1.css" type="text/css"/>
<link rel="stylesheet" href="/images/maincss2.css" type="text/css"/>
<link rel="stylesheet" href="/images/maincss2.css" type="text/css"/>

This handy features allows us to vary the number of variables passed into the template in different screens without us having to alter the template code.

Bringing it All Together

Now that we are coming to the end of our exploration into screen widgets and understand a bit more about how the different parts hang together, let's start to use the existing screen widgets that have been designed to give the components in OFBiz a consistent "look and feel" and page structure. These widgets, since they are common to webapps in both the applications folders (order, party) and the framework folders (webtools), can be found in the common folder framework\common\widget. Take a look at the GlobalDecorator screen widget inside the CommonScreens.xml file. This is the top level decorator and is responsible for the addition of the stylesheets, adding the header, adding the top tabs, and the overall structure of the page. There is the inclusion of the messages.ftl template, which is responsible for displaying the error and success messages. The <container> tags are outputted as XHTML <div> tags, with the id attribute remaining as id and the style attribute becoming class. So <container style="centerarea"> becomes <div class="centerarea">. As such, we would expect to see a .centerarea style class defined in the file framework\images\maincss.css.

To change our learning component, we are going to take a look at how an existing component is built and apply the necessary changes to our LearningScreens.xml file. First of all, fire an http request to partymgr and login using the admin, ofbiz user. The main screen of the Party Manager is the Find Party screen. We are going to see how this page is built. Our first step is to open the Party Managers webapp's controller.xml file and check which screen is referenced in the <view-map name="main"> element. This leads us to the screen: component://party/widget/partymgr/PartyScreens.xml#findparty.

This screen is a simple FreeMarker template that is wrapped in a Decorator called main-decorator, you will notice that its location is a parameter ${parameters.mainDecoratorLocation}. This parameter is specified in the web.xml file for this webapp:

<context-param>
<param-name>mainDecoratorLocation</param-name>
<param-value>component://party/widget/partymgr/ CommonScreens.xml</param-value>
<description>The location of the main-decorator screen to use for this webapp; referred to as a context variable in screen def XML files.</description>
</context-param>

Let's start by adding a similar entry into our learning webapp's web.xml file:

<context-param>
<param-name>mainDecoratorLocation</param-name>
<param-value>component://learning/widget/learning/ CommonScreens.xml</param-value>
<description>The location of the main-decorator screen to use for this webapp; referred to as a context variable in screen def XML files.</description>
</context-param>

If we want to quickly change which main-decorator out of all screen widgets to use, we can change the location in just one place. The main-decorator has been separated out into a separate file because it is used by all of the other Screens.xml files within this component. The file component://learning/widget/learning/CommonScreens.xml does not yet exist, so create that.

Open the component://party/widget/partymgr/CommonScreens.xml file and copy and paste the whole main-decorator widget into our newly created CommonScreens.xml file.

There are a few new concepts here that will be explained as we work our way through this decorator.

The User-Interface Labels

We can see there are a number of property-map resources defined (PartyUiLabels, AccountingUiLabels). These refer to properties files found in the component's config directory. On opening the party\config directory we can see that there are a number of PartyUiLabels_xx.properties files and one PartyUiLabels.properties file. The files containing an underscore are files that have been translated into a different language and are used automatically, dependant on the user's locale. This is explored further in the chapter. Note that in current developed version of OFBiz all *UiLabels*.properties files have replaced by *UiLabels.xml files.

The line <property-map resource="PartyUiLabels" map-name="uiLabelMap" global="true"/> places the referenced properties file into a map called uiLabelMap in the global context. This makes the properties file available to all screen widgets, (including form and menu widgets), FreeMarker templates and their associated action scripts that are wrapped by this decorator.

When we want to use one of these properties, we simply pull the value from the map by using uiLabelMap.propertyName. For example, if we want to display the Company Name in a Freemarker template, we would put ${uiLabelMap.PartyCompanyName}.

Our component does not yet have a uiLabels properties file. In a new directory ${component:learning}\config, create a new file called LearningUiLabels.properties.

In it, place four properties:

LearningComponent=Our Learning Application
LearningCompanyName=Learning Company Name
LearningCompanySubtitle=Learning Company Subtitle
LearningMain=Main

The name of this file is very important. For the framework to recognize it as a UiLabels.properties file, it has to follow the correct naming convention. Also, as we do not need to give a location to the files in the <property-map> element, it is important that the names are unique. We must make sure there is only one file called LearningUiLabels.properties in the whole project.

This config directory must be on the classpath for it to be recognized so open the file ${component:learning}\ofbizComponent, and add the line:

<classpath type="dir" location="config"/>

immediately underneath the other <classpath> elements.

Our learning component is much more simple than the party manager component from where we copied this screen widget from and does not need all of these properties. Delete five of the <property-map> elements, leaving the reference to CommonUiLabels and replace them with just our newly created uiLabels:

<property-map resource="LearningUiLabels" map-name="uiLabelMap" global="true"/>

We can now change the references to PartyCompanyName and PartyCompanySubtitle to match our new LearningCompanyName and LearningCompanySubtitle properties.

The addition of the extra stylesheets and JavaScripts are not needed and can be deleted and the activeApp can be set to learning.

The contents of CommonScreens.xml should now simply be:

<screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/ widget-screen.xsd">
<screen name="main-decorator">
<section>
<actions>
<!-- base/top/specific map first, then more common map added for shared labels -->
<property-map resource="LearningUiLabels" map-name="uiLabelMap" global="true"/>
<property-map resource="CommonUiLabels" map-name="uiLabelMap" global="true"/>
<set field="layoutSettings.companyName" from-field="uiLabelMap.LearningCompanyName" global="true"/>
<set field="layoutSettings.companySubtitle" from-field="uiLabelMap.LearningCompanySubtitle" global="true"/>
<set field="layoutSettings.headerImageUrl" value="/images/ofbiz_logo.jpg" global="true"/>
<set field="activeApp" value="learning" global="true"/>
<set field="appheaderTemplate" value="component://learning/webapp/ learning/includes/appheader.ftl" global="true"/>
</actions>
<widgets>
<include-screen name="GlobalDecorator" location="component://common/widget/ CommonScreens.xml"/>
</widgets>
</section>
</screen>
</screens>

Adding the appheader

You may have noticed that the appheaderTemplate location has been changed. The GlobalDecorator needs this value to be set or it will throw an exception. In the includes directory add the appheader.ftl file and enter into it:

<#assign selected = headerItem?default("void")>
<div id="app-navigation">
<h2>${uiLabelMap.LearningApplication}</h2>
<ul>
<li<#if selected == "main"> class="selected"</#if>> <a href="<@ofbizUrl>main</@ofbizUrl>"> ${uiLabelMap.CommonMain}</a></li>
<#if userLogin?has_content>
<li class="opposed"><a href="<@ofbizUrl>logout</@ofbizUrl>"> ${uiLabelMap.CommonLogout}</a></li>
<#else>
<li class="opposed"><a href="<@ofbizUrl>${checkLoginUrl?
if_exists}</@ofbizUrl>">${uiLabelMap.CommonLogin}</a></li>
</#if>
</ul>
<br class="clear"/>
</div>

We must now tell our existing screen widgets in the LearningScreens.xml file to use this new main-decorator.

Simply find all instances of:

<decorator-screen name="CommonLearningDecorator">

and replace them with:

<decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">

As we have added and changed some .properties files and added to the web.xml file, we must restart OFBiz to see these changes take effect. Once restarted, fire an http request to the webapp partymgr and take a look at the applications tabs along the top of the screen.

Clicking on the Learning tab will still give a white screen and an error in the logs. The OFBiz framework automatically produces the tabs for each component, and it automatically creates the links pointing to main. We must therefore define a request-map called main in our controller.xml file by adding:

<request-map uri="main">
<response name="success" type="view" value="main"/>
</request-map>

And add the view-map:

<view-map name="main" type="screen" page="component://learning/widget/learning/ LearningScreens.xml#SimplestScreenWithMenu"/>

Since the main request is usually the first port of call when accessing the webapps, it is common practice for it to be the first request-map element in controller.xml files. Once this is in place, select the Learning tab.

As soon as you enter the Learning Application the tabs disappear. The tabs are only displayed to users who are logged in, and this means logged into the individual webapp. There is no way to log into the application as a whole, only each component individually.

In the next chapter we will be exploring how we can add login functionality to our Learning component.

Summary

With that, we've covered the most developed and most stable parts of OFBiz widgets and we now have a working OFBiz application. The widgets we have covered, screen, form, and menu, are what we will be using almost all of the time. For more creative and complex user-interfaces, we fall back on FreeMarker.

As powerful as FreeMarker already is, the OFBiz Widget Engine integrates with it such that it gains the invaluable organizational structure of OFBiz widgets. FreeMarker under OFBiz widgets can utilize the Widget Engine's decorator model giving the ability to neatly compose larger screens with smaller screens or sections, and even some access to the database (covered later in the book).

In this chapter, we looked at:

  • Menu widgets, and creating them via <menu> elements within a <menus> element, like how we placed <form> elements within a <form> element.

  • Creating our first menu widget in the file LearningMenus.xml.

  • Embedding or including menu widgets into screen widgets via <include-menu> elements.

  • Creating sub-menus with menu widgets, simply by placing sub-menus under top-level menus.

  • Pre-processing for menu widgets, much like the <actions> element for screen widgets. The example given serves to streamline conditionals in menu widgets with succinct pre-processing scripts.

  • OFBiz's widget (View component) integration with FreeMarker via elements <platform-specific> and <html>.

  • Using FreeMarker files as decorator templates via elements <html-template-decorator> and <html-template-decorator-section>.

  • Displaying dynamically created list variables in FreeMarker using the <#list ... as ...> FreeMarker construct.

  • Reusing existing screen widgets and creating an OFBiz application from existing screens.

That wraps up our study of the View component of OFBiz. In the next chapter, we will look at the flow through OFBiz—The Controller.