AJAX Support in Aida/Web

1. Introduction

AJAX (Asynchronous Javascript And Xml) is is a web development technique for creating interactive web applications. The intent is to make web pages feel more responsive by exchanging small amounts of data with the server behind the scenes, so that the entire web page does not have to be reloaded each time the user requests a change. This is meant to increase the web page's interactivity, speed, and usability.

AJAX is supported by Aida in a way, as closer to Smalltalk programming as possible. You don't need to deal with any Javascript code to ajaxify your web applications.

Main features of Ajax support in Aida are:

  • update of any web element on a page after some event, like click on some other element
  • immediate post (send to a server) of input fields and other form elements after change
  • periodic update of any web element
  • Ajax components:
    • Autocomplete field
    • In-place editing field

1.1. Example of Ajax updating

Let we start directly with a simple example. See demo site http://demo.aidaweb.si/demos.html?view=ajax, Example 1:

Code for above example looks as simple as that:

WebDemo >> ajaxUpdateTimeExample

| e element |
e := WebElement new.
e addTextH4: 'Example 1: Update element from server (AJAX)'.
element := self ajaxTimeElement.
e add: element; addBreak.
e add: ((WebElement new addText: 'click here to update') onClickUpdate: element).
^e

WebDemo >> ajaxTimeElement

| e |
e := WebElement new.
e style: ''.
e addTextBold: Date today shortPrintSloString, ' ', Time now printString.
^e

First method builds an element of web page, shown in above image. That element is composed of a time element, built by second method #ajaxTimeElement and a text 'click here to update'. Clicking on that text should update a time element and this is done simply by sending to text element a method #onClickUpdate: with time element to be updated as argument.

2. Update an element

WebElement events-ajax

  • onClickUpdate: anElementOrId
    • update (and show if not yet) a specified element from server
  • onClickUpdate: anElementOrId with: aParmString
    • update a specified element from server with calling an element method with a parameter
  • onMouseOutUpdate: anElementOrId
  • onMouseOutUpdate: anElementOrId with: aParmString
  • onMouseOverUpdate: anElementOrId
  • onMouseOverUpdate: anElementOrId with: aParmString

2.1. Periodical update

WebElement events-ajax

  • updateEverySeconds: aNumber
    • periodically update itself from server with calling an element method
  • updateEverySeconds: aNumber with: aParmString
    • periodically update itself from server with calling an element method with a parameter

3. Posting a form element

WebFormElement events-ajax

  • onChangePost
    • after field change immediatelly post contents to server
  • onChangePostAndUpdate: anElementOrId
    • after field change, post contents to server and AJAX update anElementOrId
  • onChangePostAndUpdate: anElementOrId with: aParmString
    • after field change, post contents to server and AJAX update anElementOrId with a parameter
  • onKeyPressPostAndUpdate: anElementOrId
    • after any key press in a field, post contents to server
  • onKeyUpPostAndUpdate: anElementOrId
    • after any key release in a field, post contents to server

Usage:

  • Send above methods to your form element. You can do together with creation and adding:
    • (e addInputFieldAspect: #name for: self observee) onChangePost

3.1. Delayed input field

Class WebDelayedField is used to delayed Ajax posting to server. It waits for delay after last key input, before it posts its contents. Ideal for live-search fields.

Usage:

  • same as for WebInputField, but post is done immediately after delay timeout (0.5s by default)
  • additional methods:
    • afterPostUpdate: anElementOrId
    • afterPostUpdate: anElementOrId with: aParmString
    • delay: seconds

3.2. Example of form posting

See demo site http://demo.aidaweb.si/demos.html?view=ajax, Example 3:

WebDemoApp >> ajaxInputFieldExample

| e element input |
e := WebElement new.
e addTextH4: 'Example 3: Live send input to server (AJAX)'.
element := self ajaxInputFieldElement.
input := WebInputField aspect: #input for: self observee.
input onKeyUpPostAndUpdate: element.
e add: input; addBreak; add: element.
^e

WebDemoApp >> ajaxInputFieldElement "actually just shows content of input field"

| e |
e := WebElement new.
e style: ''.
e addTextBold: self observee input.
^e

4. Ajax components

4.1. Autocomplete field

Class WebAutocompleteField in package WEB-Components. Those are class creation methods:

  • aspect: aSymbol for: anObject choicesAspect: aChoicesSymbol for: aChoicesObject
    • choicesAspect method is called with our field as argument on aChoicesObject. It must return a collection of strings
    • example: myObject searchFor: aString (choicesAspect is #searchFor: )
  • aspect: aSymbol for: anObject choicesAspect: aChoicesSymbol for: aChoicesObject afterPostAndUpdate: aWebElementOrId
    • immediately post and update element, after user has selected one of choices
  • aspect: aSymbol for: anObject choicesAspect: aChoicesSymbol for: aChoicesObject choiceAspect: aTextSymbol
    • choicesAspect method is called with our field as argument on aChoicesObject.
    • It returs a collection of objects, an choice's text is retrieved with a call of choiceAspect method on each
  • aspect: aSymbol for: anObject choicesAspect: aChoicesSymbol for: aChoicesObject choiceAspect: aTextSymbol afterPostAndUpdate: aWebElementOrId
    • choicesAspect method is called with our field as argument on aChoicesObject.
    • It returs a collection of objects, an choice's text is retrieved with a call of choiceAspect method on each"

For example see demo site http://demo.aidaweb.si/demos.html?view=ajax, Example 4:

WebDemo >> ajaxAutocompleteExample

| e field |
e := WebElement new.
e addTextH4: 'Example 4: Autocomplete field with data from server (AJAX)'.
e addText: 'Type first chars of month names to select: '. e addBreak.
field := WebAutocompleteField aspect: #field for: self observee choicesAspect: #monthsStarting: for: self.
e add: field.
^e

WebDemo >> monthsStarting: aString

"as choicesAspect for autocompletion demo"
| months |
months :=
#('januar' 'prosinec' 'siječanj' 'January'
'februar', 'svečan' 'veljača' 'February'
'marec', 'sušec' 'ožujak' 'Mart'
'april', 'mail traven' 'travanj' 'April'
'maj', 'veliki traven' 'svibanj' 'May'
'junij', 'rožnik' 'lipanj' 'June'
'julij', 'mali srpen' 'srpanj' 'July'
'avgust', 'veliki srpen' 'kolovoz' 'August'
'september', 'kimavec' 'rujan' 'September'
'oktober', 'vinotok' 'listopad' 'October'
'november', 'listopad' 'studeni' 'November'
'december', 'gruden' 'prosinac' 'December').
^months select: [:each | aString, '\*' match: each]

4.2. In-place editing field

Class WebInPlaceEditableText in package WEB-Components.

Convinient adding to any WebElement:

  • addInPlaceEditableTextAspect: aSymbol for: anObject
  • addInPlaceEditableTextAspect: aSymbol for: anObject size: aNumberOrPoint
  • addInPlaceEditableTextAspect: aSymbol for: anObject size: aNumberOrPoint allow: aBoolean

Additional usefull methods in WebInPlaceEditableText:

  • wikiFormated
    • source text is formated in wiki format
  • triggerElement: aWebElementOrId
    • element to trigger - open a field in editor. Usualy a small icon to click.

wikiFormated
"source text is formated in wiki format"

For example see demo site http://demo.aidaweb.si/demos.html?view=ajax, Example 5:



WebDemo >> ajaxInPlaceEditorExample

| e |
e := WebElement new.
e addTextH4: 'Example 5: In place editor example (AJAX)'.
e addText: 'Click text below to edit it'. e addBreak.
" e addInPlaceEditableTextAspect: #editField for: self observee." "that's shorter way"
e add: ((WebInPlaceEditableText aspect: #editField for: self observee)
triggerElement: ((WebImage gif: #buttonEditGif) title: 'Edit this text') ). "image to enter edit"

5. Ajaxification of web elements

Web elements must be correctly prepared in order to be updated via Ajax. This is done in two ways:

  • automatically: element must be build in its own app method. Name of that building method is then saved in an element and called every time, when Ajax update request come. That's a case in all above examples
  • manualy: set the creation method with calling anElement method: aSymbol . This method must exist on that element. This is handy for updating a standalone web components like WebGrid.

5.1. Manual preparation of components

A component is a standalone web element with more complex functionality, like WebGrid for tables. To prepare and ajaxify such an component, do:

  1. subclass it from WebElement, like MyComponent
  2. write a method to rebuild your component by Ajax, like #ajaxBuild
  3. register that method after instantiation:
    • MyComponent class new ^super new method: #ajaxBuild
  4. to update with argument, write a method with one argument, which must be only a string! If you like to pass more than one argument, compose them all in one string.

More suggestions:

  • write another method #build, which actually builds a component
    • start that method with: self initElements; initTable.
    • continue with building: self add: ...
    • end with: self app form registerFormElementsIn: self. "this will add registrations to grid form elements"
  • call above method in overriden method #prepareToHTMLPrintOn:
super prepareToHTMLPrintOn: aSession.
self build

  • This method is handy for late building of an element just before it is converted to HTML.

  • method #ajaxUpdate just leave empty, or when with argument, receive and process that argument.

5.2. How an element is Ajax updated

When Ajax update request comes to ajaxified element, the following steps are done:

  1. building method #method is called, which could or could not rebuild that element.
    • if a WebApplication containing that element understands building method, that one is called
    • if an element understands building method, it is called there
    • Note: be carefull not to name a building method same as any method in an App containing that component!
  2. then a standard printWebPageFor:onSession is called to transform that web element into HTML and return to browser, which then replace the old element with new content. This method calls first #prepareToHTMLPrintOn:, where you can do some late preparations or even late build an element.

5.3. Examples in WebGrid

WebGrid allows a table to be sorted, filtered and browsed by page without refreshing entire page. After any such event it updates itself. See demo site http://demo.aidaweb.si/demos.html?view=grid, (Demos > Grid, tabs, menus) - there you can sort and filter instantly:

In WebGrid a method #ajaxUpdateWith: aString is used to rebuild a grid after Ajax update. Additional argument (one string only!) is used to tell what to do (sort/filter) and has also sub argument. Example: 'sort-2' for sorting column 2.