opentaps Form Macros Documentation

Important Note to all Developers: Please keep this document up to date! If you make changes to the form macros, please also change this document.

Why It Exists

Many of us are constantly creating forms for our users to enter and display data. Most of those forms share common elements: text input fields, date input fields, drop downs, etc. etc. Wouldn't it be nice to have a tool which helps you make and manage? At the same time, the tool should still give you control over the design of your form, so you don't end up with an ugly cookie-cutter look for all your forms. You should be able to add form elements in HTML when they are appropriate, or completely change the layout and design of your forms by just changing the HTML.

The opentaps Form Macros were created for this reason: to make writing forms easier, while still giving you control over the final layout. The macros help you design form elements such as input rows, select boxes, and date fields more efficiently but do not force you to use them--you can write some form elements with them, write others in HTML or anything else. It is completely written in Freemarker and can be accessed from any Freemarker page, so you can combine opentaps Form Macros, HTML, Freemarker in the same form. It is also easily extend or re-skin: you edit the form macros file and make your changes there, without updating XSD definitions or Java code.

How It Works

First, you must make sure that the opentaps form macro importing tool is loaded. This can be done by including the following code in your beanshell (.bsh) script for your page. They can be put in main-decorator.bsh so that the form macros would work for your entire webapp:

    loader = Thread.currentThread().getContextClassLoader();
    globalContext.put("import", loader.loadClass("org.opentaps.common.template.freemarker.transform.ImportTransform").newInstance());
    globalContext.put("include", loader.loadClass("org.opentaps.common.template.freemarker.transform.IncludeTransform").newInstance());

The form macros are located in an FTL file in your opentaps-common directory:

opentaps/opentaps-common/webapp/common/includes/lib/opentapsFormMacros.ftl
To use it, simply include the form macros in your Freemarker (FTL) page, like this:
<@import location="component://opentaps-common/webapp/common/includes/lib/opentapsFormMacros.ftl"/>
<@import /> is an opentaps Freemarker extension which allows macros to be imported into the current context from any file in your opentaps applications.

IMPORTANT: There must be no spaces in your file path!

Now you are ready to use the form macros, like this:

    <#list inventoryProduced as inventoryItemProduced>
        <#assign inventoryItem = inventoryItemProduced.getRelatedOne("InventoryItem")/>
        <#if inventoryItem.inventoryItemTypeId == "SERIALIZED_INV_ITEM">
        <tr class="${tableRowClass(rowIndex)}">
            <@displayLink href="EditInventoryItem?inventoryItemId=${inventoryItem.inventoryItemId}" text="${inventoryItem.inventoryItemId}"/>
            <@display text="${inventoryItem.productId}"/>
            <@inputText name="serialNumber_o_${rowIndex}" default="${inventoryItem.serialNumber?if_exists}"/>
            <@inputHidden name="_rowSubmit_o_${rowIndex}" value="Y"/>
            <@inputHidden name="inventoryItemId_o_${rowIndex}" value="${inventoryItem.inventoryItemId}"/>

In this example, we've mixed Freemarker directives (if, list, assign), HTML and CSS tags (tr, class), and opentaps forms macros (displayLink, display, inputText, inputHidden.) The form macros are just macros for generating the appropriate HTML around the parametrized fields nad values. The list of form macros and how to use them are given in the API below.

That's all there is to it.

The opentaps Form Macros API

Notation:

@inputHidden name value=""
means that the macro can be used as:
<@inputHidden name="facilityId">
which creates a hidden input with default value of "". Or, it can be used as:
<@inputHidden name="facilityId" value="${facilityId}">
which creates a hidden input with default value of whatever ${facilityId} is in the context. Each attribute (ie, name or value in this case) after the name of the macro (inputHidden in this case) is a parameter. If there is an ="" after the attribute, it defines a default value.

By convention, these are standard fields for all macros which may use them:

The form macros can be divided into two sub-groups: element and row macros. Element macros are for creating a single cell or form element. Row macros use the element macros for creating an entire input row. For example, an element macro might be used to create a date entry field, which can be used in a multi-row or single submit form. A date entry row macro might then use the date entry field element macro to create a row with a title ("Start Date") and the elemnt macro, all wrapped in TR and TD tags.

NOTE: There are many more form macros available. Please look in your opentaps/opentaps-commons/webapp/common/includes/lib/opentapsFormMacros.ftl file for details.

Row Macros

Macro Usage
@inputTextRow name title size=30 maxlength="" default="" titleClass="tableheadtext" index=-1 rowId="" hidden=false Creates a text entry row for field with name and displays the title. Optionally specify size, maxlength, and default values.
@inputLookupRow name title lookup form size=20 maxlength=20 default="" titleClass="tableheadtext" Creates a text entry row with a lookup. The lookup URL is specified in the "lookup" parameter.
@inputHidden name value="" Creates a hidden input for name with default value of ""
@inputDateTimeRow name title default="" titleClass="tableheadtext" popup=true weekNumbers=false onUpdate="" onDateStatusFunc="" linkedName="" delta=0 Creates a date and time input row.
@inputDateRow name title default="" titleClass="tableheadtext" popup=true weekNumbers=false onUpdate="" onDateStatusFunc="" linkedName="" delta=0 Creates a date input row.
@inputIndicatorRow name title required=true default="" titleClass="tableheadtext" index=-1 Creates an input row with a Y/N dropdown (select) box. This uses inputIndicator (see above.)
@inputSubmitRow title colspan="1" Creates a submit button. Specify the word in the button with title and how many columns it spans.

Element Tags

Macro Usage
@displayTitle text class="tableheadtext" width=200 Display a title tag. Used by inputRowText.
@inputLookup name lookup form default="" size=20 maxlength=20 index=-1 An input element with a lookup button next to it. lookup is the controller request for the lookup (ie, LookupProduct)
@inputIndicator name required=true default="" Creates dropdown (select) box of Y/N for the name. If required=true, user must select one.
@inputSelect name list title="" key="" displayField="" default="" index=-1 required=true defaultOptionText="" display="row|cell|block|inline" Creates a select box. defaultOptionText is the value of "required" is false, and an empty default option element is generated, then "defaultOptionText" will contain the display text for that option.

For a row:
@inputSelect name list key="" displayField="" default="" index=-1 required=true defaultOptionText=""
or: @inputSelect name list key="" displayField="" default="" index=-1 required=true defaultOptionText="" display="row"

For a cell:
@inputSelect name list key="" displayField="" default="" index=-1 required=true defaultOptionText="" display="cell"

For a block:
@inputSelect name list key="" displayField="" default="" index=-1 required=true defaultOptionText="" display="block"

Inline:
@inputSelect name list key="" displayField="" default="" index=-1 required=true defaultOptionText="" display="inline"

@inputText name size=30 maxlength="" default="" index=-1 Create an input text box. index not implemented yet.
@inputConfirm title href="" form="" confirmText=uiLabelMap.OpentapsAreYouSure class="buttonDangerous" Creates a confirmation button. When the button is pressed, it will produce a popup confirmation dialogue. If the user cancels, then nothing happens. If the user confirms, then either the given form name is submitted or the user is sent to the given href link. The text in the popup window can be set with confirmText, but the buttons are browser specific. (See the javascript function confirm() for reference.)
@inputStateCountry address=null stateInputName="stateProvinceGeoId" countryInputName="countryGeoId" Creates two dropdowns for the user to select state and country. If the country is changed, the state dropdown will be updated to show the states in that country. The default country is defined in opentaps.properties as defaultCountryGeoId.

You may either pass in a PostalAddress with the address= argument, or you can specify the parameter field names with stateInputName and countryInputName.

In order for this macro to work properly, the following script should be called in the implementing screen,

components://opentaps-common/webapp/common/WEB-INF/actions/includes/stateDropdownData.bsh

For a row:
@inputRowSelect title stateInputName="stateProvinceGeoId" countryInputName="countryGeoId"

For a cell:
@inputCellSelect stateInputName="stateProvinceGeoId" countryInputName="countryGeoId"

Header and Menu Tags

Macro Usage
@sectionHeader title headerClass="subSectionHeader" titleClass="subSectionTitle" Creates a header for a subsection within an OpenTaps screen. Parameter "title" is the title that will be shown in header, "headerClass" is the class of the section header DIV element, and "titleClass" is the class of the actual title (technically, the DIV element that contains the title, which in turn is contained within the section header DIV element). Note that additional contents, such as FTL code for menu buttons, can be "nested" within this macro.

Other Macros

Macro Usage
@pagination viewIndex viewSize currentResultSize requestName totalResultSize=-1 extraParameters="" Generates a pagination block for a list, eg: Previous 21-35 of 35 Next

viewIndex: Current starting record number, eg: 21
viewSize: Number of records to show, eg: 20
currentResultSize: Number of records currently showing, eg: 15
totalResultSize: Total records in result set, eg: 35. If not supplied, total results will not appear (eg: Previous 21-35 Next)
requestName: request to pass back and forth in the Previous and Next links
extraParameters: Any extra request parameters to pass in the Previous/Next links. This will be HTML-encoded by the macro.

