The target of this tutorial is to explain in detail the basic principles of an OpenWGA CMS application based on the sample application delivered with the OpenWGA Developer Studio.
If not already done create a new OpenWGA example application inside your OpenWGA Developer Studio like described here, but use the template "application-sample" instead.
The new created app looks like this (after you pressed the button "create demo addresses"):
The application itself should be self explaining. This tutorial will explain the concepts of how to build such an CRUD (create/read/update/delete) application with the OpenWGA CMS.
Lets have a look at the main WebTML module "tml/html/main.tml" and highlight some first parts:
<head>
<tml:htmlhead/>
<link rel="stylesheet" type="text/css" href="<tml:url type="css" name="layout"/>"/>
<script type="text/javascript" src="<tml:url type="js" name="scripts"/>"></script>
</head>
<body>
<div id="page">
<h1>OpenWGA AJAX-Portlet Sample</h1>
<tml:label key="app-description"/>
<div id="content">
<tml:portlet name="addr" ref="addresses:portlet" ajax="true"/>
</div>
</div>
© 2010 Innovation Gate GmbH
</body>
</html>
This module declares the main HTML structure of the application responsible for the UI and the look and feel of the application. Feel free to change this structure to your taste by adding HTML elements or changing the CSS-Module "scripts/css/layout" that is included in the HTML-Head section of the main module.
The TML Tag <tml:htmlhead/> is a tag which should be included in the HTML head section of every WebTML page as it includes some scripts necessary for form handling and authoring. Just add this tag without any attributes.
The tag <tml:label> loads a so called WebTML label. This is a text which is to be shown in the UI but is defined in multiple different languages, so it can be displayed to the user in a language he understands. Click the contents of the label key while holding the "Control" button ("Command" button on a Mac) to be taken to the definition (for one language).
WebTML Portlets
All real functionality of this application is included using the <tml:portlet> tag:
<div id="content">
<tml:portlet name="addr" ref="addresses:portlet" ajax="true"/>
</div>
<tml:portlet> includes the referenced WebTML module "adresses:portlet" (just like <tml:include> in the Website sample). This means that the code from "/tml/html/addresses/portlet.tml" will be executed (rendered) and the resulting HTML-fragment will be inserted into the HTML-page inside the <div id="content">.
In addition to that <tml:portlet> tells OpenWGA that the included module will:
- Have a given name, specified by the "name" attribute
- Have its own persistent data storage per user, to be used by the application in some custom way
- May have different portlet modes in which the include may be displayed
That's what we call a WebTML portlet in OpenWGA. It is a much more lightweight component than other portlet concepts you might know, mostly because it does not require any more declaration than you have learned right now.
The Attribute ajax="true" tells OpenWGA to refresh only this part of the HTML page instead of the whole page when a so called "AJAX action" is executed. More on this later.
In this example we will use the "persistent storage" to store the user-selected sort order of the address collection. Also we will use the "portlet mode" to determine what our application is currently doing, may it be viewing all addresses, creating a new address or editing an existing one.
Lets how look at the WebTML Code of the portlet in "tml/html/addresses/portlet.tml":
This is a rather short but "intelligent" WebTML module. It includes another WebTML module depending on the current portlet mode.
The syntax attribute="[string{expression}]" tells the OpenWGA template engine, that the attribute value will be dynamically calculated before the tag is executed. The parts directly inside the square brackets "[]" are strings while the parts inside curly braces "{}" are expressions in the OpenWGA-native expression language TMLScript. In this case the expression just retrieves the current portlet mode.
In this case we calculate the name of a WebTML module to include. The resulting attribute value will be "::mode-view" if the mode is "view", "::mode-edit" if the mode is "edit" and so on. The prefix "::" tells OpenWGA to take a module from the same folder as the current module, so the included modules will be "/tml/html/addresses/module-view", "/tml/html/addresses/module-edit" etc.
Before anybody has set the portlet mode to some value, OpenWGA renders each portlet in mode "view". Mode "view" therefore is the only predefined portlet mode in OpenWGA. All other mode values can be defined by the designer.
Because we didn't set the portlet mode to any special value yet, the portlet.tml will include "tml/html/addresses/mode-view.tml".
Lets have a look at the code of this module:
<div align="right">
<tml:form id="sortorder" source="portlet">
sort by ...
<tml:input name="sortitem" type="select" options="lastname, firstname" changeaction="$store" ajax="true"/>
</tml:form>
</div>
<tml:range context="name:addresses">
<tml:if haschildren="true">
<tml:then>
...
</tml:then>
<tml:else>
<tml:label key="no-addresses"/>
<tml:button clickaction="::create-demo-addresses" ajax="true"><tml:label key="create-addresses-button"/></tml:button>
</tml:else>
</tml:if>
<hr>
<tml:button portletmode="new" ajax="true"><tml:label key="new-address-button"/></tml:button>
</tml:range>
WebTML Actions
A WebTML Action is a function you can call from your template code. But unlike JavaScript functions it is always executed on the OpenWGA server, not in the browser client. So calling an action sends a request to the server which then performs the action operation and returns a result to the browser.
There are different ways to call actions. In the middle part of the code you see a <tml:button> which calls an action of name "::create-demo-addresses" when the user clicks on a button:
<tml:button clickaction="::create-demo-addresses"
ajax="true">...</tml:button>
In the upper part you see a <tml:input> defining an input field, which calls an action named "$store" when the contents of the field has changed (specified by attribute "changeaction"):
<tml:input name="sortitem" ... changeaction="$store" ajax="true"/>
Action code is normally written in TMLScript and stored in a .tmlscript file in the "scripts/tmlscript" folder of the design. This is the case with the action "::create-demo-addresses". You will find a file of that name in this folder. More on that later.
However there is a special kind of actions we call default actions. Those default actions are predefined inside the OpenWGA runtime and you don't have to code them by yourself. The names of default actions begin with a $ sign. The above action "$store" is such a predefined default action, but we will see other type of actions later in this tutorial.
<tml:form> and the $store action
The action "$store" on <tml:input> will save the selected sort order inside the "persistent storage" of the portlet.
To render a select box we use the construct <tml:form>...<tml:input type="select"/>...</tml:form>.
This will create an HTML <form> tag with a <select><option>...<option></select> part inside it. What options are generated is specified by the options="..." attribute of the <tml:input type="select"> tag.
But the <tml:form> tag will do more for you. It uses the source="..." attribute to fill your form fields with data from the source you specified. In our case we have source="portlet", so when generating <tml:input> OpenWGA looks inside the persistent storage of the current portlet for an item with the name from the "name" attribute of the <tml:input> tag. It then gives the generated field the value from the portlet item.
So if the portlet item with name "sortitem" has the value "lastname" then <tml:input> will generate:
<select name="sortfield">
<option name="lastname" selected="true">
<option name="firstname">
</select>
The portlet item "sortitem" is saved through the $store action. We trigger this action using changeaction="$store" in the <tml:input> tag. When a $store action is called, all data of the form is collected and submitted to the OpenWGA server.
OpenWGA then stores this submitted data to some kind of data store specified by the source="..." attribute of <tml:form>. Because we use source="portlet" all form values are stored to the configuration of the current portlet. Each single form field becomes a so called "item" on this configuration.
<tml:button> without clickaction
In the bottom part of the code you will find a <tml:button> without clickaction. This will only reload the current portlet without executing any code.
The tag <tml:button> lets WGA render a <button onclick="...">. When a user clicks on the button, the specified action will be executed, or only the portlet will get reloaded if none is specified.
However this button is not completely unoperative, as it owns an attribute portletmode="new". This attribute tells WGA that on reloading the portlet will change to a new portlet mode named "new".
Remember: Our main portlet WebTML code uses the mode to decide which WebTML to include. So clicking this button will refresh the portlet and let it include WebTML module "mode-new" instead of "mode-view".
In the next section of this tutorial you will learn how to create a new address "document" using our framework for treating application data, called "HDBModel".
Continue on next page ...