Changes between Initial Version and Version 1 of AjaxPrototypeNewForms


Ignore:
Timestamp:
Feb 25, 2007, 8:32:29 AM (17 years ago)
Author:
Georgi Stanojevski <glisha gmail com>
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AjaxPrototypeNewForms

    v1 v1  
     1= Ajax submiting and processing form errors with newforms and prototype =
     2
     3
     4== The task == 
     5{{{
     6#!html
     7<ul>
     8<li> Show a form;</li>
     9<li> Submit the form and show validation errors;</li>
     10<li> Handle both js enabled and js disabled (ajax and standard form submiting).</li>
     11</ul>
     12}}}
     13
     14I want to submit only the form and get validation errors from django newforms without submiting the whole page. Django Newforms library is really exciting and easy to use. Django is also great that doesn't restrict me to use any specific ajax/js library so I can use whatever I want in this case ''prototype''.
     15
     16So I'm using
     17{{{
     18#!html
     19<ul>
     20<li> django.newforms </li>
     21<li> django.utils.simplejson which converts python data into JSON data so I can exchange it with the browser. </li>
     22<li> And I'm using the great <a class="ext-link" href="http://www.prototypejs.org"><span class="icon">prototype.js</span></a> library that makes javascript and ajax quite easy.</li>
     23</ul>
     24}}}
     25
     26
     27== The Form ==
     28
     29The form is a simple contact form with all four fields required. Just to have something to validate. :)
     30
     31
     32{{{
     33#!python
     34from django import newforms as forms
     35from django.newforms.widgets import *
     36
     37# A simple contact form with four fields.
     38class ContactForm(forms.Form):
     39    name = forms.CharField()
     40    email = forms.EmailField()
     41    topic = forms.CharField()
     42    message = forms.CharField(widget=Textarea())
     43}}}
     44
     45
     46== The View ==
     47
     48The view returns either JSON with the errors from the form or a whole page if javascript is disabled in the browser.
     49
     50I have a hidden input element in the form. I set it to None just before sending the form with javascript so the view knows what should it return, the whole page or just the errors with JSON.
     51
     52The rest is just a generic newforms code. I check if the form is valid and redirect the user if it is or return the errors if it's not. When the form is valid and using AJAX I use a special field {'OK': 'java script for the browser to execute'}. The client-side javascript "knows" that it should run the javascript when it encounters 'OK'.
     53
     54
     55{{{
     56#!python
     57def contactview(request):
     58    if request.method == 'POST':
     59        theform = ContactForm(request.POST)
     60
     61        # if "js" got here together with the form it means that the
     62        # browser doesn't have javascript so handle the form without js
     63        js = request.POST.get('js', None)
     64
     65        if theform.is_valid():
     66            if not js:
     67                # simplejson makes a json format so we can do an
     68                # eval() and run the javascript the view returns
     69                jsaction = 'window.location.pathname="/"'
     70                data = simplejson.dumps({'OK':jsaction})
     71            else:
     72                return HttpResponseRedirect('/')
     73        else:
     74            if not js:
     75                data = simplejson.dumps(theform.errors)
     76            else:
     77                return render_to_response('contactform.html', {'form': theform})
     78        return HttpResponse(data, mimetype="text/javascript")
     79    else:
     80        # show a blank form
     81        return render_to_response('contactform.html', {'form': ContactForm()})
     82}}}
     83
     84
     85== The Javascript ==
     86
     87
     88{{{
     89#!text/html
     90<script type="text/javascript">
     91// when the page get's loaded call the init function to hijack all the form
     92Event.observe(window, 'load', init, false);
     93
     94function init(){
     95    // hijack the form submit
     96    Event.observe($('theform'), 'submit', processform, false);
     97}
     98
     99function processform(e) {
     100
     101    // get the form that got submited ( the first parrent element of the element that trigger the event)
     102    var aform = Event.findElement(e, 'form');
     103    // where to submit the form
     104    var ajaxurl = aform.action;
     105
     106    // get the submit button of the form and show an ajax-loader instead
     107    var submitbutton = aform.getInputs('submit')[0];
     108    new Insertion.Before(submitbutton, '<img src="/site_media/ajax-loader.gif" id="ajaxloader" />');
     109    submitbutton.hide()
     110
     111    // make the js field empty so that django knows how to handle the request
     112    // if js is submited with the form django handles it as a normal form
     113    // if js is None django returns AJAX variables
     114    document.getElementById('js').value = ''
     115
     116        var myAjax = new Ajax.Request(
     117        ajaxurl,
     118        { method: 'post', parameters: aform.serialize(true), onComplete: handlereq });
     119
     120    function handlereq(req) {
     121        // evaulate the response (get the errors or run the javascript)
     122        var errors = eval('(' + req.responseText + ')');
     123
     124        // clean the errors from the previous run (elements with class="errorlist"
     125        var old = aform.getElementsByClassName('errorlist');
     126        for (i=0; i<old.length; i++) { old[i].remove(); }
     127
     128        // if there is an OK attribute in the response, run the javascript
     129        // if not show the errors in the for_fieldname element
     130        if ( errors.OK ){
     131            eval(erros.OK)
     132        } else {
     133            $H(errors).each(function(pair) {
     134                $('for_'+pair.key).innerHTML = '<ul class="errorlist"><li>'+pair.value+'</li></ul>';
     135            });
     136
     137        $('ajaxloader').remove() // remove the ajax-loader
     138        submitbutton.show() // return the submit button
     139        }
     140    }
     141
     142    // don't trigger the actual html submit form
     143    Event.stop(e);
     144}
     145</script>
     146}}}
     147
     148
     149== The Template ==
     150
     151The template for the form has a span element with id="for_fieldname" where the javascript puts the errors and {{ forms.fieldname.errors }} where django puts the errors if javascript is disabled.
     152
     153
     154{{{
     155#!text/html
     156<html>
     157<head>
     158<title>Contact form</title>
     159<meta http-equiv="content-type" content="text/html; charset=utf-8">
     160<script type="text/javascript" src="/site_media/prototype.js"></script>
     161</head>
     162<body>
     163<form method="post" action="." id="theform" name="theform">
     164    <fieldset>
     165    <legend>Contact form</legend>
     166
     167    <label for="id_name">Your name:</label>
     168   {# in the for_fieldname element I put the errors with js #}
     169    <span id="for_name"></span>
     170   {# this are the errors when javascript is disabled #}
     171    {{ form.errors.name }}
     172    <br />
     173   {{ form.name }}<br /><br />
     174    <label for="id_email">Your email:</label>
     175    <span id="for_email"></span>
     176    {{ form.errors.email }}
     177    <br />
     178   {{ form.email }}<br /><br />
     179
     180    <label for="id_topic">Topic:</label>
     181    <span id="for_topic"></span>
     182    {{ form.errors.topic }}
     183    <br />
     184   {{ form.topic }}<br /><br />
     185
     186    <label for="id_message">Message:</label>
     187    <span id="for_message"></span>
     188    {{ form.errors.message }}
     189    <br />
     190   {{ form.message }}<br /><br />
     191
     192    <br />
     193    <input type="hidden" name="js" id="js" value="disablewithjs">
     194    <input type="submit" id="submitbutton" value="Send">
     195    </fieldset>
     196</form>
     197</body>
     198</head>
     199
     200}}}
Back to Top