The Key to Jquery Form Plugin + Drupal Formapi

08.28.2007

Today I bring you an incomplete, yet stunningly easy solution to a problem that's been making want to set buildings on fire.

This problem was making drupal comments go AJAX by way of jquery's form plugin. I had all the good makings of a digg clone comment system, with reply forms dynamically inserting under replies (instead of going to another page). It was just the sans-pagerefresh comment submit thing that was missing.

Tragically, breaking the submit (necessary for ajax) also broke the ability for formapi to do its magic. Then, MerlinofChaos pointed out the 'obvious'approach on IRC.

Use drupal_execute to emulate form submission at a custom callback url. This approach can be applied to any form, so far as I can imagine. So I'm sharing it before I complete the much needed, and long desired ajax_comments.module (yes, it is really coming... solving this problem means I jumped over the last hurdle).

Here's the stripped down, proof of concept code: AJAX_COMMENTS.MODULE

<?php

define('AJAX_COMMENTS_PATH', drupal_get_path('module', 'ajax_comments'));

function ajax_comments_menu($may_cache) {
 
$items = array();
 
$items[] = array(
   
'title' => 'AJAX callback',
   
'callback' => 'ajax_comments_post',
   
'path' => 'ajax_comments_post',
   
'access' => user_access('post comments'
  );
  return
$items;
}

function ajax_comments_post() {
 
// we submit our form to a special path to account for comments expecting a nid, and pid.....
 
$args = array('nid' => arg(1), 'pid' => arg(2));
 
// in that order 1. form_id, 2. $_POST, 3. (etc) additional arguments that the form would expect
 
drupal_execute('comment_form',$_POST, $args);
 
// over here you would fire a "response" function that would actually return html, that you would then insert into the page via jquery. Obviously, we aren't going there in this code snippet.
}

/* Now I fully admit THIS code is untested... its just a quick simplification of the code I have behind ajax_comments module at the moment
For this code, the only purpose is to break the default submit, and allow a comment to run through FAPI... everything else is for the released module that's coming.
*/
function ajax_comments_form_alter($form_id, &$form) {
  if (
$form_id == 'comment_form') {
   
//include the necessary jquery plugin
   
drupal_add_js(AJAX_COMMENTS_PATH.'/jquery.form.js', 'module');
   
// this is a variable, thank god you have me commenting it :-P
   
$nid = arg(1):
   
drupal_add_js("
      $(document).ready( function() {
      var options = { 
        // this is the url that is define in hook menu as the callback
        url: '/ajax_comments_post/'.$nid,
        type:    'POST',
        clearForm: true ,
        resetForm: true,
      };
      // using variable to show that this is a very general approach
      $('$form_id').submit(function() {
        $(this).ajaxSubmit(options);
          // RIGHT HERE BREAKS SUBMIT
          return false;
        });
      });
      "
, 'inline');
  }
}
?>

In conclusion, this code pretends to do nothing besides break a form submit, and allow formapi to nevertheless go about its business doing validate/submit/insert DONE! Its not a complete solution, just the solution to the one remaining bigticket problem that I had building an ajax comments module modeled after the DIGG comment system. Its meant to be useful to people who already are pretty deep into this stuff, and not people who are learning (albeit, I am a javascript woodpusher, and am just pawing around with the JS code, much like a cat pawing its way out of a cage.) Anyways, feedback on what's wrong with the approach is much appreciated, since I am, indeed, a child who shows "great creativity" when it comes to javascript. Cheers.

Comments

buy wow gold cheap wow power

nice

nice

Nice post

Nice post

very nice post thanks

very nice post thanks ;)

Propecia online

have you been able to make

have you been able to make any progress on this?

____________________
muscle supplements

Same Technique for Login?

I am trying to use the Thickbox module for login but it breaks usability-wise, in my opinion, when authentication fails and it redirects to the regular user/login page. Could this technique be used to keep login within the modal Thickbox window until authentication succeeds, never needing to go to the regular user/login form?

Any pointers?

Thanks!
-Patrick

Oye... I'd probably use some

Oye... I'd probably use some sort of client side validation of the password and username being correct, and hope that the thickbox doesn't disappear when a form submission doesn't return false. Hope that makes sense :-/

have you been able to make

have you been able to make any progress on this?

this works.

Not sure if you ever got this all working - but this works. Part of the issue may be that (for comment form at least)

$form_id = "comment_form" where as what you want to pass is actually $form['#id'] which is "comment-form". Silly that these wouldn't be the same thing. Also, you need to to actually pass "#comment-form"; hence the concat that is there.

Also, no idea what the dmitrig01 is doing with the setting code - almost sounded like this would add the config settings for this magically to some admin page... but no idea ???

            drupal_add_js("
              $(document).ready( function() {
              var options = {
                // this is the url that is define in hook menu as the callback
                url: '/liquid_post/$nid',
                type:    'POST',          
                clearForm: true,         
                resetForm: true          
              };
              // using variable to show that this is a very general approach
              $('#".$form['#id']."').submit(function() {
                  $(this).ajaxSubmit(options);
                  return false;  }); });", 'inline');

note: my module is named "liquid" so change accordingly.

Now, this is only doing the comment post - ideally, at least for my app, it would be nice if you could see that post after making it (at the moment the ajax doesnt return anything). If i do a refesh i see my new posts; but ideally i think the ajax handler need s to call the node and pass that back as html to replace into the page.

As written and depending how it is addded to some form_alter hook code this will ajax-ify all forms - but you don't always want the result of the post to be what i want for the comment form - so not quite sure how to handle that.. to make this a real module i think possibly an admin page for where you want specific form redirects to go to - and default to just redisplaying your current page (which is good for a lot of places: comments, login, send to friend, cck submitts, etc

That sounds brilliant!

I can think of half a dozen applications for these kinds of forms beyond commenting - Drupal should extend its jQuery library with a generic function for submitting forms programmatically, client-side...

(I'm on a two-week absence in a grueling statistics course right now, but I'll want to test it and perhaps try to understand it as soon as I get back.)

Well, where I'm at is a core

Well, where I'm at is a core hack that lets me actually control how comments, and forms are rendered.... regardless, I have the ajax part, and the threaded part working in Safari 3, Firefox, IE6/7... BUT as soon as you submit a comment, the threaded feature breaks... ::nick cries like a big baby::

I think I should let the code out in the world, so someone can sensibly say "gee... you're stupid.... this is how its done... see, wasn't that easy?"

p.s. if you spy on how digg did it, it doesn't look like it...

Nice

Can't wait for this to be released as a complete module.

This building it... made a

This building it... made a huge error in the inital versions method of handling drop down forms for comment replies vs. comments about nodes. There's a very particular order, and indentifying markup that I totally f#cked up (but its my first real jquery project... so these things happen).

Stay tuned. I think I'll even be able to make it work without template overrides or core hacks.... (not as easy as you might think).

For any one thinking of

For any one thinking of using this code for a purpose other than submitting the comments form bear in mind that if your form function is: my_exciting_form()
it will be displayed by drupal after rendering with an id of "my-exciting-form".

So you need to target #my-exciting-form
not #my_exciting_form (which is what "#$form_id" returns)

Removing the register and new password tabs?

I am thrilled to be able to hijack yet another page of my site! To make this complete, can you suggest a way to remove the navigation tabs for new account, login and new password? I'd like to include those links in my newly styled page rather than using the standarnd drupal method.

I can hide by setting the CSS display attribute for .tabs, but I'm wondering if there's a better way.

...

drupal_add_js("
...
url: '/ajax_comments_post/'.$nid,

This code looks suspicious. It's a JS code, yet you use the PHP string concatenation operator (the dot). Eliminating such bugs is one more reason for using drupal_add_js(..., 'settings'), as dmitrig01 suggested. Alternatively, you can call drupal_to_js() directly.

(Oh, thanks for not forcing me to type a bogus email when replying here...)

Excited

3 Words. Yes! Yes! Yes!

This has been left out of Drupal for way to long! Can't wait! ETA?

I've been thrown off track

I've been thrown off track by a "special forces" mission at my company. It was supposed to make it into next weeks release, but that's been postponed. More or less here's the status:
1. code is getting cleaner more consistant.
2. There's a few major bugs, the comments work on perfectly on replies, but on replies to the node itself, the system is broekn. The other bug is that the comment module's form, validtation, submission, and comment listing rendering, excuse my irreverence, sucks -- but works. There's a lot of processing that happens outside of the formapi, but instead in this horrid comment_reply function that is so 4.6 it makes me want to cry. At our company, we simplely hacked node.module's node_show function, and set the reply form not to show on the same page (so that we could decide its location ourselves), I have yet to dive into making it work in the non-hacked core codebase which is so stubborn about the markup, and structure of comments. WTF can't comment_render be THEME_comment_render... I remember there was a patch in drupal 6 to solve these issues, but it got shot down (I think) for one reaso or another... my guess is that function comment_reply was preventing any sensible improvements to the comment module....

uggg....

I should probably avoid drinking whiskey when criticizing core modules.

Excited

3 Words. Yes! Yes! Yes!

This has been left out of Drupal for way to long! Can't wait! ETA?

Awesome

Been wanting something like this for quite a while, but it's way over my programing abilities. Happy to provide testing/documentation whenever you've got something you're satisfied with.

Correction

Great work!
I found two small things. First, $('$form_id').submit(function() { should be $('#form_id').submit(function() {.
Second, IMO the options should be done differently. Why not harness Drupal's Drupal.settings?
It'd looks like this if you did:

<?php
drupal_add_js
(array("ajax_comments" => array("url" => "/ajax_comments_post/". $nid, "type" => "POST", "clearForm" => true, "resetForm" => true)), 'setting');
   
drupal_add_js("
      $(document).ready( function() {
      $('$form_id').submit(function() {
        $(this).ajaxSubmit(Drupal.settings.ajax_comments);
          return false;
        });
      });"
, 'inline');
?>

When you add JS with 'setting' as the second param, it goes into Drupal.settings

Didn't know about

Didn't know about DRupal.settings! Thanks for the corrections, I'll give drupal settings a try.

Making a version that works

Making a version that works without clean URLs or inside a base directory is left as an exercise to the reader?

Well, I haven't thought of

Well, I haven't thought of those situations... but I imagine that replacing:
"/ajax_comments..."

With

base_url().'ajax_comments/...'

should work.

just use

just use /index.php?q=ajax_comments and it will work with either clean urls or not.

...

But 'index.php' may not be in the root. better to do url("ajax_comment/$nid").

(Hmm... why doens't my inline <code> stand out from the text?)

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • Lines and paragraphs break automatically.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.