Thursday, July 12, 2007

ExpressionEngine Mailing list Signup - AJAX

A big part of any new social media site is the refined Web 2.0-y interface features enabled by AJAX technologies. As an ExpressionEngine guy I've been struggling to see how I can make EE functions work with AJAX. Well yesterday I finally hit some pay dirt.

Implementing an AJAX mailing list sign up for ExpressionEngine is, as it turns out, pretty simple, but you gotta hack a few things (at least with the solution I found). Here's what I did.

Using the default mailing list signup form with some added divs we get:

<h2>Join our default mailing list!</h2>
<div id="mailinglist">
{exp:mailinglist:form list="default"}

<p>Join our Mailing List</p>

<p><input type="text" name="email" value="{email}" /></p>

<p><input type="submit" value="submit" /></p>

{/exp:mailinglist:form}
</div>


Then we
1. run that through a template
2. copy and paste out the actual form code
3. throw in the XID global variable
4. grab an AJAX loader gif and upload that to your server, then put a bit of id'd code in for that in the div
5. paste in the submit button and replace the id names... I'll explain more in a second
6. add the following to your HEAD and, of course, get the prototype and scriptaculous code sets uploaded to your server and put in the /javascripts/ folder

<script src="/javascripts/prototype.js" type="text/javascript"></script>
<script src="/javascripts/scriptaculous.js" type="text/javascript"></script>


Here it is, the final form code for your sexy ajax body:

<div id="mailingList2">
<span id="indicator2" style="display: none"><img src="/images/ajax-loader.gif" alt="indicator icon" /></span>
<form id="mailinglist_form2">
<div>
<input type="hidden" name="XID" value="{XID_HASH}" />
<input type="hidden" name="ACT" value="3" />
<input type="hidden" name="RET" value="http://www.yoursite.com" />
<input type="hidden" name="list" value="beta" />
<input type="text" name="email" value="" class="text" /><br />
<input type="submit" value="Submit" onclick="new Ajax.Updater('mailingList2', '/index.php/', {onComplete:function(request, json){Element.hide('indicator2')}, onLoading:function(request, json){Element.show('indicator2'),Element.hide('mailinglist_form2')},parameters:Form.serialize(this.form),method:'post',asynchronous:true}); return false;" />
</div>
</form>
</div>

I'll explain a bit about the submit button, which kinda holds the key parts of this solution and on which I slaved away the most to understand (as a newbie to Prototype).
new Ajax.Updater(container,url,options)

a new Ajax.Updater is the Prototype class(?) that submits forms when you want to get some new HTML or code back from the server to replace it. I'm guessing this will be the most useful class for me. So you feed it the container div's id whose inner HTML you'll want replaced. In my case here I wanted to swap out the whole form for the page of results that EE provides back (more on this in a second). Next you put the URL on your server which will process the submitted form (the action). With EE it's going to be your site's index.php file. Lastly we get to the scary options...

In Prototype you tell it which optional features you want to make it do to get the right effects, decide whether it's POST or GET, etc. One by one:

1. The [options]. All your options are surrounded by {}s and each one is separated by commas therein

2. onLoading:function(request, json){Element.show('indicator2'),Element.hide('mailinglist_form2')}
As soon as the submit button fires and your data starts "loading" (clever eh?) it's going to show the indicator2 span which holds your ajax loader gif, then hide the form itself (it's not needed anymore right?).

3. onComplete:function(request, json){Element.hide('indicator2')}
When the returned data is done loading, we're going to hide the indicator2. Don't worry about the (request,json) parts of these, it doesn't seem to matter in this case. All of my functions have it and it works. I'm sure we can learn more about this part later. Since we already told it which div to put the returned data in, we can be assured that this will be hidden anyway (actually replaced). Perhaps this step is redundant. I'll find out later. Go experiment yourself too.

4. parameters:Form.serialize(this.form),method:'post',asynchronous:true}); return false;
This is the good stuff! The parameters are all the variables and information you're going to send to your processing page. With Prototyhttp://www.blogger.com/img/gl.link.gifpe it does this great Form.serialize() function which grabs all relevant input fields and their values from your form automatically! I put in this.form but you could put in the id of the form too (use your single quotes of course). Specify which method, either POST or GET next, with EE's mailing list you need to use 'post' because you're returning some data. I think all EE forms use POST but I could be very wrong about that. Lastly you want to keep the asynchronous-ness going on, making it true, otherwise you'd lock up the browser while it's loading your results.

And that's it! Oh wait, no it's not! Big next step: go into your EE's Admin > Specialty Templates > User Message Template and edit that (I just took out the CSS). Yes, this is going to be a problem, you'll also see that the RET value of the form gets a link at the bottom of this page's content. One of my next steps is to figure out what to do about this problem.

One solutions I just thought of while writing this is to not actually return any data from EE, in which case I believe you'd use new Ajax.Request instead of the Updater, then you can figure out some other content to show, like "Thank you!" using more Javascript. Feel free to suggest away here...

No comments: