PHOCOA PHP Framework

PHOCOA AJAX Integration

Core Capabilities

PHOCOA's integrated AJAX infrastructure makes adding dynamic Javascript and AJAX features to your application easy.

For any DOM event, you can configure a PHOCOA widget to:

  • Call a javascript function (a JSAction)
  • Execute a method on the server via a full page refresh (ServerAction)
  • Execute a method on the server via AJAX, and have that method send data to the client, or effect UI updates (AjaxAction)

All of this functionality uses the same PHOCOA programming model as standard form/action programming, and requires very little effort to set up.

PHOCOA also includes many YUI widgets that have AJAX capabilities, such as AutoComplete, TreeView, and PhocoaDialog (an AJAX-loading YUI Dialog) that have been plugged in nicely to PHOCOA for easy use and require even less setup. All you have to do is supply a PHP callback to provide dynamically loaded data. No Javascript code is required at all.

AJAX Integration Basics

Setting Up AJAX Event Handlers

At the highest level, PHOCOA provides an "onEvent" property for all classes in the WFView hierarchy that is used to attach Javascript behaviors to your application. Since the onEvent interface takes in a string as a parameter, you can configure AJAX behaviors via YAML with no PHP coding. If you need more complex behavior, you can always use the PHP API, but 95% of the time you'll find that onEvent works perfectly.

The basic syntax is:

onEvent: <eventName> do <typeOfAction>[:<target>][:<action>]
  • eventName - The event name to listen for, i.e., click, change, blur, etc.
  • typeOfAction - j for JSAction, s for ServerAction, or a for AjaxAction.
  • target - The target setting used for ServerAction or AjaxAction. The default is #page# (the page delegate). You can use this optional setting to target the object of the action to #page#outlet.keyPath or #module#keyPath.
  • action - The action to be called on the target object.
    JSAction
    The default is the Javascript function PHOCOA.widgets.<widgetId>.events.<eventName>.handleEvent.
    If you put in your own action, it will be executed as an anonymous Javascript function.
    By default, the underlying DOM event that triggered the JSAction will *not* be stopped. To stop event propagation, call this.stopEvent(event) from your event handler (works in handleEvent or a "j:" customer handler).

    ServerAction and AjaxAction
    The default is the php method <widgetId>Handle<eventName>.
    The Javascript handleEvent function PHOCOA.widgets.<widgetId>.events.<eventName>.handleEvent is also called. To cancel the event, implement this function and return false to cancel the RPC. Any return value (or lack thereof) besides "false" will result in the RPC being called.
    If you put in your own action, it will be interpreted as the php method call of that name on the target object.
    The underlying DOM event that triggered ServerAction or AjaxAction is stopped to prevent event propagation.

A few examples (all added to the widget of id myWidget):

  • onEvent: click do j
    Will call the Javascript function PHOCOA.widgets.myWidget.events.click.handleEvent. This is simly the "default" Javascript function based on a naming convention that phocoa will call.
  • onEvent: click do j: myFunc()
    Will call the Javascript function myFunc.
  • onEvent: click do j: alert("HI");
    Will execute alert("HI").
  • onEvent: click do s
    Will refresh the page and execute the server action #page#myWidgetHandleClick (which simply calls the myWidgetHandleClick method of the page delegate).
  • onEvent: click do s:myPhpFunc
    Will refresh the page and execute the server action #page#myPhpFunc (which simply calls the myPhpFunc method of the page delegate).
  • onEvent: click do a:#module#:myPhpFunc
    Will make an AJAX request, executing the server action #module#myPhpFunc (which simply calls the myPhpFunc method of the module).

Sending data from AJAX handlers back to the client

For many AJAX (a:) operations, the server will need to send data back to the client, and the client will need to react to that data.

To return data from your Ajax callback, simply return a WFActionResponse instance. There are several subclasses for handling different return types:

  • JSON
    return new WFActionResponseJSON($phpData);
  • XML
    return new WFActionResponseJSON($xmlString);
  • Plain Text
    return new WFActionResponsePlain($textString);
  • Cause UI Updates - PHOCOA has a special WFActionResponse subclass that the PHOCOA client layer will interrupt and process automatically. It is a very easy way for you to effect updates in the browser. The WFActionResponsePhocoaUIUpdater class allows you to update HTML elements, replace them, or run Javascript code that is sent from the server. WFActionResponsePhocoaUIUpdater has a fluent interface to make it easy for you to send multiple updates at the same time.
    return WFActionResponsePhocoaUIUpdater::WFActionResponsePhocoaUIUpdater()->
             ->addUpdateHTML('myDiv', '<b>new html</b>')
             ->addReplaceHTML('myOtherDiv',
                                 '<div id="myOtherDiv">replacement div</div>')
             ->addRunScript('alert("You did it!");');
             

Having the client deal with data returned from the Ajax call

When your Ajax call completes successfully, the clickSuccess handler for that widget (if it exists) will be executed.

Example:


// php handler
public function myCustomFunction($page, $params, $senderId, $eventName) {
   // do stuff
   return new WFActionResponseJSON(array('customString' => 'something meaningful'));
}

// javascript handler
PHOCOA.namespace('widgets.myLink.events.click');
PHOCOA.widgets.myLink.events.click.ajaxSuccess = function(response) {
     // response is a native Javascript object
     alert(response.CustomString);
}

Advanced AJAX Integration

