Server-Side Events

Client-Server Event Binding

Client-Server event binding is what makes Raxan truly special. Imagine never having to write a single line of JavaScript code in order to receive an Ajax notification when a user clicked on a button. Imagine being able to bind your server-side event handler to just about any HTML tag inside your web page. The possibilities are endless when you realize that you have total control over every DOM element inside your client's web browser.

Binding to an Event

Sending data to and from the server requires very little or no JavaScript coding when an event is triggered within a browser. In fact, it is very straightforward to configure your web pages to receive an server-side event notifications from the client .

The simplest way to do this is to use the xt-bind extended attribute to listen to events being triggered from the client.

Usage:

xt-bind="type, callback, serialize, autoDisable, autoToggle"
  • type - The type of event to be invoked on the server.
  • callback - Server side callback method. The method name must be a class method within the page.
  • serialize - CSS selector - Form elements that are to be serialized when sending data to the server.
  • autoDisable - CSS selector - Disables the selected elements during event transmission. Set to true to disable the current element
  • autoToggle - CSS selector - Show/Hide selected elements during event transmission. Set to true to toggle the current element

Note: Comma separate CSS selectors (e.g .class1, .class2, .classN) are currently not supported when using the xt-bind attribute. They can however be used with the bind() method.

Usage Examples:

xt-bind="click, showDetails" - makes a synchronous call to the server

xt-bind="#click, showDetails"  - makes an asynchronous call to the server

- The following will disable the current element, toggle the "#loader" element and serialize
- the all "form" elements before making an asynchronous post back to the server

xt-bind="#click,editUser,form,true,#loader"

Working example:

<?php
    require_once('raxan/pdi/autostart.php');

    class MyPage extends RaxanWebPage {

        // callback function
        protected function buttonClick($e) {
            // select the #msg element and set html to hello world
            $this->msg->html('Hello World');
        }
    }

?>

<form name="form1" action="" method="post">
    <input id="mybutton" type="button" value="Click Me" xt-bind="click,buttonClick" />
    <div id="msg" />
</form>

Event Delegates

Event delegates are special events that are used to bind an event handler to a dynamic set of elements. This eliminates the need of having to individually bind each element to an event handler whenever the set is updated.

To do this you can use either the xt-delagate extended attribute or the

Usage:

xt-delegate="selector type, callback, serialize, autoDisable, autoToggle"
  • selector - CSS Selector - The selected elements to bind (or delegate) the callback method
  • type - The type of event (e.g. click) to be invoked on the server

Note: See xt-bind for a descrition of the other parameters

Usage Example:

xt-delegate="a.edit click, editRecord"   - delegates a.edit click event

Working Example:

<?php
    require_once('raxan/pdi/autostart.php');

    class NewPage extends RaxanWebPage {

        protected function doSomething($e) {
            $i = $e->intval(); // get the integer value for the event
            $this->panel->text('You have clicked item #'.$i);
        }
    }

?>

<ul xt-delegate="li a click,doSomething">
    <li><a href="#1">Item 1</a></li>
    <li><a href="#2">Item 2</a></li>
    <li><a href="#3">Item 3</a></li>
    <li><a href="#4">Item 4</a></li>
</ul>
<div id="panel"></div>

The above will delegate the click event for all a.edit elements within for the current <ul> element. Newly added items that matches the a.edit selector item will be automatically bounded to the click event handler.

Timeout Events

There are times when you will need to periodically update an element on a web page with content from the server without any user intervention. This task would normally require a developer write JavaScript code to periodically poll the server for updates.

Raxan eliminates the need to have to manage your own client-side updates with the xt-autoupdate extended attribute:

Usage:

xt-autoupdate = "true"  - Default mode. Enable automatic updates during ajax calls
xt-autoupdate = "seconds, callback, repeat, serialize, autotoggle" - enable timeouts
  • seconds - The number of seconds to wait before invoking the event.
  • callback - Server side callback method. The method name must be a class method within the page.
  • repeat - Sets number of times to repeat an Ajax timeout event. Setting the value to "true" or "repeat" will repeat the timeout indefinitely. Defaults to 1.
  • serialize - CSS selector - Form elements that are to be serialized and posted to the server.
  • autoToggle - CSS selector - Show/Hide selected elements during event transmission. Set to true to toggle the current element

