AJAX requests with Django and AngularJS
Posted on 2014-10-25 in Programmation
In this tutorial, I will explain how to submit a Django form with an AJAX request thanks to AngularJS. First, we need to prepare our app - I don't precise imports and I assume that you know in which file to write the code. First, the model:
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
The form:
class CardForm(forms.ModelForm): class Meta: model = Card
The view:
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())
And the 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>
As any Angular app, it contains a ng-app and a ng-controllers tags to link your page with the JS code. I also added {% load staticfile %} to use django's static directive.
The form must be completely present in the template - you can't use {{ form.as_p }} - in order to correctly map its element with the model in Angular:
- The Textaera with ng-model="card.content".
- The options with ng-model="card.category".
- The submit button with ng-click="submit($event)". $event is required to avoid a page reload when the form is submitted.
And now, the JS code:
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'; // To send the csrf code. $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken; // This function is called when the form is submitted. $scope.submit = function ($event) { // Prevent page reload. $event.preventDefault(); // Send the data. 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) { // Reset the form in case of success. $scope.card = angular.copy({}); }); } }]);
In order to use the csrf code - which is stored in a cookie, you need to use the ngCookie plugin. You should be able to understand the rest of the code. In case of trouble, leave a comment.
This code requires jQuery. I have not found a way to correctly pass the argument with just Angular. The only method I have is:
var in_data = 'content=' + $scope.card.content + '&category=' + $scope.card.category + '&csrfmiddlewaretoken=' + $cookies.csrftoken;
Note: The AJAX request can also be written like this:
$http({ url: '/add', method: 'POST', data: in_data }).success(function(out_data) { $scope.card = angular.copy({}); });