Refactored form submit with Dojo and newforms
Synopsis
Here's a simple recipe for rolling your own server-validated form widget with Dojo.
Introduction
Dojo and Django are two emerging toolkits gaining in popularity theses days.
The following article describes a step-by-step recipe inspired from AjaxDojoFormSub and from this tutorial for validating a form with Dojo and the Django newforms library.
My initial objective was to display validation errors without doing a page-refresh, while using JSON (python-cjson) for carrying errors (or whatever else you could possibly imagine) back to the client browser.
The server view
First thing first, lets write the server view, as its really important to make the forms working without the use of Javascript:
import cjson from django.http import HttpResponse from django.template import loader, Context def register(request): # Register or reports any (new)form-validation errors using JSON. form = ExampleForm(request.POST) t = loader.get_template('myform.html') if not form.is_valid(): # Return JSON object containing the errors object. if xhr: return HttpResponse(cjson.encode(form.errors), mimetype='text/javascript') else: # No JSON here! return HttpResponse(t.render(Context(form.errors)) else: # Do something when the form has been validated. ...
The client view
Once the server view has been written, the idea is to write down the client UI (or view, depending whether you're a purist or not.. ;-)
Quite frankly, that step is straight-forward, and looks pretty-much like writing down basic HTML:
<script type="text/javascript"> djConfig = { isDebug: true, debugAtAllCosts: false }; </script> <script type="text/javascript" src="/media/js/dojo/dojo.js"></script> <script type="text/javascript"> dojo.require("dojo.event.*"); // sophisticated AOP event handling dojo.require("dojo.io.*"); // for Ajax requests dojo.require("dojo.json"); // serialization to JSON </script> <!-- Here's the client UI script to play with Dojo --> <script type="text/javascript" src="/media/js/ajax.js"></script>
The djConfig.isDebug
parameter is important for debugging, so you should
consider setting it to "true" when inspecting you're application with Firebug
or something else.
To avoid loading the Dojo modules twice (for speed), just roll your own Dojo script, and then remove the implied ("baked") modules from the list of required modules above.
Form validation with dojo.io.bind
Let's complete our application by writing down the core JS file, for doing some-cute-AJAX-related-things-with-Dojo (tm):
ajax.js:
function myinit() { // connect the event with the good handler var myButton = dojo.byId("xBtn1") dojo.event.connect(myButton, "onclick", "sendForm") } function sendForm() { var fNode = dojo.byId('xForm') var fParms = dojo.io.encodeForm(fNode) var bindArgs = { url: ".", mimetype: 'text/plain', method: "post", // useCache: true, // sync: true, transport: "XMLHTTPTransport", postContent: fParms, error: function(type, error, http){ // Handle error here alert(error.message) if (http.responseText) // Return the raw server response // (useful when debugging) document.write(http.responseText) return false }, load: function(type, data, http, kwArgs) { /* * Handle "successful" responses here **/ var errStr = '' var errDiv = dojo.byId('errBox1') if(type == 'load'){ try{ var json = eval( '(' + data + ')' ) }catch (e){ // not json // alert(e) if (!http.responseText){ document.write(data) } else { document.write(http.responseText) } return false } var listElement = document.createElement('ul') errDiv.innerHTML = 'Testing..' if (json.error != null){ errDiv.innerHTML = json.error }else if(json.errors != null){ for (x in json.errors){ myLi = document.createElement('li') myLi.innerHTML = x + ': ' + json.errors[x].toString() listElement.appendChild(myLi) } errDiv.appendChild(listElement) } }} }; var xmlhttp = dojo.io.bind(bindArgs) return xmlhttp }; dojo.addOnLoad(myinit);
Notice the absence of the formNode
param for postContent
, and the presence of dojo.io.encodeForm
to parse/encode the form data into a proper format. That is more than sufficient for validating the
form with a XMLHTTPTransport
. However, this approach doesn't work per se with file-uploads. To have file-uploads,
you could however change the transport
line to IframeTransport
. Here's a link to the IframeTransport api.
Have fun! That recipe should work in Firefox 2 and Opera 9.10.
Limitations/Bugs
- Usability issues: HTML 4.01 vs XHTML 1.0 strict vs No javascript
- XMLHTTPTransport doesnt support file-uploading.
- DOM manipulations wont update the raw HTML source, however the correct aspect is preserved.
Notes/Comments/Suggestions
- This is based off Dojo 0.4.x code & Dojo has undergone some major changes (up to v1.1 now). While it makes everything faster, simpler, and easier, the code above won't work. :( I'll get onto rewriting it shortly. - rcoup :)
- I'd surely be updating formio.js with your patch :) Thanks for your input. --erob
References
- python-cjson
- The newforms library
- Dojo, the Javascript Toolkit
- Dojo API Reference
- HTML 4.01 Specification
- Core Javascript 1.5 Reference (mozilla)
- ECMAScript support in Opera 9
Author
Etienne Robillard <robillard.etienne (at) gmail.com>
Attachments (2)
-
ajax.js
(2.1 KB
) - added by 18 years ago.
script for handling form validation errors w/ dojo
-
ajax.2.js
(2.1 KB
) - added by 18 years ago.
script for handling form validation errors w/ dojo
Download all attachments as: .zip