Note: Setting xt-autoupdate="true" will automatically cause the element to be updated during ajax calls. This is similar to calling the updateClient() method.

Usage Examples:

xt-autoupdate = "true"  - enable automatic updates during ajax calls
xt-autoupdate = "5000,update,true" - updates every 5 seconds (repeatedly)

- The following will asynchronously updates the element every
- 15 seconds (repeatedly) and toggles #loader during updates

xt-autoupdate = "#15000,update,true,,#loader"

Working Example:

<?php
    require_once('raxan/pdi/autostart.php');

    class NewPage extends RaxanWebPage {

        protected function doSomething($e) {
            $this->msgbox->text('A message from your server');
        }
    }

?>

<a href="#" xt-bind="#click,doSomething">Click Here</a>
<div id="msgbox" xt-autoupdate="true"></div>

Setting Target Window

It's possible to have the browser display your event response within a new window of frame. To do this, you can use the target or formtarget attribute to set the window where the results will be displayed when the user triggers an event.

Example:

<a href="#" target="_blank" xt-bind="click,popup">New Window</a>

<input type="button" name="button1" id="button1" value="New Window" formtarget="_blank" xt-bind="click,popup" />

<div target="_blank" xt-bind="click,popup">New Window</div>

Target values:

  • _blank - load event response in a new blank window.
  • _parent - load event response in the immediate parent of current document
  • _self - load event response in the current window
  • _top - load event response in the full body of the window. This useful for breaking out of a nested FRAMESET
  • window name - Name of window or frame to load event response. For example: mywindow

Note: Target window will only work for full-page events.

Using the bind() and delegate() methods

The bind() method provides you with more options when compared to the xt-bind attribute. With the bind() and delegate() method you can use various event callback options:

  • Bind Function name to event. Call a user-defined function. Example:

    <?php
        $this->button1->bind('click', 'My_Function_Name');
    ?>
    
  • Bind Page Method name to event. Call a method on the current page. Example:

    <?php
        $this->button1->bind('click', '.My_Page_Method');
    ?>
    

    Event callbacks are handled directly from the Page controller. When you bind an event using '.callback' the framework will automatically look for the method on the page controller instance. In this case the method can be protected or public. The '.callback' format is only a shortcut to array($this, 'callback').

  • Bind Object method to event:

    <?php
        $this->button1->bind('click',array($object, 'method_name'));
    ?>
    

    When binding an event to a method that's external to the page controller, you will have to use the array($object, 'method_name') format. In this case method_name() must be a public method since it will be called from the page controller.

  • Bind Anonymous functions to event. This only works on PHP 5.3 and higher:

    <?php
        $this->button1->bind('click',function($e){
            // do something here
        });
    ?>
    

The bind() method can be configured to submit selected form content to the server or hide/show elements before or after an event is triggered:

<?php

    $this->button1->bind('#click',array(
        'callback' => '.auto_search',
        'autoDisable' => true,   // disable the button during event call to server
        'autoToggle' => 'img#pre', // show pre loader
        'serialize' => 'form :input' // serialize and post form inputs back to the server
    ));

?>

As stated above, the delegate() method is similar to the bind() method with the exception that it takes an additional css selector as the first parameter:

<?php

    // delegate the click event for all a.edit element within list1
    $this->list1->delegate('a.edit', '#click', '.editRecord');

?>

Note: Event bindings must done before or during page load.