PHOCOA uses a delegation paradigm to implement the AJAX integration. We have already looked at the default handleEvent delegate method above. There are a few additional delegate methods that you can implement if you want to pass arguments to your handleEvent function, or have specialized success or error handlers.

  • PHOCOA.widgets.<widgetId>.events.<eventName>.collectArguments() is called when the event you're listening to fires, to give you a chance to tell the infrastructure what arguments to pass to your Javascript handler.
  • PHOCOA.widgets.<widgetId>.events.<eventName>.ajaxSuccess() is called if an AJAX RPC occurred, and succeeded.
  • PHOCOA.widgets.<widgetId>.events.<eventName>.ajaxError() is called if an AJAX RPC occurred, and failed.

Examples

JSAction - call a Javascript function when an event fires

Local JS Action

The setup for this is done in the YAML file by specifying the onEvent property:

onEvent: click do j
And, in Javascript, set up the delegate functions:
PHOCOA.namespace('widgets.localAction.events.click');
PHOCOA.widgets.localAction.events.click.collectArguments = function() { return ['myArg1', 'myArg2']; };
PHOCOA.widgets.localAction.events.click.handleEvent = function(e, myArg1, myArg2) {
    alert("I've been clicked!\nThe first argument to the callback is the event: "
    + e + "\nFollowed by all arguments from collectArguments(): " + myArg1 + ", " + myArg2);
};

ServerAction - refresh the page to execute an action on the server when an event fires

ServerAction The server will stick a random number in this space when you click the link above.

The setup for this is also trivially simple. In the YAML file:

onEvent: click do s
In PHP, we implement the default callback method <widgetId>Handle<EventName>:
function rpcPageDelegateServerHandleClick($page, $params, $senderId, $eventName)
{
    if (WFRequestController::sharedRequestController()->isAjax())
    {
        return WFActionResponsePhocoaUIUpdater::WFActionResponsePhocoaUIUpdater()
            ->addUpdateHTML('ajaxTarget', 'I am the server and this is my random number: ' . rand());
    }
    else
    {
        $page->outlet('ajaxTarget')->setValue('I am the server and this is my random number: ' . rand());
    }
}

You will notice that we handle the event different based on whether the call is an AJAX call or not...

For the ServerAction, we need only update the widget's value. This will be reflected in the HTML response that is sent to the client, just as done in normal PHOCOA action handlers.

For the AjaxAction, to effect the UI updates on the client, we return a WFActionResponsePhocoaUIUpdater object. This object has addUpdateHTML(), addReplaceHTML(), and addRunScript() methods that make it easy for you to update the innerHTML of any element, replace any element, and run Javascript code in response to an AjaxAction.

AjaxAction - make an AJAX call when an event fires

We are using the same example as above, but turning it into an AJAX action.

AjaxAction Click the link and look to the right of the "ServerAction" link above...

For this example, we want to call a PHP method other than the default, since we've already set up the method we need for the above example:

onEvent: click do a:rpcPageDelegateServerHandleClick

Event and Widget Support

The PHOCOA Ajax integration supports several DOM events, which are allowed on most of the WFView subclasses. The blocks below demonstrate various UI widgets and DOM events:

  • Click
  • Mouseout
  • Mousedown
  • Mouseup
  • Change:
  • Focus:
  • Blur:
  • Multiple Events (try mousing over me and clicking me)

Form Integration

The PHOCOA programming model for form submission is extended with our Ajax integration. Everything works the same way, except that you can return WFActionResponse objects to effect UI changes from the server. Even PHOCOA's validation infrastructure works with Ajax.

Below is an example form with two fields. The first field requires any string but "bad" and the second field requires any string but "worse". If there are no errors, the two strings will be interpolated into a single response and updated in the UI.

The submit button is a normal form submit. The link will submit the form via Ajax.


Enter some text:
type 'bad' to trigger an error


Enter some other text:
type 'worse' to trigger an error


Once again, the code to build this Ajax functionality is quite simple.

In YAML, we set up our link to trigger the form submission:

onEvent: click do a:ajaxFormSubmitNormal
We also implement our ajaxFormSubmitNormal action handler, which in this example, we use for both normal and ajax form submission:

function ajaxFormSubmitNormal($page, $params, $senderId, $eventName)
{
    $result = 'You said: "' . $page->outlet('textField')->value() . '" and "' . $page->outlet('textField2')->value() . '".';
    if (WFRequestController::sharedRequestController()->isAjax())
    {
        return WFActionResponsePhocoaUIUpdater::WFActionResponsePhocoaUIUpdater()
            ->addUpdateHTML('ajaxFormResult', $result);
    }
    else
    {
        $page->assign('formResult', $result);
    }
}
When combined with the template code:
<div id="ajaxFormResult">{$formResult}</div>
The proper result is displayed either by Ajax or by traditional template programming.

In Javascript, we have an eventHandler to "remove" our "result" when the request is submitted. This prevents previous results from showing if there is an error with the current submission.

PHOCOA.namespace('widgets.ajaxFormSubmitAjax.events.click');
PHOCOA.widgets.ajaxFormSubmitAjax.events.click.handleEvent = function(e) {
    $('ajaxFormResult').update();
};

Examples

Cookbook
  Widgets
  Bindings
  Pagination
  Formatters

Basic Examples
  Email
  Skin Browser
  Forms
  Regex Tester

AJAX Examples
  Infrastructure

  (YUI Examples)
  AutoComplete
  ColorPicker
  Menu
  Tree View
  Tab View
  Container

Appcelerator Integration
  Demo
Copyright (c) 2014 Alan Pinstein. All Rights Reserved.