User:Mark Johnson/Mforms and AJAX

Jump to: navigation, search

With a little bit of trickey, it's possible to load, display and sumbit a Moodle Form (mform) defined using the [Forms API] entirely via AJAX, without having to reload the page.

To start with, you'll need a PHP script that looks much like a normal script used to display an mform, with a few modifications:

require_once('config.php');
require_once('form_class.php');
 
...
// Check permissions etc
...
// Get record for editing
$record = $DB->get_record('table', $params);
...
// Initialise the form class
$form = new form_class();
 
if ($record) {
    $form->set_data($record);
}
 
if ($data = $form->get_data()) {
  // Process the submission and save data to the database
}

The end of the script will look a bit different to normal. Rather than displaying a page containing the form, we just want to capture the HTML for the form

ob_start();
$form->display();
$formhtml = ob_get_clean();

Now there's a slightly trickier bit. The form API has built-in javascript validation, an in addition we might have our own javascript which needs to be initialised after the form is displayed. For this, we need to generate the javascript using the [Output API] and rip out the bits we need:

// First we get the script generated by the Forms API
if (strpos($formhtml, '</script>') !== false) {
    $outputparts = explode('</script>', $formhtml);
    $html = $outputparts[1];
    $script = str_replace('<script type="text/javascript">', '', $outputparts[0]);
} else {
    $html = $formhtml;
}
// Next we get the Javascript libraries included by YUI
$headcode = $PAGE->requires->get_head_code($PAGE, $OUTPUT);
$loadpos = strpos($headcode, 'M.yui.loader');
$cfgpos = strpos($headcode, 'M.cfg');
$script .= substr($headcode, $loadpos, $cfgpos-$loadpos);
// And finally the initalisation calls for those libraries
$endcode = $PAGE->requires->get_end_code();
$script .= preg_replace('/<\/?(script|link)[^>]*>/', '', $endcode);

We've now got the HTML of the form, and the Javascript that goes with it. We just need to send it back to the client.

echo json_encode(array('html' => $html, 'script' => $script));

That's the server side done for now. Let's look at the code for displaying and posting the form (using YUI3)

In out Javascript module, we'll have a function that looks something like this. It can be triggered on load, off a button press, or whatever:

display_form: function() {
    Y = this.Y;
    Y.io(M.cfg.wwwroot+'/form_ajax.php', {
       on : {
           success: function(id, o) {
               response = Y.JSON.parse(o.responseText)
               form = Y.Node.create(response.html); // Create a Node from the HTML
               Y.one('#form-container').setContent(form) // Display the form on the page before we do anything with javascript, since the javascript will expect the form elements to already be on the page
 
               scriptel = document.createElement('script'); // Create a <script> tag
               scriptel.textContent = response.script; // Put the Javascript inside the script tag
               document.body.appendChild(scriptel); // Add the script tag to the page.
           }
       } 
    });
}

Node that when you do document.body.appendChild(scriptel), the javascript will be executed immediately, meaning any libraries will be included, defined functions will be come available, and init functions will be run.