Bind options for events

  • callback - Server side callback function
  • value - Value to be passed to the server-side event. This can be assigned from the client-side
  • prefTarget - Sets the preferred target element to be invoked on the server. Format: target@url - @url is optional.
  • targetWindow - Sets the target window to display full-page event response.
  • accessible - Modify hyperlinks and form action urls to trigger server-side events without the use of javascript
  • serialize - Form input CSS selector - sets the form or input elements to be submitted to the server when the event is trigger.
  • script - (String/Array) - Javascript to be executed before event is sent to server. Use an associative array to assigned scripts that are to be executed before and after a callback. Example: array('before' => 'alert("Before Call")', 'after' => 'alert("After Call")')
  • data - Data to be passed the to server-side event. This data is only passed to the event handler when the event is triggered. It is not sent to the client.
  • autoDisable - Disables the selected elements during event transmission. Accepted values: TRUE | CSS Selectors. If TRUE is specified then the current element will be toggled
  • autoToggle - Show/Hide selected elements during event transmission. Accepted values: TRUE | CSS-selectors. If TRUE is specified then the current element will be toggled
  • delay - Delays server-side callback in milliseconds. Accepted values: TRUE | Integer. If TRUE is specifed then delay will be set to 200ms (minimum value)
  • inputCache - Specifies the minimum number of input characters to cache before sending request to server. Possible values: TRUE or Interger - Number of characters to cache. This option is only valid for <input type="text"> and <textarea> elements.
  • repeat - Number of times to repeat an ajax timeout event. Used internally by the timeout() method. Setting the value to TRUE will repeat the timeout indefinitely. Defaults to 1 for ajax timeouts.
  • view - Set the view that should be called when the event is being invoked.
  • confirm - Used to display a confirmation popup dialog before then event is triggered.

Event Object

When the event handler is called, an instance of the RaxanWebPageEvent object is passed as the first parameter with the following properties:

  • target - Target DOMElement
  • type - event type. Example: click
  • value - value return from client. This value can either be assigned when binding to the event or it can be retrieved from the target element
  • pageX - Mouse position X
  • pageY - Mouse position Y
  • targetX - Target element position X on client
  • targetY - Target element position Y on client
  • which - Ascii Key value
  • button - Mouse button
  • ctrlkey - Control Key
  • metakey - Meta Key (Mac)
  • uiDraggable - jQuery UI Draggable DOMElement
  • uiHelper - jQuery UI Helper DOMElement
  • uiSender - jQuery UI Sender DOMElement

Available methods:

  • textVal - Returns text value after removing the html tags
  • intVal - Returns an interger if value is numeric or null if there was an error
  • floatVal - Returns float if value is numeric or null if there was an error.

Passing values from client to the Event Handler

When an event is trigger a value is submit to the event handler. This can be useful when you want to carry out an action based on the returned value.

To do this you can either assign the value when binding the event to the DOM element:

<?php
    protected function _init() {
        $this->button1->bind('click',array(
            'callback'=>'.eventHandler',
            'value' => '12345'
        ));
    }

    protected function eventHandler($e) {
        // retrieve event value from the $e object
        $value = $e->value();

        // retrieve and sanitize event value
        $value = $e->intVal();

        /* do something here */
    }
?>

Or set the value using one of the following methods:

If the element is an anchor tag, then add the value after the url "#" (hash):

<a href="#10" xt-bind="clic,changePage">Next Page</a>

If the element is a form element then the content of the "value" attribute will be sent back to the server as the event value

<select name="select1" id="select1" xt-bind="change,doSomething">
    <option value="1">Item 1</option>
    <option value="2">Item 2</option>
</select>

To set the event value on any element (including the above), you can use the "data-event-value" attribute or add the value to the "class" attribute. When using the class attribute you must first add the "v:" prefix to the value as shown below:

<button id="btn1" 
    class="v:12345"
    xt-bind="click,showMessage">Submit</button>

Or use the data-event-value attribute as follows:

<button id="btn2"
    data-event-value="12345"
    xt-bind="click,showMessage">Show Message</button>

Note: Adding values to the class attribute has being deprecated. Use the data-event-value attribute.

Additional data attributes

In addition to the the data-event-value attribute there are two other attributes that can be assigned to an element:

  • data-event-view - This is used to determine the view to loaded when the server-side event is being triggered.

    <button id="btn1"
        data-event-view="page"
        xt-bind="click,changeView" >Next Page</button>
    
  • data-event-confirm - This is used to display a confirmation dialog before then event is triggered.

    <button id="btn1"
        data-event-confirm="Are you sure you want to delete this records?"
        xt-bind="click,deleteRow">Delete Record</button>
    

