Table of content
Anchor | ||||
---|---|---|---|---|
|
I'd like to see improved Ajax support in Click 2.2.0, especially around the area of reusable Ajax controls. It is not currently possible to create such controls.
...
I'll use jQuery where client-side JavaScript is required to convey a concept.
Anchor | ||||
---|---|---|---|---|
|
There are two sides to Ajax support, the client side HTML markup and JavaScript code to initiate the Ajax request, and the server side support, to handle the request and return a partial response.
...
But lets quickly look at the JavaScript code necessary to initiate an Ajax request. Below is a simple client-side example to initiate an Ajax request when a link is clickedlink is clicked.
Please note: the code below is an example of the markup and JavaScript code that must be rendered by a client-side Click implementation. The implementation will most likely use generic templates and that accepts variables such as the CSS selector, event, page url etc.
Code Block | ||||
---|---|---|---|---|
| ||||
<a id="mylink" href="my-page.htm">Click Me</a> <div id="target">Update Me</div> |
...
Next I'll cover the proposals to handle the Ajax request and response on the server-side.
Anchor | ||||
---|---|---|---|---|
|
Anchor | ||||
---|---|---|---|---|
|
...
actions
The simplest way to improve Ajax support is to allow Page methods actions to be called directly from the browser. The Page method action could return a Partial response that contains the data to return to the browser.
...
The example client-side Ajax call will need slight modification, since a Page method action is invoked instead of a Control:
Code Block | ||||
---|---|---|---|---|
| ||||
$.get("my-page.htm", { mylink: 1, event: event.type, page-methodpageAction: loadLabel }, success); |
In order to support invocation of Page methodsactions, Click should be enhanced so that if the 'page-methodpageAction' request parameter is present, that request parameter value will be the Page method action to invoke. The signature of the method must be: "public Partial methodName()". Except for creating the Page object and onSecurityCheck event, no other page events (onInit, onRender etc) are executed.
Please note that this feature could be useful for non Ajax requests as well. Consider a dynamic <img> element that displays a different image depending on its parameters. The ability to invoke a Page method action directly would be useful in this case as the Page method action implementation could inspect the request parameters, grab the image from the database and stream back the image to the browser. All this without having to invoke other Page events (onInit, onProcess) or having to set the Page path to null, which often trips new users.
...
- caters only for GET requests, not POSTS, since page events are not executed
- does not cater for ajax aware controls
- interaction with other components on the Page will be fairly limited, since no initialization or binding occursor binding occurs
Anchor | ||||
---|---|---|---|---|
|
A more sophisticated approach would be to allow control listeners to be invoked for Ajax requests. A special AjaxListener can be set as a Control listener, that will be invoked if the incoming request targets that control:
...
- It could encourage writing complex Pages that are hard to understand and maintain
- Could lead to an explosion of control subclasses to add Ajax functionality. This can be alleviated using Helper objects to encapsulate Ajax behavior
- Can lead to difficult to find bugs if there is mismatch between the client side Ajax ID attribute and the server side control ID attribute. Dynamic stateless pages could also lead to difficulties if an Ajax control is added inside another control's listener and subsequent requests won't have the Ajax control present in the server-side component tree
- Implementation is more complex than for Page MethodsActions
- The stateless nature of Click makes Ajax applications inherently tricky, because if a non-Ajax request is made to the server, all Ajax related state will be undone. This can be alleviated by using Stateful pages, but since requests to stateful pages are serialized, this creates another, albeit small, problem in that parallel Ajax requests to a stateful page won't work. In other words if the browser makes two Ajax requests to a stateful Page where the first request is very resource intensive and takes 10 seconds to execute, the second Ajax request will have to wait until the first request finishes. Although this might not be a common use case, it is worth taking into consideration.
...
- A control can only have a single listener defined. How will a control handle multiple incoming events? For example a field might handle two Ajax events, "onblur" for validation purposes, and "onkeypress" for auto-completion. The Event object passed into the listener could be inspected, but this leads to if-else logic. This brings up the idea of having multiple listeners per control.
- Pro: Each listener could be registered for a specific event, and will only be triggered for Ajax requests of that event type.
- Con: Having multiple listeners per control could increase the conceptual complexity and could lead to an increase in maintenance. For example, if the client-side code neglects to pass the event parameter, no listener will be invoked. The user will now have to deal with whether the control ID or event request parameter or both are missing.
Anchor | ||||
---|---|---|---|---|
|
Behaviors is a concept borrowed from Wicket and JSF. A behavior is attached to a Control and will be notified by the Control for specific events such as onInit, onProcess, onRender, getHeadElements. A behavior can also act as a listener and receive events, just like a Control does.
...
- The initial thinking is that a behavior can only have a single listener defined. How will a behavior handle multiple incoming events? For example two behaviors could be registered on a field, "onblur" for validation purposes, and "onkeypress" for auto-completion. The Event object passed into the listener could be inspected, but this leads to if-else logic. This brings up the idea of having multiple listeners per behavior.
- Pro: Each listener could be registered for a specific event, and will only be triggered for Ajax requests of that event type.
- Con: Having multiple listeners per behavior could increase the conceptual complexity and could lead to an increase in maintenance. For example, if the client-side code neglects to pass the event parameter, no listener will be invoked. The user will now have to deal with whether the control ID or event request parameter or both are missing.
Anchor | ||||
---|---|---|---|---|
|
I'm leaning towards introducing both Page actions and Ajax Behaviors. The simplicity of Page actions combined with the flexibility and reusability of Ajax Behaviors seems like a good choice.