You are on page 1of 4

THE SCREEN-WIDGET COOKBOOK

==========================
This document is a series of HOWTOs on using the OFBIZ screen-widget to create s
creens.
* How to check for a permission FOO_ACTION and render a widget if it passes or a
failure widget if it fails
<decorator-section name="foo">
<section>
<condition><if-has-permission permission="FOO" action="_ACTION"/></conditi
on>
<widgets><!-- condition success widgets go here --></widgets>
<fail-widgets>
<!-- condition failure widgets go here -->
<label style="head3">${uiLabelMap.FooActionPermissionError}</label>
</fail-widgets>
</section>
</decorator-section>
* How to test a value in the context to see if widgets should render or not
<section>
<condition><if-compare field-name="objectInContext" operator="equals" valu
e="true" type="Boolean"/></condition>
<widgets> These will render if condition passes </widgets>
<fail-widgets> Otherwise, these widgets will render </fail-widgets>
</section>
- You can place the <section> block anywhere underneath a <widgets> or <fail
-widgets> to acheive nested conditions:
<widgets> ....
<section>
<condition>...</condition>
<widgets> ...
<section>
<condition>....</condition>
<widgets/>
</section>
</widgets>
<fail-widgets><section>...</section></fail-widgets>
</section>
</widgets>

- You can group conditions by wrapping them with <and>, <or>, <xor> and inve
rse the sense with <not>:
<condition><not><and><if-compare one/><if-compare two/></and></not></c
ondition>
- There is a <if-empty field="fieldName"> condition for testing if a value i
s null or not-defined

* Note on using uiLabelMap within screens
Usually the "main-decorator" would define the uiLabelMap, but these cannot be ac
cessed from higher level screens due to the way
<actions> are executed. Avoid using uiLabelMaps within the screens except in the
<widgets> section. Instead, define the name of
the map to be called, such as "CommonLogin" in a parameter and then call this ui
LabelMap from within the template file. For example,
<actions>
<set field="someField" value="CommonLogin"/> <!-- then use in the templa
tes with ${uiLabelMap.get(someField)} -->
<set field="anotherField" from-field="uiLabelMap.CommonLogin"/> <!-- Thi
s won't work! uiLabelMap is not defined yet -->
</actions>
<widgets>
<label style="head3">${uiLabelMap.CommonLogin}</label> <!-- this is the
only exception: you can use the context variables inside <widgets> -->
</widgets>
This especially applies to page titles you might want to set for specific screen
s. That's why you see screen sections define a pageTitleLabel="CommonLogin"
rather than reference uiLabelMap directly.
* How to set data in the <actions> tag for later use
* How to get a single enity in the <actions> section and store in a value
<set field="entityPrimaryKeyId" from-field="parameters.entityPrimaryKeyId"/>
<entity-one entity-name="EntityName" value-name="entityValue"/>
* How to create a two column screen. This comes from a real main-decorator
<!-- This is a box around the main content of the application under the se
ction tabbar -->
<container style="centerarea">
<!-- subheader includes a place to put the title and login/logout butt
ons -->
<platform-specific><html><html-template location="component://crmsfa/w
ebapp/crmsfa/includes/subheader.ftl"/></html></platform-specific>
<!-- a div of class "contentarea" -->
<container style="contentarea">
<!-- a div of id "column-container" -->
<container id="column-container">
TODO: insert code to show explicit columns
<!-- will render shortcuts only if shortcutsScreenName value n
ot empty -->
<include-screen name="${shortcutsScreenName}"/>
<!-- The main column where most of the content goes. -->
<container>
<!-- Draw any messages, such as errors. TODO: use our own
prettier version -->
<platform-specific><html><html-template location="componen
t://common/webcommon/includes/messages.ftl"/></html></platform-specific>
<!-- Finally, include the section screen -->
<decorator-section-include name="section-decorator"/>
</container>
</container>
</container>
</container>
* How to accept a partyId parameter from a link and also have a partyId input p
arameter in a form without conflict
If you wish to have a form that accepts a partyId but also fills it in from a
URL parameter, you may run into an error where the partyId was read as an
array with two values. This is because the screen widget isn't very smart abou
t handling request parameters + forms. It will combine the URL parameter partyId
and the form parameter partyId into an array keyed to parameters.get("partyId"
). order to get around this, do the following at the top of your beanshell
script before the form is rendered.
partyId = request.getParameter("partyId"); // check actual URL paramet
ers first
if (partyId == null) parameters.get("partyId"); // if none, it's safe that
this is a form parameter and not an array
* Setting properties file values
You can set values from .properties files for use in the screens by doing this
:
<property-field field="defaultCurrencyUomId" resource="general" property="curren
cy.uom.id.default" default="USD"/>
where resource denotes which properties file (in this case, "general.properties"
) and the property is the property to use.
Note that this does not have a global="true" attribute, so it can only be set fo
r the screen that is using it, not globally
in a decorator.
* Paginating your forms
You need to set the VIEW_INDEX and VIEW_SIZE in your form-widget so that your fo
rms will paginate properly. They can be set in the
<actions> section of your screen like this:
<set field="viewIndex" from-field="parameters.VIEW_INDEX" type="
Integer" default-value="0"/>
<set field="viewSize" from-field="parameters.VIEW_SIZE" type="In
teger" default-value="20"/>
In opentaps 0.9 and earlier versions, make sure you set those default-values. O
therwise, your first page might be a negative value,
or your screen might show 100 records at a time.
If you are using an older version of ofbiz and need to pass additional
parameters with the Next and Previous links, put a parameter "queryString"
in the <actions> section as follows,
<set field="queryString" value="partyId=${parameters.partyID}&amp;orderId=${
parameters.orderId}"/>
* Order of Execution of <actions>
TODO: describe the problem with the order of execution of <actions>, the
need to set some global variables, and how to work around it. For instance,
we can't do this,
<actions>
<set field="label" value="${uiLabelMap.SomeLabel}"/>
</actions>
<widgets>
<!-- include the GlobalDecorator's main-decorator here, which
has an <actions> that sets the uiLabelMap. -->
</widgets>
That is because the first actions to be executed are the ones in the screen
that is invoked. This is an event-based parsing system and is quite logical.
* How to set a conditional header item
In the financials application, the same payment screen could belong in either
the Receivables or Payables tab, so we had to set it conditionally like this:
in the editPayment.bsh, inside an if block:
parameters.put("headerItem","receivables");
in the PaymentScreens.xml AFTER the .bsh is executed:
<set field="headerItem" from-field="parameters.headerItem"/>
* How to access page attributes in screens
The screen widget sets a Map (actually a javolution FastMap) called "page" whi
ch can be used in the Freemarker and Beanshell templates to access
the parameters set in the screen. Here is an example of it from the ecommerce a
pplication:
page = [leftbarScreenName=leftbar, rightbarScreenName=rightbar, MainColumnStyle=
nocolumns, titleProperty=PageTitleCategoryPage, layoutSettings=[extraHead=<link
rel="stylesheet" href="/content/images/contentForum.css" type="text/css"/>]]
and here is an example of how to use it in the FTL, from header.ftl of the eco
mmerce application:
<#if page.title?has_content>${page.title}<#elseif page.titleProperty?has_content
>${uiLabelMap.get(page.titleProperty)}</#if>

You might also like