When the button is clicked, the value of the data-event-confirm attribute will be displayed as a confirmation popup before the request is sent to the server.

Registering Custom Server-Side Events

There are times when you need to create special events for a page in order to execute a specific task. To do this you can you the registerEvent() method.

The following will register an event for the current page and bind the event to a callback handler:

<?php
    protected function _init() {
        // registers a custom event
        $this->registerEvent('custom-event','.myEventHandler');
    }
?>

Note: Events must be registered before or during page load.

Triggering a Server-Side Event

Server-side events can be triggered by using the framework's API, client-side scripts, form submits or hyperlinks.

Using Hyperlinks to trigger events

Events can be triggered from a url or form by submitting special name/value pairs to the server via a POST or a GET method.

Here's a list of some of the name/value pairs that are used when making a GET request:

  • _e[target] - Target element id. For registered events the target id should be set to "page"
  • _e[type] - Event Name - Defaults to click
  • _e[value] - (Optional) Value to be passed to the event
  • _e[tok] - Special token to be sent to server. This entry is optional if the event was registered using @global.

Before you can trigger the event from a url you will have to either 1) bind the event as a global event using the @global keyword or 2) pass a special token to the server along with the event request. Use the Raxan::$postBackToken property to retrieve the current token value.

Here's an example using @global:

<?php
class NewPage extends RaxanWebPage {
    protected function _init() {
        // register editorder event as a global event
        $this->registerEvent('editorder@global','.editOrder');
    }

    protected function editOrder($e) {
        $v = $e->intVal();  // get event value
        /* do something here */
    }
}
?>

<a href="order.php?_e[target]=page&_e[type]=editorder&_e[value]=1234">Click here</a>

The example above will register the editorder event as a global event so that it can be triggered from any url. This will also override the need for passing the token to the event.

Another solution is to let the framework handle the request gracefully. This is useful when JavaScript is not enabled inside the web browser:

<?php
    protected function _config() {
        $this->degradable = true; //enables degradable mode if JavaScript is not available
    }

    protected function _init() {
        $this->button1->bind('click','.editOrder');
    }
?>

<a id="button1" href="#1234">Edit Order</a>

In the above example, the framework will automatically generate the necessary code to trigger the event when JavaScript is not available.

Note: Raxan has a built-in security feature that helps to prevent Cross-Site Request Forgery (CSRF). Each event request must be accompanied with a valid token id. This token is unique to the user's active browser session and will be destroyed once the browser is closed. To bypass this security check you must append the @global to the event name when binding to or registering the event.

Using JavaScript to trigger events

In addition to hyperlinks, you can programmatically trigger a server-side event by using either the Raxan.dispatchEvent() method or manually triggering the event on the browser's DOM element:

    <script type="text/javascript">
        function dispatchEventExamples() {

            // manually trigger button1 click event
            $('#button1').trigger('click'); // this will also trigger the click event on the server

            // trigger button1 click event (normal post back)
            Raxan.dispatchEvent({
                type: 'click',          // event name
                target: 'button1'       // target element
            })

            // explicitly trigger button1 click event via ajax
            Raxan.dispatchEvent({
                type: '#click',             // event name
                target: 'button1'
            })

            // implicitly trigger click event via ajax by adding a callback function
            Raxan.dispatchEvent({
                type: 'click',          // event name
                target: 'button1',
                complete: function(result,success){
                    if (!success) return;
                    if (result=='ok') alert('Task completed');
                }
            })

            // trigger button2 "click" event on another page
            Raxan.dispatchEvent({
                type: 'click',              // event name
                target: 'button2',
                url:  'another-page.php'   // page url where event is registered
            })


            // passing a value to the event
            Raxan.dispatchEvent({
                type: 'click',          // event name
                target: 'button1',
                value: 'This is a messge from the browser',
                complete: function(result,success){
                    if (!success) return;
                    // do something here
                }
            })

            // serialize form elements
            Raxan.dispatchEvent({
                type: 'click',          // event name
                target: 'button1',
                serialize: '#form1',    // css selector
                complete: function(result,success){
                    if (!success) {
                        // handle error
                        alert('Error while connecting to server');
                        return true;  // let the Raxan know that we that the error was handled
                    }
                    // do something here
                }
            })

        }
    </script>

