Handling form submit event

Handling form submit event

by Kevin Burton -
Number of replies: 15

I have a form that is built and defined using the Moodle Form API methods. At the end I include the method to add the 'action' buttons:

    $this->add_action_buttons(....);

Using the a debugger I find that this puts a "submit" type input button of the id 'id_submitbutton'.I would like to have 'submit' do all the normal things that it does but I also want to be notified that a submit event has occured. So in my YUI sandbox I put:

    Y.on("submit", function(e) {

        console.log('submit');
    }, '#id_submitbutton');

The problem is that this event never seems to occur. I know that sandbox is set up correctly because there are other elements (mainly action <a> items) on the page that responde to 'click' events with no problem. My theory right now is that Moodle handle the form submit and prevents the event from bubbling anywhere else. I have tried changing the registration to 'click' but it also doesn't seem to occur. Any idea on how I can get notified on a Moodle Form submit in the YUI sandbox? Thank you.

 

Average of ratings: -
In reply to Kevin Burton

Re: Handling form submit event

by Kevin Burton -

Any idea on how I can catch a form submit event. It seems like Moodle is grabing the event and not allowing it to bubble up to my handler. Ideas?

In reply to Kevin Burton

Re: Handling form submit event

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

I'm just about to head out the door, but take a look at the formchangechecker in lib/yui/src/formchangechecker/js/formchangechecker.js

You need to listen on the form, not the submit button. The button is clicked, the form is submitted.

AFAIK, we don't stop the event from bubbling on the forms - I can't recall whether stopImmediatePropagation also calls preventDefault() or not so this may not even be possible.

Andrew

In reply to Andrew Lyons

Re: Handling form submit event

by Kevin Burton -

Thank you for the tip. Unfortunately I have very little experience with YUI and Moodle. Is there a simple example of how I can use this code? I understand enough to put $PAGE->requires->js() to run JavaScript code after the page loads but aside from that I am out of my league. I am also a little unclear what the global 'M' variable represents.

Ideally I would like to provide status for a long running PHP process. When the form is submitted the user has supplied parameters governing a web service call that is carried out with some PHP code. As it is now the user submits the form and waits for the results to come back from one or more web service calls and get formatted and output to the page. This is all synchronous and as you can imagine sometimes slow. As the progress indication would need to be asychronous and probably JavaScript, I thought the first place to start would be when the form was submitted and the process begun.

Thanks again.

In reply to Kevin Burton

Re: Handling form submit event

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi Kevin,

So it looks like you're trying to do quite a bit in one go.

To start with, the 'M' is a global variable which in which we namespace a variety of things including configuration, and code. You'll find things like M.util.get_string(), M.cfg.sesskey, and M.core.dialogue in there.

There are a couple of ways of including Javascript in Moodle - the preferred way is to use the yui_module() function and to write your code as a YUI JS module - doing so means that you get dependency management, minification, linting of your code, and a few more bits besides. Writing a YUI module is documented in http://docs.moodle.org/dev/YUI/Modules but I realise that it's a non-trivial task. You may want to start by writing a $PAGE->requires->js_init_call() instead and convert once you get your head around the YUI side a bit more.

On to your actual issue... once the form is actually submitted, the page will redirect and start to load the new content.

You'll have to catch the submit, prevent the default action, and instead submit the form using Y.io.

At that time, you'll want to start off something (a timer, or a queue) to keep polling your server for a status. This is where you may potentially get into locking issues - you need to write the script that is polled (php side I mean) in such a way that it only outputs status information and only takes something like an ID. We do this for the editpdf plugin - you can see the script which is polled at mod/assign/feedback/editpdf/ajax_progress.php.

Once it returns, you'll need to stop any active timers/queues, and then redirect.

Andrew

Average of ratings: Useful (1)
In reply to Andrew Lyons

Re: Handling form submit event

by Kevin Burton -

I was reading 'Moodle JavaSciprt Cookbook' and maybe it started me off on the wrong path. Basically it suggested using something like:

$PAGE->requires->js(<js code>, true);

