CartoWeb 3.5.0
Download Now!


CartoWeb
Advanced Geographical Information System for the Web

Nine steps to make your plugin ajaxable, enabling it to refresh the Cartoweb GUI asynchronously.

Prerequisites

  • You know PHP and Javascript basics
  • You know about coding plugins in Cartoweb
  • You have a good knowledge of CartoWeb functional principles

  • You have your plugin ready and working in synchronous mode (that is, as we've always done it)

Step by step

1. Copy the code skeleton for the Javascript side of your plugin

  1. Create the following file /plugins/[yourPluginName]/htdocs/js/[youPluginName].ajax.js

  2. Copy and paste the following skeleton into the newly created file:

    AjaxPlugins.[yourPluginName] = {
    
        handleResponse: function(pluginOutput) {
        }
    
    };
    
    /*
     * Plugin actions
     */
    AjaxPlugins.[yourPluginName].Actions = {};
    
  3. Replace [yourPluginName] with your plugin name.

2. Define the AJAX action(s) made available by your plugin

  1. Below the AjaxPlugins.[yourPluginName].Actions = {}; copy and paste the following code for each action.

    AjaxPlugins.[yourPluginName].Actions.[actionName] = {
        buildPostRequest: function(argObject) {
            return AjaxHandler.buildPostRequest();
        },
        buildGetRequest: function(argObject) {
            return '';
        },
        onBeforeAjaxCall: function(argObject) {
        },
        onAfterAjaxCall: function(argObject) {
        }
    }
    
  2. For every action, copy the AjaxPlugins.[yourPluginName].Action.[actionName] and replace the [actionName] with your action name.

  3. If your plugin provides more than one action, you might want to code and test one action first.

3. For each action, define the GET/POST parameters to be passed to the Cartoclient

  1. Adapt the buildPostRequest() method. It returns a query string containing all the POST parameters sent to the Cartoclient when this actions is triggered.
  2. Do the same with the buildGetRequest() method if you need to send GET parameters. It is likely that your plugin doesn't need any GET parameters. In that case, you can simply remove this method.

4. Optionnaly define a specific behaviour for before and after AJAX calls (for each action)

  1. You can implement onBeforeAjaxCall() and onAfterAjaxCall() method if you want some specific logic to be executed before and after AJAX calls

    • onBeforeAjaxCall() is executed before the action is triggered

    • onAfterAjaxCall() is executed after the plugins have refreshed.

In example, this logic could lay a spinner on your plugins GUI to show to user it is being update.

5. Attach your action(s) trigger(s) to the GUI widgets

In order to trigger an AJAX action you can either attach it using the onclick HTML attribute in your plugin template, or attach it using the event system.
Attention: If your plugin refresh logic replaces HTML code containing element with listeners, you need to either (1) use the onclick attribute or (2) detach the listeners before the refresh and reattach them after (for each refresh). This is due to IE issues when swapping elements with listeners for the same elements.

  • Using the onclick attribute

    1. Add the suitable event attribute to your HTML widget
    2. Write the instruction to trigger an AJAX action defined above. In example:

      <input type="checkbox" name="myCheckBox" onclick="javascript:CartoWeb.trigger('Layers.LayerShowHide', 'formItemSelected()');" />
      
    • Syntax: void CartoWeb.trigger(string actionToTrigger, string nonAjaxInstruction, object argObject)

      • string actionToTrigger: name of the AJAX action to trigger
      • string nonAjaxInstruction: instruction to execute if AJAX mode is disabled (fallback)
      • object argObject: optionnal object containing arbitrary information that can be used by the plugin Javascript logic (methods in AjaxPlugins.[yourPluginName])

  • Using the event system
    1. Create a AjaxPlugins.[yourPluginName].init() method to be called on page load (called from AjaxPlugins.Common.init() in /htdocs/js/AjaxPlugins.js)

    2. Write the instructions to attach actions, using AjaxHandler.attachAction():

      AjaxPlugins.[yourPluginName] = {
          handleResponse: function(pluginOutput) {
              [...]
          }
      
          init: function() {
              // Pan buttons
              AjaxHandler.attachAction('pan_n', 'click', 'Location.Pan', {source: 'button'});
              AjaxHandler.attachAction('pan_nw', 'click', 'Location.Pan', {source: 'button'});
              [...]
              AjaxHandler.attachAction('pan_ne', 'click', 'Location.Pan', {source: 'button'});
      
              // Keymap pan
              AjaxHandler.attachAction('keymap', 'click', 'Location.Pan', {source: 'keymap'});
          }
      }
      
    • Syntax: void Ajaxhandler.attachAction(string elementId, string eventType, string actionToTrigger, object argObject)
      • string elementId: id of the HTML element to attach the action to.
      • string eventType: event to attach the action to (click, change, ...)

      • string actionToTrigger: name of the AJAX action to trigger
      • object argObject: optionnal object containing arbitrary information that can be used by the plugin Javascript logic (methods in AjaxPlugins.[yourPluginName])

    • Note: in this case, the argObject is used by the AjaxPlugins.Actions.Pan() logic.

6. For each action, define plugins enable level (what other plugin is needed by your plugin)

Now that we have defined and attached actions to your widgets on the Javascript side, we can dig into the Cartoclient side to define how your plugin responds to these actions.

  1. Open your ClientPlugin located in /plugins/[yourPluginName]/client/Client[PluginName].php

  2. Implement the Ajaxable interface in your class declaration:

    class ClientLayers extends ClientPlugin
                       implements Sessionable, GuiProvider, ServerCaller,
                                  Exportable, InitUser, Ajaxable {
    
  3. Implement the ajaxHandleAction() method in your Client[PluginName]. It will look that this:

    /**
     * @see Ajaxable::ajaxHandleAction()
     */
    public function ajaxHandleAction($actionName, PluginEnabler $pluginEnabler) {
        switch ($actionName) {
            case 'Layers.LayerShowHide':
                $pluginEnabler->disableCoreplugins();
                $pluginEnabler->enablePlugin('images');
            break;
            case 'Layers.LayerDropDownChange':
                $pluginEnabler->disableCoreplugins();
                $pluginEnabler->enablePlugin('layers');
                $pluginEnabler->enablePlugin('images');
             break;
         }
    }
    
  4. By default, coreplugins are enabled and plugins are disabled.
  5. Depending on the $actionName, set the plugin execution level need for your plugin execution.
    • disablePlugin() sets the plugin enable level to ClientPlugin::ENABLE_LEVEL_SERVERCALL (processes GET/POST requests and builds/handles Cartoserver requests)

    • enablePlugin() sets the plugin enable level to ClientPlugin::ENABLE_LEVEL_FULL (same as above, plus render XML response)

    • You can use setEnableLevel() to set another predefined enable level (i.e. ClientPlugin::ENABLE_SERVER_LOAD)

  6. Important: to prevent coreplugins/plugins and plugins/plugins coupling, coreplugins and plugins activation have to be located at the right place, as described below:

    • If you're developing a coreplugin...
      • ...and need to enable another coreplugin: you can activate it either in your coreplugin or in the other coreplugin (since coreplugins is an atomic whole)
      • ...and need to enable a plugin: you *have to* activate it in the plugin (that means, have a case 'CorepluginName.ActionName': ... break; in the plugin)

    • If you're developing a plugin...
      • ...and need to enable a coreplugin: you *have to* activate it in your plugin or in the other coreplugin (since coreplugins is an atomic whole)
      • ...and need to enable another plugin: you *have to* activate it in the other plugin (that means, have a case 'CorepluginName.ActionName': ... break; in the other plugin)

7. Define the data sent back by your plugin for GUI refresh on AJAX actions

  1. Open your ClientPlugin /plugins/[yourPluginName]/client/Client[PluginName].php

  2. Implement the ajaxGetPluginResponse() that will look like this:

    /**
     * @see Ajaxable::ajaxGetPluginResponse()
     */
    public function ajaxGetPluginResponse(AjaxPluginResponse $ajaxPluginResponse) {
        $ajaxPluginResponse->addHtmlCode('layers', $this->drawLayersList());
        $ajaxPluginResponse->addHtmlCode('switches', $this->drawSwitches());
        $ajaxPluginResponse->addHtmlCode('switch_id', $this->layersState->switchId);
        $ajaxPluginResponse->addVariable('startOpenNodes', "'" . implode('\',\'', $this->unfoldedIds) . "'");
    }
    
  3. Add HTML code or variables to the plugin response.
  4. Syntax: void addHtmlCode(string htmlCodeId, string htmlCodeValue)
    • string htmlCodeId: id for the html code to be added
    • string htmlCode: html code to add
  5. Syntax: void addVariable(string viariableId, string variableValue)
    • string variableId: id for the variable to be added
    • string variable: variable to add

8. Define how the plugin refreshes the GUI on the Javascript side

When your plugin is enabled at the FULL level, the data defined in ajaxGetPluginReponse() is be encapsultated in an XML flow. The presence of your plugin data in this XML flow will trigger a call to AjaxPlugins.[yourPluginName].handleResponse() method. This method defines how your plugin GUI is refreshed.

  1. Open your plugin Javascript side: /plugins/[yourPluginName]/htdocs/js/[yourPluginName].ajax.js

  2. Code the handleResponse() body. Here is an example for the layers plugin:

    handleResponse: function(pluginOutput) {
        /* Redraws layers HTML Code */
        var guiHtmlCode = pluginOutput.htmlCode.switches + pluginOutput.htmlCode.layers;
        AjaxHandler.updateDomElement('folder2', 'innerHTML', guiHtmlCode);
    
        /* Reopen open nodes */
        startOpenNodes = pluginOutput.variables.startOpenNodes;
        // Uses layers.tpl + layers.js mecanism
        eval("var openNodes = new Array(" + startOpenNodes + ");");
        writeOpenNodes(true);
    }
    

In this example, the HTML code added with 'switches' and 'layers' identifiers is injected into the HTML element whose id is 'folder2' through the AjaxHandler.updateDomElement() method. Then, the open nodes of the layer tree are reopenend as they were before the injection.

9. Include your plugin Javascript side

  • Add the following line in the <head> tag of the cartoclient.tpl file:

    <script type="text/javascript" src="{r type=js plugin=[yourPluginName]}[yourPluginName].ajax.js{/r}"></script>
    

CartoWeb Wiki: HowToAjaxablePlugin (last edited 2020-08-15 07:20:16 by MathieuBornoz)

© 2002-2007 CartoWeb by Camptocamp SA - Wiki powered by MoinMoin