To trigger a custom (registered) event from the client, use the dispatchEvent() method as shown below:

<script type="text/javascript">
    function dispatchEventExamples() {
         // trigger "custom" event (normal post back)
        Raxan.dispatchEvent('custom-event');

        // implicitly trigger "custom" event via ajax by passing a callback function
        Raxan.dispatchEvent('custom-event',function(result,success){
            if (!success) return;
            // do something
        });

        // passing a value to the custom event
        Raxan.dispatchEvent('custom-event',value,function(result,success){
            if (!success) return;
            // do something
        });
    }
</script>

The dispatchEvent() method is primarily to use to invoke custom (registered) events. See Client-Server Introduction for more information.

Intercepting Events

It's possible to intercept and prevent a server-side event from being triggered by listening to special events on the target element. This feature can be useful when you need to perform an action before or after the event has been triggered.

To do this you can use one or more of the following client-side events:

  • togglecontent - This event is similar in function to the autotoggle parameter of "xt-bind" and "xt-delgate" attributes. It can be used to toggle preloaders or validate user data.
  • disablecontent - This event is similar in function to the autodisable parameter of the "xt-bind" and "xt-delgate" attributes. It can be used to disable elements before the data is sent to the server.
  • serializecontent - This event is similar in function to the serialize parameter of the "xt-bind" and "xt-delgate" attributes. It can be used to serialize data to be submitted to the server.

The togglecontent event

This event is invoked before and after an event notification is sent to the server. This can be useful when you want to validate form data or toggle special pre-loader elements:

<script type="text/javascript">
    Raxan.ready(function(){
        $('#buton1').bind('togglecontent',function(e,mode){
            if (mode=='on') {       // before server-side event execution
                var valid = true;
               // code to validate form data
               if (!valid) return false; // return false to cancel event execution

               // show pre-loader
               $('#preloader').show();
            }
            else if (mode=='off') { // after server-side event execution
                // hide pre-loader
                $('#preloader').hide();

                // get data returned from server-side event handler
                var result = e.serverResult;
                // do something with result
            }
        })
    })
</script>

Note: If the "autotoggle" selector option or parameter was assigned to the element on the server, then the "autotoggle" elements will be toggled before this event is invokded.

The disablecontent event

This event is invoked before and after an event notification is sent to the server.

<script type="text/javascript">
    Raxan.ready(function(){
        $('#buton1').bind('disablecontent',function(e,mode){
            if (mode=='on') {       // before server-side event execution
                var valid = true;
               // code to validate form data
               if (!valid) return false; // return false to cancel event execution

               // disable form elements
               $('#form :input').attr('disabled','disabled');
            }
            else if (mode=='off') { // after server-side event execution
                // enable form elements
                $('#form :input').removeAttr('disabled');

                // get data returned from server-side event handler
                var result = e.serverResult;
                // do something with result
            }
        })
    })
</script>

Note: If the "autodisable" selector option or parameter was assigned to the element on the server, then the "autodisable" elements will be disabled/enabled before this event is invokded.

The serializecontent event

This event is invoked before an event notification is sent to the server. The event handler must return a 2D array containing data to be submitted to the server.

<script type="text/javascript">
    Raxan.ready(function(){
        $('#buton1').bind('serializecontent',function(e){
            var data = $('#form').serializeArray();
            // add additional data to be submitted to server
            data[data.length] = {name:'any-name', value:'any-value'}
            return data;
        })
    })
</script>

Note: If the "serialize" selector option or parameter was assigned to the element on the server, then the data from the "serialize" elements will be merged with data returned from the serializecontent event.


Up Next: Server-Side Templates