logo

Make web development powerful & efficient

Elements and Widgets

To use Iliad, you need to understand two important parts of the framework: Widgets and Elements. Widgets are high level stateful graphical objects, while Elements are composite low level stateless objects for building HTML. Widgets use elements in their #contents method to build themselves.

People who are familiar with Aida/Web will immediately understand how elements work. Each XHTML tag has a corresponding element class which knows how to print itself as HTML with the #printHtmlOn: method.

e div 
  class: 'example';
  h1: 'Hello world!'

The #contents method of widgets used to build html returns a block closure which takes an element as parameter:

Iliad.ILWidget subclass: MyWidget [
    
  contents [
    ^[:e | 
      e div
        class: 'example';
        h1: 'hello world!']
    ]
]

Applications

Iliad applications are buildable widgets, used as entry points of web applications. Unlike widgets, they know how to dispatch a request to the corresponding controller method.

Iliad.ILApplication subclass: MyApplication [
    
  MyApplication class >> path [^'my_application']
    
  index [
    <category: 'controllers'>
 
    ^[:e |
      e h1: 'Hello world!']
    ]
]

Controller methods in applications are pretty much like the #contents method of widgets. By default, they must be in the 'controllers' method protocol, else they won't be allowed to be used as controllers.

The #path class method is important, it tells Iliad what is the base path of the application.

The #index method is the default controller method, so this view can be reached at:

The counter widget example

Let's take a look at the ILCounter widget class.

Iliad.ILWidget subclass: Counter [
  | count |

  initialize [
        
    super initialize.
    count := 0
  ]

  contents [
        
    ^[:e |
      e h2: count printString.
      e a
        action: [self increase];
        text: '++'.
      e space.
      e a
        action: [self decrease];
        text: '--'.]
  ]

  decrease [
        
    count := count - 1.
    self markDirty
  ]

  increase [
        
    count := count + 1.
    self markDirty
  ]
]

A counter widget has a count instance variable, initialized to 0. Its #contents method builds a header displaying the current count value, and two anchors, one to increase the count, and one to decrease it.

There is something new in this #contents method: the actions associated to the anchors. Actions are block closure that will be evaluated when the user clicks on the associated link or button. Here we use them to modify the count value with #increase and #decrease methods.

Also note the #markDirty call in #increase and #decrease. This method is really important when updating the state of a widget. It tells Iliad that the widget's state has changed, so it will be rebuilt.

The counter application

The counter widget is, like every Iliad widget, a standalone graphical object. Let's create a simple application to see it in action!

Iliad.ILApplication subclass: CounterApplication [
  | counters |

  CounterApplication class >> path ['counters']

  initialize [
    super initialize.
    counters := OrderedCollection new.
    5 timesRepeat: [counters add: Counter new]
  ]

  index [
        
    ^[:e |
      self counters do: [:each | e build: each]]
  ]
]

As you may have noticed, counters are stored into an instance variable, so new ones won't be created on each request. This is important, because each counter has state, and it must be maintained between requests.

Also, we don't call the #contents method of the counter directly. it should never be called from the outside.

That's it, you can see your counter at: