Search form

Drupal 7 - Ajaxify a form in a popup

src: http://www.jaypan.com/tutorial/drupal-7-ajaxify-form-popup
Step 1: Alias the Drupal core provided password reset form
Many times in Drupal 7, we want to re-use a form provided by core or by a contributed module, without affecting the original form itself. The password reset form exists in its natural state on the password reset page, located at /user/pass. In this tutorial, we will create our alias so that we can modify this form within our popup, while leaving it unaffected on the password reset page.

In order to achieve this goal, we need to create an 'alias' of the form. This means creating a new form ID that loads the password reset form. We can then make our alterations using the new form ID, which will then affect only our aliased form, and not the original.

To do this, in our module (pass_popup), we will implement hook_forms(). In this hook, we will tell Drupal that when we call a form with the aliased form ID pass_popup_user_pass, we want to show the user_pass form:

In the above code we have told the system that if we call drupal_get_form('pass_popup_user_pass') the system should show the form user_pass.
Step 2: Customize the form in our theme

The next thing we want to do is make some theme-specific alterations to the form. We will do this in our theme, pass_theme. In this case, we will remove the title of the field, and instead set it as the placeholder attribute of the field. This will put the text in the field itself as a default, and when the user starts typing, this default text will be removed. Note that this placeholder attribute only works in modern browsers, and will not work in IE9 or below.

In order to make this change, will will implement hook_form_FORM_ID_alter(), using our aliased form ID from step 1:

As we will be submitting the form within a popup, we don't want a page reload when we submit the form. Rather, we want to submit our form through AJAX, so that we can then hide the popup after the form has been submitted, keeping the user on the same page.

To do this, we will return to our module, and implement hook_form_FORM_ID_alter(), and ajaxify the submit button. We do this in the module, rather than the theme, as we will want the form to be ajaxified even if we switch themes in the future.

// First, we will create a unique ID that can be used as a wrapper for the form. We could hard-code an

// ID, however I prefer to use something that will be unique every time the form is called, on the off chance

// that we someday want to use the same form multiple times on a single page. The #build_id of each form

// is unique, and as such, we can use it to generate a unique form wrapper:

$unique_key='form-wrapper-'.$form['#build_id'];

// Now we can add a wrapper to our entire form, that will be used as our ajax wrappre:

$form['#prefix']='<div id="'.$unique_key.'">';

$form['#suffix']='</div>';

// And finally we will ajaxify our submit button:

$form['actions']['submit']['#ajax']=array

(

// We pass the ID of our form wrapper as the ajax wrapper

'wrapper'=>$unique_key,

// We will get to this callback in the last step of the tutorial

'callback'=>'popup_pass_user_pass_ajax_submit',

);

}

Now our form will be submitted through AJAX when the submit button is clicked, rather than forcing a page reload. Note that we will look at the ajax callback function 'popup_pass_user_pass_ajax_submit() later on in the tutorial.

Step 4: Create a block that contains this aliased form

The next thing we want to do is create a block that contains this form. We will then set this block to be hidden upon page load, and insert it somewhere into the page, showing it when the 'forgot password' link is clicked.

To create the block, we will return to our module, and implement hook_block_info() to define the block, and hook_block_view() to provide the content of the block:

// We use an arbitrary key here. This will be explained in the comments

// after the code

'custom_user_pass_block'=>array

(

// We use our aliased form ID to create a form, that become the contents

// of the block.

'form'=> drupal_get_form('pass_popup_user_pass'),

),

),

);

return$block;

}

}

You'll notice that an arbitrary key was used in the block content, as a wrapper to the form. For more information on this, see issue #2 of our tutorial Custom Drupal Blocks the Right Way. As we wrapped our form with a prefix and a suffix in the last step when we ajaxified the form, we need this prefix and suffix to wrap the form, and not the entire block. As such, this arbitrary key is very necessary.

Step 5: Insert the block into the page upon page load so it's available to be shown in a popup

The next thing we want to do is insert the block into the page, so it can be shown as a popup when the 'forgot password' link is clicked. However, we don't want to have the block visible upon page load, so we need to set the block to be hidden when the page is loaded. To accomplish this, we will do two things. First, we will add some CSS to the block that hides the block when it's inserted into the page. At the same time, we will also add the CSS that will center the block on the screen when the block is shown to the user. We put the following CSS into the css file user_pass_block.css (to be attached later)

Now all we need to do is go to the admin block interface at /admin/structure/block, and drop the block that we named 'Password Reset Block' into the content region of the page. It will be hidden upon page load, so having it in the content region won't cause any issues. You should probably also go into the block settings, and set the block to only be shown to anonymous users, and only on pages that have a 'forgot password' link. If you are using the User Login block, it will have this link, so you can set the Password Reset block to show on the same pages as the User Login block.

Step 6: Create JavaScript to show the form when the 'forgot password' link is clicked

In this step, we are going to ajaxify any link that points to the page 'user/pass'. This way, if our javascript fails, or the user has javascript disabled, they will be directed to the password reset page. When javascript is enabled however, our block will be shown instead. To do this, we will use jQuery to target any link that has the href user/pass. We will create the JavaScript file user_pass_block.js (to be attached later):

// We need to create a custom AJAX command to be used by our AJAX submit

// in order t hide our form after a successful submission. This will be triggered in

// step 8 of the tutorial.

Drupal.ajax.prototype.commands.passThemeHidePassPopup=function()

{

hidePassBlock();

};

}(jQuery, Drupal));

Step 7: Attach the JavaScript and CSS files to our block

In the previous two steps, we created the files user_pass_block.css and user_pass_block.js. We need to attach these to our block, so that they can be used. We will do this in our theme, as this functionality may change in the future if/when we change our theme. To do this, we will implement hook_block_view_MODULE_DELTA_alter():

// We only want to attach the files if the block has been rendered on the page. So we check if $data is equal to TRUE.

if($data)

{

// The block is to be shown on the page, so we will add our CSS and JS

// Get the path to our theme, as our CSS and JS file will be in the theme

$path= drupal_get_path('theme','pass_theme');

$data['content']['#attached']['css'][]=array

(

'type'=>'file',

'data'=>$path.'/user_pass_block.css',

);

$data['content']['#attached']['js'][]=array

(

'type'=>'file',

'data'=>$path.'/user_pass_block.js',

);

}

}

Now, our CSS and JS will be included on the page any time our block is included on the page. This will ensure that the block is hidden, and that it is shown when links that point to the password reset page are clicked.

Step 8: Hide the form after a successful submission

The last step is to create our ajax callback function, popup_pass_user_pass_ajax_submit(), that we added to the submit button of the form back in step 3. This will be done in the module, not the theme, as we will want this functionality to persist in the event of a theme change. For more information on what we are doing in this callback, see our tutorial Calling a function after an AJAX event in Drupal 7.