fr en

Utiliser AngularJS et Django pour faire des requêtes AJAX

Posted on 2014-10-25 in Programmation

Dans ce petit tuto, je vais vous expliquer comment faire pour poster un formulaire Django via une requête AJAX en utilisant le framework JavaScript AngularJS. Tout d'abord, il faut un peu de préparation. Voici le modèle que nous allons utiliser (les imports ne sont pas précisés et je suppose que vous savez où mettre ces fichiers) :

class Card(models.Model):
    content = models.TextField()
    date = models.DateTimeField(auto_now_add=True, auto_now=True)
    category = models.ForeignKey('Category')

    def __str__(self):
        return self.content

Le formulaire :

class CardForm(forms.ModelForm):
    class Meta:
        model = Card

Le code de la vue :

def add_card(request):
saved = False

if request.method == 'POST':
    form = CardForm(request.POST, request.FILES)
    if form.is_valid():
        card = Card()
        card.content = form.cleaned_data['content']
        card.category = form.cleaned_data['category']
        saved = True
        card.save()
else:
    form = CardForm()

return render(request, 'board/add_card.html', locals())

Et enfin le template :

<!DOCTYPE html>
{% load staticfiles %}
<html ng-app="ajax">
<head>
  <meta charset="UTF-8" />
   <script src={% static "angular.js" %} type="text/javascript" ></script>
   <script type="text/javascript" src={% static "angular-cookies.js" %}></script>
   <script src={% static "controllers.js" %} type="text/javascript" ></script>
 </head>
 <body>
<form action="{% url "board.views.add_card" %}" ng-controller="MyFormCtrl"
       method="post" name="form">{% csrf_token %}
       <p>
         <label for="id_content">Content:</label>
         <textarea cols="40" id="id_content" name="content" rows="10" ng-model="card.content"></textarea>
       </p>
         <p><label for="id_category">Category:</label> <select ng-model="card.category" id="id_category" name="category">
         <option value="" selected="selected">---------</option>
         <option value="1">Backlog</option>
         <option value="2">Done</option>
         <option value="3">In progress</option>
         </select>
         <button ng-click="submit($event)">Submit</button>
       </p>
 </form>
 </body>
 </html>

Comme toute application Angular, il contient la balise ng-app pour signaler au framework qu'il s'agit une portion de HTML géré par Angular et une balise ng-controller pour faire le lien avec le contrôleur de Angular. J'ai également ajouté {% load staticfiles %} pour utiliser la directive static de django.

Le formulaire doit être présent en intégralité dans le template (on ne peut pas utiliser {{ form.as_p }}) pour pouvoir faire correctement les liens avec la partie Angular :

  • L'aire de texte avec ng-model="card.content".
  • La liste d'option avec ng-model="card.category".
  • Le bouton de soumission avec ng-click="submit($event)". Le $event est nécessaire pour éviter le rechargement de la page (voir le code JavaScript).

Je vous laisse vous renseigner sur le fonctionnement d'Angular si vous n'êtes pas familier avec celui-ci.

Et pour finir, le code Javascript.

var nameSpace = angular.module("trello", ['ngCookies']);

nameSpace.controller("MyFormCtrl", ['$scope', '$http', '$cookies',
function ($scope, $http, $cookies) {
    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
    // Pour envoyer le code csrf.
    $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;

    // Cette fonction est appelée quand on soumet le formulaire.
    $scope.submit = function ($event) {
        // On évite le rechargement de la page.
        $event.preventDefault();
        // On prépare l'envoie des données.
        var in_data = jQuery.param({'content': $scope.card.content, 'category': $scope.card.category, 'csrfmiddlewaretoken': $cookies.csrftoken});
        $http.post('/add', in_data)
          .success(function(out_data) {
            // Remet le formulaire à zéro si tout c'est bien passé.
            $scope.card = angular.copy({});
        });
    }
 }]);

Normalement le code est assez commenté pour que vous le compreniez. Au besoin, postez un commentaire. Je signale juste que pour utiliser correctement le code csrf (qui est stocké dans un cookie) il faut utiliser le plugin ngCookies.

Ce code dépend de jQuery. Je n'ai pas trouvé de méthode pour passer correctement et facilement les paramètres de la requête avec uniquement Angular. La seule méthode que j'ai est :

var in_data = 'content=' + $scope.card.content + '&category=' + $scope.card.category + '&csrfmiddlewaretoken=' + $cookies.csrftoken;

Note : la requête AJAX peut aussi se mettre sous la forme plus longue suivante :

$http({
    url: '/add',
    method: 'POST',
    data: in_data
}).success(function(out_data) {
    $scope.card = angular.copy({});
});