4.3. AjaxBehavior Execution

The execution sequence for an AjaxBehavior being processed and rendered is illustrated in the figure below. Note that it is similar to a normal HTTP request flow. The main differences are that Ajax requests do not have an onGet or onRender event and that only the Ajax target Control is processed.

AjaxBehavior Sequence Diagram

Figure 4.2. AjaxBehavior Sequence Diagram


Stepping through this Ajax GET request sequence, first a new Page instance is created.

Then the onSecurityCheck() handler is executed to authorize access to the page, and if necessary abort further processing. If the request is aborted for an Ajax request, no response is rendered to the browser. If you want to render a response you need to write to the HttpServletResponse directly or create and render an ActionResult yourself.

The next method invoked is onInit() to initialize, create and setup controls and Behaviors. onInit is an ideal place to add Behaviors to Controls. When a Behavior is added to a Control that Control is automatically registered with the ControlRegistry as a potential Ajax target control.

The next step is to find and process the Ajax target control. First the ClickServlet needs to determine which Control is the Ajax target. To resolve the target Control the ClickServlet iterates over all the Controls registered with the ControlsRegistry and invokes each Control's isAjaxTarget method. The first control which isAjaxTarget method returns true, will be the Ajax target.

The simplest isAjaxTarget implementation is to return true if the Control ID is passed as a request parameter. The client-side JavaScript code that initiate the Ajax request, must ensure the Control ID is sent as part of the Ajax request. Note, if the ClickServlet cannot find a target control, no response is rendered.

If an Ajax target control is found, the ClickServlet will invoke that control's onProcess method. Other controls are not processed.

Please note: since Click is a stateless framework, processing a control for an Ajax request has the same requirements as processing a control for a non-Ajax request. In other words, in addition to the Control ID (or other identifier), the Ajax request must include all the parameters normally expected by the target Control and its children. For example, a Field expects it's name/value parameter while an ActionLink expects its actionLink/name parameter. Putting it another way, if for example an ActionLink is clicked and we only pass the link's HTML ID parameter, Click will identify the link as the Ajax target control and invoke the link's onProcess method. The onProcess method is where the link's values are bound and if it was clicked it's action event (AjaxBehavior) will be fired. An ActionLink is "clicked" if the actionLink parameter has a value matching the link's name. If no actionLink parameter is present, the server doesn't know that the link was clicked and won't fire the AjaxBehavior's onAction event. So for an Ajax request it is still necessary to pass all the parameters normally expected by the ActionLink onProcess method. For ActionLink that means the Ajax request must include it's href parameters while a Form would require all it's Field name/value pairs.

Next, the target control AjaxBehaviors are fired. The ClickServlet iterates over the control AjaxBehaviors and for each AjaxBehavior invoke the method: isAjaxTarget. Each AjaxBehavior which isAjaxTarget method returns true, will have their onAction method invoked to handle the Ajax request. The AjaxBehavior's onAction method returns an ActionResult that is rendered to the browser.

Please note: multiple AjaxBehaviors can handle the same Ajax request, however only the first ActionResult returned will be rendered to the browser. If an onAction method returns null, the ActionResult returned by the next AjaxBehavior's onAction method will be used. If all onAction methods returns null, no response is rendered.

Next the ActionResult is rendered to the browser.

The final step in this sequence is invoking each control's onDestroy() method and lastly invoke the Page onDestroy() method.