| 128 |  |     if (type == 'error') | 
          
            | 129 |  |         alert('Error when retrieving data from the server!'); | 
          
            | 130 |  |     else | 
          
            | 131 |  |         // de code the simpleJson answer from django ''details'' method. | 
          
            | 132 |  |         // it populates a Js array ! straigth so cool ! | 
          
            | 133 |  |         arrayData = dojo.json.evalJson(data); | 
          
            | 134 |  |         // now we update the html using the css id as a pointer to the part  | 
          
            | 135 |  |         // we want to update | 
          
            | 136 |  |         dojo.byId("mark_total").innerHTML = arrayData[1]; | 
          
            | 137 |  |         dojo.byId("mark_message").innerHTML = arrayData[2]; | 
          
            | 138 |  |         dojo.byId("mark_status").innerHTML = arrayData[3]; | 
          
            | 139 |  |         // and the fancy fading effect | 
          
            | 140 |  |         dojo.lfx.html.highlight("mark_status", [255, 151, 58], 700).play(300); | 
          
            | 141 |  |     } | 
          
            | 142 |  |      | 
          
            | 143 |  |     function init() | 
          
            | 144 |  |     { | 
          
            | 145 |  |         var sendFormButton = dojo.byId('sendFormButton'); | 
          
            | 146 |  |         dojo.event.connect(sendFormButton, 'onclick', 'sendForm') | 
          
            | 147 |  |     } | 
          
            | 148 |  |   | 
          
            | 149 |  |     dojo.addOnLoad(init); | 
          
            | 150 |  |          | 
          
            | 151 |  | </script> | 
          
            | 152 |  | }}} | 
          
            | 153 |  |  | 
          
            | 154 |  | the following ''HTML code'' just comes after the upper code snipset. | 
          
            | 155 |  |  | 
          
            | 156 |  | {{{ | 
          
            | 157 |  | [... total mark get updated, lets put css id="mark_total" ...] | 
          
            | 158 |  | {% if r.total_mark  %}<li><b>Score</b>: <span id="mark_total">{{ r.total_mark }}</span>/5</li>{% endif %} | 
          
            | 159 |  | [....] | 
          
            | 160 |  | {% if not user.is_anonymous %} | 
          
            | 161 |  |     {% ifnotequal user.id r.owner_id %} | 
          
            | 162 |  |         <form enctype="multipart/form-data" id="myForm" method="post"> | 
          
            | 163 |  |             <span id="mark_message">{{mark_message}}</span> | 
          
            | 164 |  |                <select name="select_mark"> | 
          
            | 165 |  |                     <option value ="1" {% ifequal my_mark 1 %} selected {% endifequal %}>1</option> | 
          
            | 166 |  |                     <option value ="2" {% ifequal my_mark 2 %} selected {% endifequal %}>2</option> | 
          
            | 167 |  |                     <option value ="3" {% ifequal my_mark 3 %} selected {% endifequal %}>3</option> | 
          
            | 168 |  |                     <option value ="4" {% ifequal my_mark 4 %} selected {% endifequal %}>4</option> | 
          
            | 169 |  |                     <option value ="5" {% ifequal my_mark 5 %} selected {% endifequal %}>5</option> | 
          
            | 170 |  |                </select> | 
          
            | 171 |  |        </form> | 
          
            | 172 |  |       <button id="sendFormButton">Notez</button> | 
          
            | 173 |  |       <br/> | 
          
            | 174 |  |       <span id="mark_status">{{ mark_status }}</span> | 
          
            | 175 |  |      {% endifnotequal %} | 
          
            | 176 |  | {% endif %} | 
          
            | 177 |  | }}} | 
          
            | 178 |  |  | 
          
            | 179 |  | And, voila. | 
          
            | 180 |  |  | 
          
            | 181 |  | To have a demo use guest as login, guest as password here [http://ozserver.no-ip.com:345] or if not working here [http://www.cefinban.net]. | 
          
            | 182 |  | Go to index and pick a recipe, update the rating. | 
          
            | 183 |  |  | 
          
            | 184 |  | You can also have a look at the screenshot here :  [http://ozserver.no-ip.com/~greg/images/ajaxdjango.png] | 
          
            | 185 |  |  | 
          
            | 186 |  | == Dreamhost and Simplejson == | 
          
            | 187 |  |  | 
          
            | 188 |  | If you are using dreamhost for hosting please be aware that simplejson is not installed. | 
          
            | 189 |  | Instead you will have to install the source of simplejson in a folder in your home directory eg /proz/json/simple_json | 
          
            | 190 |  | The simple_json directory contains the required __init__.py for it to be loaded as a python module. | 
          
            | 191 |  |  | 
          
            | 192 |  | Then in your ~/.bash_profile add the directory to your python path like below. | 
          
            | 193 |  |  | 
          
            | 194 |  | {{{ | 
          
            | 195 |  | export PYTHONPATH=$PYTHONPATH:$HOME/django/django_src:$HOME/django/django_projects:$HOME/progz/json | 
          
            | 196 |  | }}} | 
          
            | 197 |  |  | 
          
            | 198 |  | That will allow yout to use simpl_json in python shell. | 
          
            | 199 |  | But '''dont forget''' to change django.fcgi ! | 
          
            | 200 |  | Add | 
          
            | 201 |  | {{{ | 
          
            | 202 |  | sys.path +=['/home/coulix/progz/json'] | 
          
            | 203 |  | }}} | 
          
            | 204 |  | log out/in and try import simple_json (or simplejson depends on what source you picked) | 
          
            | 205 |  |  | 
          
            | 206 |  | {{{ | 
          
            | 207 |  | #!html | 
          
            | 208 |  | <a name="P2"><h1>Handling the form when JavaScript is deactivated.</h1></a> | 
          
            | 209 |  | }}} | 
          
            | 210 |  | If a user has deactivated his browser's javascript support, or is using a text mode browser, we need a way of making the previous rating button submit the rating to the server which should this time return an html template instead of data to the Ajax call. | 
          
            | 211 |  |  | 
          
            | 212 |  | == Updating the form HTML (details.html template) == | 
          
            | 213 |  | This time we put a submit type input inside the form instead of the button type in part 1. | 
          
            | 214 |  | type="submit" as indicates its name, submits the form to the server, we will need a way of stopping this behavior using javaScript. | 
          
            | 215 |  |  | 
          
            | 216 |  | {{{ | 
          
            | 217 |  | <form  enctype="multipart/form-data" id="myForm" method="post"> | 
          
            | 218 |  |         <span id="mark_message">{{mark_message}}</span> | 
          
            | 219 |  |         <select name="select_mark"> | 
          
            | 220 |  |                 [...] | 
          
            | 221 |  |         </select> | 
          
            | 222 |  |         <input id="sendFormButton" type="submit" value="Notez" /> | 
          
            | 223 |  | </form> | 
          
            | 224 |  | }}} | 
          
            | 225 |  |  | 
          
            | 226 |  | Now, how can we tell our details method in view.py to know if it comes from a normal submit request or an Ajax request ? | 
          
            | 227 |  | Two solutions, | 
          
            | 228 |  |  | 
          
            | 229 |  | The '''first''' uses a hidden variable in form.html and an added content element in the JS part. | 
          
            | 230 |  |  | 
          
            | 231 |  | {{{ | 
          
            | 232 |  |     function sendForm() | 
          
            | 233 |  |     { | 
          
            | 234 |  |      dojo.byId("mark_status").innerHTML = "Loading ..."; | 
          
            | 235 |  |      dojo.io.bind({ | 
          
            | 236 |  |                     url: '.', | 
          
            | 237 |  |                     handler: sendFormCallback, | 
          
            | 238 |  |                     content: {"js", "true"}, | 
          
            | 239 |  |                     formNode: dojo.byId('myForm') | 
          
            | 240 |  |                 }); | 
          
            | 241 |  |     } | 
          
            | 242 |  |  | 
          
            | 243 |  |  | 
          
            | 244 |  | [...] | 
          
            | 245 |  | <form  enctype="multipart/form-data" id="myForm" method="post"> | 
          
            | 246 |  | [...] | 
          
            | 247 |  | <input type="hidden" name="js" value="false"> | 
          
            | 248 |  | </form> | 
          
            | 249 |  | }}} | 
          
            | 250 |  |  | 
          
            | 251 |  | With this, in our django method in view.py we can test for request["js"]=="true" it would means that Js is activatd and we return the appropriate answer to the Ajax request. | 
          
            | 252 |  |  | 
          
            | 253 |  | The '''second''' uses the url to pass a new variable ajax_or_not to the detail method. | 
          
            | 254 |  |  | 
          
            | 255 |  | {{{ | 
          
            | 256 |  | #!python | 
          
            | 257 |  | def details(request, r_slug, r_cat, ajax_or_not=None): | 
          
            | 258 |  | [...] | 
          
            | 259 |  | }}} | 
          
            | 260 |  |  | 
          
            | 261 |  | We modify the url.py to accept this new parameter. | 
          
            | 262 |  | {{{ | 
          
            | 263 |  | #!python | 
          
            | 264 |  | (r'^recettes/(?P<r_cat>[-\w]+)/(?P<r_slug>[-\w]+)/(?P<ajax_or_not>.*)$', 'cefinban.recettes.views.details'), | 
          
            | 265 |  | }}} | 
          
            | 266 |  |  | 
          
            | 267 |  | The dojo binding needs to append a variable to the original document url, to make ajax_or_not not None. | 
          
            | 268 |  |  | 
          
            | 269 |  | {{{ | 
          
            | 270 |  |     function sendForm() | 
          
            | 271 |  |     { | 
          
            | 272 |  |      dojo.byId("mark_status").innerHTML = "Loading ..."; | 
          
            | 273 |  |      dojo.io.bind({ | 
          
            | 274 |  |                     url: './ajax/', | 
          
            | 275 |  |                     handler: sendFormCallback, | 
          
            | 276 |  |                     formNode: dojo.byId('myForm') | 
          
            | 277 |  |                 }); | 
          
            | 278 |  |     } | 
          
            | 279 |  | }}} | 
          
            | 280 |  |  | 
          
            | 281 |  | == New details method in view.py == | 
          
            | 282 |  |  | 
          
            | 283 |  | We just need to test for the existence of ajax_or_not | 
          
            | 284 |  |  | 
          
            | 285 |  | {{{ | 
          
            | 286 |  | #!python | 
          
            | 287 |  | def details(request, r_slug, r_cat, ajax_or_not=None): | 
          
            | 288 |  | [...] | 
          
            | 289 |  |         if request.POST: | 
          
            | 290 |  |                 [...] same as part 1  | 
          
            | 291 |  |                 # except here we check ajax_or_not | 
          
            | 292 |  |                 if  ajax_or_not: | 
          
            | 293 |  |                         # use json for python js exchange | 
          
            | 294 |  |                         # it was a french string, if we dont't use unicode | 
          
            | 295 |  |                         # the result at the output of json in Dojo is wrong. | 
          
            | 296 |  |                         message = unicode( message, "utf-8" ) | 
          
            | 297 |  |                         jsonList = simple_json.dumps((my_mark, total, form_message ,message)) | 
          
            | 298 |  |                         return HttpResponse(jsonList) | 
          
            | 299 |  |  | 
          
            | 300 |  |         return render_to_response('recettes/details.html', {'r': r, 'section':'index', | 
          
            | 301 |  |                  'mark_status':message , 'mark_message':form_message, 'my_mark': my_mark}, | 
          
            | 302 |  |                   context_instance=RequestContext(request),) | 
          
            | 303 |  | }}} | 
          
            | 304 |  |  | 
          
            | 305 |  | {{{ | 
          
            | 306 |  | #!html | 
          
            | 307 |  | <a name="P3"><h1>Fixing the frozen fading when user resend the form without waiting for the first fading to end.</h1></a> | 
          
            | 308 |  | }}} | 
          
            | 309 |  | If you haven't realised yet, if two or more calls are sent to the javascript function sendForm in a short time, the fading effect of the current sendForm Callback method might get stuck / froze / bugged.  | 
          
            | 310 |  | We need a way of avoiding this by desactivating the connection between the submit button and the sendForm method while the fading '''animation''' is active. | 
          
            | 311 |  | Thanks Dojo there is such things ! in two lines of code its done. | 
          
            | 312 |  |  | 
          
            | 313 |  |  | 
          
            | 314 |  | {{{ | 
          
            | 315 |  | function sendFormCallback(type, data, evt) | 
          
            | 316 |  |     { | 
          
            | 317 |  |        [...as before ...] | 
          
            | 318 |  |         // and the fancy fading effect | 
          
            | 319 |  |          | 
          
            | 320 |  |         // first disconnect the listener ! | 
          
            | 321 |  |         dojo.event.disconnect(sendFormButton, 'onclick', 'sendForm'); | 
          
            | 322 |  |         // assign our fading effect to an anim variable. | 
          
            | 323 |  |         var anim = dojo.lfx.html.highlight("mark_status", [255, 151, 58], 700).play(300); | 
          
            | 324 |  |         // When this anim is finish, reconnect | 
          
            | 325 |  |         dojo.event.connect(anim, "onEnd", function() {  dojo.event.connect(sendFormButton, 'onclick', 'sendForm'); }); | 
          
            | 326 |  |     } | 
          
            | 327 |  | }}} | 
          
            | 328 |  |  | 
          
            | 329 |  | how nice is this ! | 
          
            | 330 |  | Careful, while talking about how to fix the problem using onEnd in Dojo IRC chanel, they realised play() method didnt behave properly and updated it to react to onEnd and such. | 
          
            | 331 |  | su you need at least revision '''4286'''. | 
          
            | 332 |  | Update your dojo source | 
          
            | 333 |  |  | 
          
            | 334 |  | {{{ | 
          
            | 335 |  | svn co http://svn.dojotoolkit.org/dojo/trunk dojo | 
          
            | 336 |  | }}} | 
          
            | 337 |  |  | 
          
            | 338 |  |  | 
          
            | 339 |  |  | 
          
            | 340 |  |  | 
          
            | 341 |  | '''Note'''  It might be completely wrong. | 
          
            | 342 |  | More questions / complaints: coulix@gmail.com  | 
          
            | 343 |  |  |