Usage example:

In screen definition:

<set field="viewIndex" from-field="parameters.VIEW_INDEX" type="Integer" default-value="1"/>
<set field="viewSize" from-field="parameters.VIEW_SIZE" type="Integer" default-value="20"/>

In bsh:
lotListIt = delegator.findListIteratorByCondition("Lot", ...);
lotList = lotListIt.getPartialList(viewIndex.intValue(), viewSize.intValue());
lotListIt.last();
lotsTotalSize = lotListIt.currentIndex();
context.put("lotList", lots);
context.put("lotsTotalSize", lotsTotalSize);

In FTL:
<#assign exParams = "&doLookup=Y&supplierPartyId=" + parameters.supplierPartyId?if_exists/> <@pagination viewIndex=viewIndex viewSize=viewSize currentResultSize=lotList?size requestName="manageLots" totalResultSize=lotsTotalSize extraParameters=exParams/>

Will generate the following code:
<div class="pagination">
  <span class="paginationPrevious"><a href="/warehouse/control/manageLots?VIEW_INDEX=1&doLookup=Y&supplierPartyId=">Previous</a></span>
  <span class="paginationText">3 - 4 of 7</span>
  <span class="paginationNext"><a href="/warehouse/control/manageLots?VIEW_INDEX=5&doLookup=Y&supplierPartyId=">Next</a></span>
</div>
Important!

The pagination macro only works with GET forms. If you have a form which feeds a page with parameter values (example: warehouse/control/backOrderedItems), the form must use the GET method, not POST. Otherwise there can be two values for the same parameter name (one passed via POST and one passed by the pagination macro in the querystring) and an ArrayList results from parameters.get(), not a String, which makes things explode.

@flexArea targetId title="" class="" style="" controlClassOpen="" controlClassClosed="" controlStyle="" state="" save=false enabled=true

Generates a clickable control block with headline, which triggers the expansion/contraction of an inner block.

targetId: DOM ID for the flexArea. Must be unique to the screen. Used to trigger the collapse/expand and to persist the state of the flexArea.
title: Headline for the control block.
class: CSS class for the opened/expanded state of the inner block. Defaults to flexAreaContainer_open.
style: CSS styles to override the expanded class of the inner block.
controlClassOpen: CSS class for the expanded state of the control block. Defaults to flexAreaControl_open.
controlClassClosed: CSS class for the collapsed state of the control block. Defaults to flexAreaControl_closed.
controlStyle: CSS styles to override both the expanded and collapsed states of the control block.
state: Controls the initial state of the flexArea on page load. If not specified and no saved state exists in the database, defaults to closed.
save: If true, the state of the flexArea will be saved to the database each time it it expanded or contracted.
enabled: If false, clicking on the control block will not expand or contract the flexArea. Specify false when another DOM element should control the expansion.

Usage example:

In bsh (include in a global bsh such as main-decorator.bsh so that every screen has access to the saved states of its flexAreas):
screenName = parameters.get("thisRequestUri");
prefMap = UtilMisc.toMap("application", opentapsApplicationName, "screenName", screenName, "userLoginId", userLogin.getString("userLoginId"));
viewPrefs = delegator.findByAnd("ViewPrefAndLocation", prefMap);
vpit = viewPrefs.iterator();
while (vpit.hasNext()) {
viewPref = vpit.next();
foldedStates.put(viewPref.get("domId"), viewPref.getString("viewPrefString"));
}
globalContext.put("foldedStates", foldedStates);

In FTL:

For a simple flexArea:
<@flexArea targetId="..." title="Click me to expand/contract"><div>Elements to hide and show</div></@flexArea>

For a flexArea which is always open on page load:
<@flexArea targetId="..." title="..." state="open" save=false>...</@flexArea>

For a flexArea which is hidden and closed on page load and has its expansion triggered by an external event:
<@flexArea targetId="..." title="..." controlClassClosed="hidden" state="closed" save=false enabled=false>...</@flexArea>

To override the default classes:
<@flexArea targetId="..." title="..." class="customClassForOpenInnerBlock" controlClassClosed="customClassForClosedControlBlock" controlClassOpen="customClassForOpenControlBlock">...</@flexArea>

isOpen(domId, default="")

openOrClosedClass(domId, openClassName, closedClassName, default="")
Supporting functions for the flexArea macro. Not useful separately.