This as I read the book runs the <js code> after the page has loaded. In order to hook into YUI it suggests something like:

YUI.use(...., function(Y) {

. . . . .

});

 

I have been following this pattern but the pattern that you suggest I can see seems to be recommended by the online documentation but I have been unable to convert to using the module approach. Any suggestions? Also since I more or less have an understanding of the YUI.use pattern can I still use some of the Moodle JavaScript methods/classes like the one you suggested for handling a form submit event? If so how? Thank you for you help.

 

In reply to Kevin Burton

Re: Handling form submit event

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

The book is serveral Moodle releases out-of-date. Andrew has been working to make things better. (Or, at least more complicated wink. Actually, it is better, but you have to understand it. At least Andrew has written reasonably good documentation.)

In reply to Tim Hunt

Re: Handling form submit event

by Kevin Burton -

Would you be able to direct me to where documentation is better?

In reply to Kevin Burton

Re: Handling form submit event

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

See the link Andrew gave above.

In reply to Tim Hunt

Re: Handling form submit event

by Kevin Burton -

I tried that. 

I have PHP code on my page that looks like:

$PAGE->requires->yui_module('moodle-mod_mediasite-search', 'M.mod_mediasite.search.init');

Thie JavaScript then looks like:

 

YUI.add('moodle-mod_mediasite-search', function(Y) {
M.mod_mediasite = M.mod_mediasite || {};
M.mod_mediasite.search = {
baseurl: null,
site: null,
resourceId: null,
tooltip: new Y.Overlay({width: 200, visible: false}),
searchprogress: new Y.Overlay({width: 200, visible: false}),
init: function() {

But when the page executes I get:

Uncaught TypeError: Cannot read property 'search' of undefined

Also there doesn't seem to be any mention on this page on using the function to handle a form submission that is part of the Moodle YUI library. Ideas?

 

In reply to Kevin Burton

Re: Handling form submit event

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi Kevin,

That generally happens because you've got some kind of issue in your code somewhere.

I've just knocked up a super quick example for you (attached).

If you unzip this folder into your mod_mediasite plugin, and add a call somewhere in your PHP to the following, then this should give you a working example to play with:

$PAGE->requires->yui_module('moodle-mod_mediasite-search', 'M.mod_mediasite.search.init', array(array('formid' => 'someFormId')));

For reference, your directory structure will look something like the following:

3554 /private/srv/www/loganberry.local/public_html/sm/mod:master> tree mediasite/
mediasite/
├── lang
│   └── en
│       └── mediasite.php
├── lib.php
├── mod_form.php
├── version.php
├── view.php
└── yui
    └── search
        └── search.js

4 directories, 6 files

Hope that this helps,

Andrew

Average of ratings: Useful (1)
In reply to Andrew Lyons

Re: Handling form submit event

by Kevin Burton -

Thank you very much. It seems much simpler when you lay it out like that. Very much appreciated.

Not to be ungrateful but would you mind showing me how I would use Y.io to post back? I want all of the global variables properly filled in so it appears as a "normal" postback.

Thanks again.

In reply to Andrew Lyons

Re: Handling form submit event

by Kevin Burton -

Progress I now am getting to the 'init' function. But for some reason my handler for the submit event doesn't seem to be called. I have:

M.mod_mediasite.search = {
. . . . .
form: null,
eventHandlers: [],

. . . . . .


init: function(formid) {
this.form = Y.one('#' + formid);
if(!this.form) {
console.log('The form identified by ' + formid + ' could not be found.');
}
this.eventHandlers.push(
this.form.on('submit', this.handleSubmission, this)
);

And the actual handler right now simply notes that the submit has happened:

handleSubmission: function(e) {
console.log('submitted');
},

 

I looked on the console.log (using Chrome) I didn't see this message. Ideas?

In reply to Kevin Burton

Re: Handling form submit event

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Apologies for the delays in my replies - I've had a busy couple of days.

Looking at this code, I'm not sure why your submission event isn't being called at present. It looks correct to me at a glance.

I assume you've tried stepping through with the JS debugger to make sure that the event is actually added. If you add a breakpoint on the eventHandlers.push and walk over, you can be sure. You can also run the following in your JS console to see those handlers (you'll need to be paused in the correct context in order to do so):

this.eventHandlers

My first guess though would be that you're not providing the correct formid to the init function. If you add a breakpoint at the this.form line, and then check what formid you're getting I think you'll find that formid is an object containing a formid. Normally we call this params, or args, or something. Personally, I quite like using the Y.Base class which includes a feature called Y.Attribute, but we'll leave that for another time for the moment as it complicates things somewhat.

So perhaps, try changing your code to:

init: function(args) {
    console.log(args);
    this.form = Y.one('#' + args.formid);
    if (!this.form) {
        Y.log('The form identified by ' + args.formid + ' could not be found. Bailing', 'warn', 'moodle-mod_mediasite-search');
        return;
    }

    // Add the submission events:
    this.eventHandlers.push(
        this.form.on('submit', this.handleSubmission, this);
    );
    
},

handleSubmission: function(e) {
    Y.log('Handling a submission event', 'debug', 'moodle-mod_mediasite-search');

// We must prevent the default browser action, otherwise the browser will redirect.
e.preventDefault(); }

You'll notice that I've used Y.log in a few places here. Y.log is filtered by YUI such that messages are not displayed when you do not have DEBUG_DEVELOPER set as your Moodle DEBUG setting. You may also know this setting as:

$CFG->debug = (E_ALL | E_STRICT);

The second argument to Y.log is the log category/level - choices are debug, info, warn, and error. The third argument is the source - this should be the module name. I'm writing some changes to make debugging easier at the moment which will utilise these more. It will mean that when your'e debugging your own module, you only get the messages you care about which should make life much easier.

You'll also notice that I've called preventDefault() on the event in handleSubmission. Without this, the page will submit anyway. See https://developer.mozilla.org/en-US/docs/Web/Reference/Events/submit for more information on the submit event.

Regarding your other question on how to use Y.io to submit the form, I've not tried this myself, but there is documentation on the YUI docs page. See http://yuilibrary.com/yui/docs/io/#serializing-html-form-as-data for more information. You're particularly interested in the form object being passed in the configuration. This is documented at http://yuilibrary.com/yui/docs/api/classes/IO.html#method_send.

You'll also want to take a peek at how we handle errors returned by IO requests. Here's an example of how we do so in Moodle in other places: https://github.com/moodle/moodle/blob/master/lib/yui/src/tooltip/js/tooltip.js#L372.

In order for the JS to realistically parse the content, you need to return a JSON object structure. This is easily achieved with a combination of adding the following to your AJAX script (the PHP side) before you require the Moodle configuration:

define('AJAX_SCRIPT', true);

And when you output content, add your return data to an Array or stdClass and return it with

echo json_encode($return);

Again, you can see an example of a basic Moodle AJAX script at https://github.com/moodle/moodle/blob/master/lib/help_ajax.php.

By doing this, you'll see that any Exceptions thrown by Moodle are correctly encoded and displayed in a dialogue to the user. Additionally, if the return value is not JSON, a message is displayed to the user.

Hope that this helps,

Andrew

In reply to Andrew Lyons

Re: Handling form submit event

by Kevin Burton -

Thank you for your continued help. I have been using console.log instead of Y.log because I wasn't sure what the best way of showing the log output. I know about 'render()' but it seemed more straightforward to just CTRL-SHIFT-J to see the log output. Maybe you have a better solution.

Anyway the results are the same I see the log entry for when I register the submit event but I do not see the log entry when I enter the handler. There was one important (now it seems important) I don't see the log entry when I don't have the call to preventDefault(). When I put that call in I see the log entry. I guess there isn't a way for me to just be a "fly on the wall" and everything proceeds as normally with me just listening?

In reply to Andrew Lyons

Re: Handling form submit event

by Kevin Burton -

Thank you. This is good information.

Would it be possible to setup a custom event and then use something like Y.on to listen for the event? The problem that I see is coordinating the custom event on the server and the listener on the client.