
Pages in WebDSL can be defined using the
define page { <page element*> } construct:
define page home() {
title { "Page title" }
section {
header("Hello world.")
text { "Greetings to you." }
}
}
Custom pages can be built using basic page elements. These determine the basic structure and layout of a page. These include:
Menus
menubar
menuheader
menu
menuitem
Tables and lists
table , row : create tables. For example:
table {
row { "Username" output(user.name) }
row { "Password" "it's a secret" }
}
list , listitem : create lists. For example:
list {
listitem { "Milk" }
listitem { "Potatoes" }
listitem { "Cheese (lots)" }
}
Forms
Forms enable user input, and may include action elements (see below).
form { ... }: defines a new form. Example:
form {
var name : String;
var password : Secret;
table {
row { "Username:" input { username } }
row { "Password:" input { password } }
}
}
captcha: creates a fully automatic CAPTCHA form element.
Generative elements of a page define page elements based on the type of their parameters.
input(<value>): creates an input form element. Can be applied directly to the proparties of an entity (e.g., input(user.name)) or to local variables.
output: displays a value.
Pages can have parameters. For example, to override the default 'view' page of an entity user, define:
define page user(u : User) {
"The name of this user is " u.name
}
Actions define the interactive behavior of pages. See actions.
We conclude this page with an example user page, that overrides the standard view page for entity type User. It displays a table with the current entity, and provides a form to edit it.
define page user(u : User) {
main()
define body() {
table {
row { "Name: " output(u.name)}
row { "Group: " output(u.group)}
row { "Email: " output(u.email)}
}
navigate ( editUser(u)) { "Go to generic edit page" }
form {
// forms may contain input elements and actions
"Edit this user"
table {
row { "Name: " input(u.name) }
row { "Group: " input(u.group) }
row { "Email: " output(u.email) }
action ( "save", saveUser() )
}
}
action saveUser() {
u.persist();
}
}
}
Templates enable reuse of page elements. For example, a template for a footer could be:
define footer() { All your page are belong to us. }
This template can be included in a page with a template call:
define page example(){
footer
}
Like pages, templates can be parameterized.
define edit(g:Group){
form {
input(g.members)
action("save",action{})
}
}
define page editGroup(g:Group){
edit(g)
}
While pages must have unique names, templates can be overloaded. The overloading is resolved compile-time, based on the static types of the arguments.
define edit(g:Group){...}
define edit(u:User){...}
define page editGroup(g:Group){
edit(g)
}
Template definitions can be redefined locally in a page or template, to change their meaning in that specific context. All uses are replaced in templates called from the redefining template.
define main{
body()
}
define body(){
"default body"
}
define page root(){
main
define body(){
"custom body"
}
}
Page and template definitions can contain variables. This example displays "Dexter":
define page cat() {
var c := Cat { name := "Dexter" }
output(c.name)
}
entity Cat {
name :: String
}
These variables are necessary when constructing a page that creates a new entity instance. The instance can be created in the variable and data binding can be used for the input page elements. The next example allows new cat entity instances to be created, and the default name in the input form is "Dexter":
define page newCat() {
var c := Cat { name := "Dexter" }
form{
label("Cat's name:"){ input(c.name) }
action("save",action{ c.save(); return showCat(c); })
}
}
It is possible to initialize such a page/template variable with arbitrary statments using an 'init' action:
define page newCat() {
var c
init {
c := Cat{};
c.name := "Dexter";
}
form{
label("Cat's name:"){ input(c.name) }
action("save",action{ c.save(); return showCat(c); })
}
}
Be aware that these type of variables (and the init blocks) are handled separately from the other elements. They do not adhere to template control flow constructs like 'if' and 'for'; they are extracted from the definition. However, you can express such functionality in the 'init' block. For example:
error:
define page bad() {
if(someConditionFunction()){
var c := Cat{}
}
else {
var c := Cat{ name := "Dexter" }
}
output(c.name)
}
ok:
define page good() {
var c
init{
if(someConditionFunction()){
c := Cat{}
}
else {
c := Cat{ name := "Dexter" }
}
}
output(c.name)
}
Iterating a collection of entities or primitives can be done using a for loop. There are three types of for loops for templates:
for(id:t in e){ elem* }
This type of for loop iterates the collection produced by expression e, which must contain elements of type t. The elements in the collection are accessible through identifier id.
The collection can be filtered:
for(id:t in e filter){ elem* }
This for loop iterates all the entities in the database of type t. These can also be filtered. Note that it is more efficient to retrieve the objects using a filtering query and use the regular for loop above for iteration.
for(id:t){ elem* }
for(id:t filter){ elem* }
This for loop iterates the numbers from e1 to e2-1.
for(id:Int from e1 to e2){ elem* }
All three template for loops can be followed by a separated-by declaration, which will separate the outputs from the for loop with the declared elem*.
separated-by{ elem* }
The filter part of a for loop can consist of four parts:
where e1
e1 is a boolean expression which needs to evaluate to true for the element to be iterated.
order by e2 asc/desc
e2 is an expression that needs to produce a primitive type such as String or Int, which will be used to order the elements ascending or descending.
limit e3
e3 is an Int expression which will limit the number of elements that get iterated.
offset e4
e4 is an Int expression which will offset the starting element of the iteration.
Each of the four parts is optional, but they have to be specified in this order. The filtering is done in the application, so use queries instead of filters to optimize the WebDSL application.
XML fragments can be embedded directly in templates. This allows easy reuse of existing XHTML fragments and CSS. For example:
define main() {
<div id="pagewrapper">
<div id="header">
header()
</div>
<div id="footer">
<p />"powered by " <a href="http://webdsl.org">"WebDSL"</a><p />
</div>
</div>
<some:tag />
}
While the name and attribute names are fixed, the attribute values can be any WebDSL expression that produces a string:
define test(i : Int) {
<div id="page" + "wrapper" + i />
}
title { element* }
Declares the title of the current page.
section { element* }
Indicate sections in a document; may be nested. May include a
header( String )
element that indicates the section title.
block [(class)] { <page element*> }
Groups text; optionally defines a class for referencing in CSS. Results in a <div> element in HTML.
navigate <page call> { <page element*> }
Link to a page. For example:
page news() { "News" }
image ( <string with relative or absolute path to image> )
Displays an image. Images placed in an "images" folder in the root directory of your application will be automatically copied during deployment.
Example:
define page root(){
image("http://webdsl.org/webdslorg/images/WebDSL-small.png")
image("/images/WebDSL-small.png")
}
select(x from y) can be used as input for an entity variable or for a collection of entities variable, where x is the variable or fieldaccess and y is the collection of options. It will create a dropdown box/select or a multi-select respectively. The name property of an entity is used to describe the entity in a select, see name property.
input(x) for an entity reference property or a collection property is the same as select, with as options all entities of its type that are in the database.
Example:
entity User {
username :: String (name)
teammate -> User
group -> Set<Group>
}
entity Group {
groupname :: String (name)
}
init{ //application init
var u := User { username := "Alice" };
u.save();
u := User { username := "Bob"};
u.save();
var g := Group { groupname := "group 1" };
g.save();
g := Group { groupname := "group 2" };
g.save();
}
define page root(){
form{
table{
for(u:User){
output(u.username)
input(u.teammate)
input(u.group)
}
}
submit("save",action{})
}
}
input(u.teammate) is a dropdown/select with options null, "Alice", "Bob". input(u.group) is a multi-select with options "group 1" and "group 2".
Example 2:
define page root(){
var teammates := from User
var groups := from Group
form{
table{
for(u:User){
output(u.username)
select(u.teammate from teammates)
select(u.group from groups)
}
}
submit("save",action{})
}
}
Equivalent to the previous example, but using explicit selects instead.
Example 3:
var u3 := User { username:="Dave" }
var g3 := Group { groupname:="group 3" }
define page root(){
var teammates := [u3]
var groups := {g3}
form{
table{
for(u:User){
output(u.username)
select(u.teammate from teammates)
select(u.group from groups)
}
}
submit("save",action{})
}
}
Options are restricted in this example, null and "Dave" for select(u.teammate from teammates) and only "group 3" for select(u.group from groups)
The null option for a select can be removed either by a not null annotation on the property:
teammate -> User (not null)
Or by setting [notnull := true] on the input or select itself:
input(u.teammate)[notnull := true]
select(u.teammate from teammates)[notnull := true]
The possible options can also be determined using an annotation on the property:
group -> Set<Group> (allowed = {g3})
In this case just using input(u.group) will only show "group 3"
The includeCSS(String) template call allows you to include a CSS file in the resulting page. CSS files can be included in your project by placing them in a stylesheets/ directory in the project root.
Example 1:
define page root() {
includeCSS("dropdownmenu.css")
}
It is also possible to include a CSS file using an absolute URL.
Example 2:
define page root() {
includeCSS("http://webdsl.org/stylesheets/common_.css")
}
The media attribute can be set by passing it as second argument in includeCSS(String,String)
Example 3:
define page root(){
includeCSS("http://webdsl.org/stylesheets/common_.css","screen")
}
The includeJS(String) template call allows you to include a javascript file in the resulting page. Javascript files can be included in your project by placing them in a javascript/ directory in the project root.
Example 1:
define page root() {
includeJS("sdmenu.js")
}
It is also possible to include a Javascript file using an absolute URL.
Example 2:
define page root() {
includeJS("http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js")
}