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({});
});