The problem I face has to do with re-rendering a Django view based on a context that is updated by an AJAX post request that is initiated by jQuery. The reason why I need an AJAX request is because I need to modify the page UI without refreshing the page, which is critical to what I want to build.
So far, I am able to trigger the AJAX post request to the URL of the same page where the update is supposed to occur, and the Django view.py adequately registers that it has been called. However, although I can reproduce the ability to update the Django view's context, the view does not seem to re-render an updated HTML based on the updated context.
The thread at How to re-render django template code on AJAX call seems to describe exactly my problem. The top-voted solution of this thread is to have a conditional that is only triggered in case of an AJAX call that renders only a partial template (not the full HTML page) - a partial template that corresponds to the component to be updated. This is what's being reproduced in the code snippets below, but the HTML does not change based on the updated context.
Attached are the relevant code snippets for a simple attempt where the page displays <h1>2<h1/> by default and is meant to be updated to 5 when we click anywhere on the window. Clicking anywhere on the window triggers the AJAX call, but the page is not updated with <h1>5<h1/>.
view.py
def index(request):
context = {"num": 2}
if (request.method == "POST"):
print("View hit due to AJAX call!") # // THIS CAN BE TRIGGERED ADEQUATELY!
context["num"] = 5
return render(request, 'num.html', context)
return render(request, 'override.html', context)
override.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="{% static 'js/num.js' %}" defer></script>
<title>Title</title>
</head>
<body class="">
<div class="">
{% include 'num.html' %}
</div>
</body>
</html>
num.html
<h1>{{ num }}</h1>
num.js
var postUrl = "http://localhost:8000/";
function getCSRFToken() {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
if (cookie.substring(0, 10) == ('csrftoken' + '=')) {
cookieValue = decodeURIComponent(cookie.substring(10));
break;
}
}
}
return cookieValue;
}
$('html').click(function(){
values = [1, 2];
var jsonText = JSON.stringify(values);
$.ajax({
url: postUrl,
headers: { "X-CSRFToken": getCSRFToken(), "X-Requested-With": "XMLHttpRequest" },
type: 'POST',
data: jsonText, // note the data, jsonText, does not really matter. The Django context gets updated to 5 instead of 2.
traditional: true,
dataType: 'html',
success: function(result){
console.log("AJAX successful") // THIS CAN BE TRIGGERED ADEQUATELY!
}
});
});
If this part of code
success: function(result){
console.log(result)
}
prints the content of num.html you can change the num.html to get h1 id as
num.hml
<h1 id="numberToChange">{{ num }}</h1>
and in your num.js
$('html').click(function(){
values = [1, 2];
var jsonText = JSON.stringify(values);
$.ajax({
url: postUrl,
headers: { "X-CSRFToken": getCSRFToken(), "X-Requested-With": "XMLHttpRequest" },
type: 'POST',
data: jsonText, // note the data, jsonText, does not really matter. The Django context gets updated to 5 instead of 2.
traditional: true,
dataType: 'html',
success: function(result){
document.getElementById("numberToChange").innerHTML = result}
});
});
I know this question has been asked several times, but I cannot find a solution.
I cannot connect the method output in views with html page.
views
def save_form(request):
if request.method == 'POST' and 'save' in request.POST:
lsection = [5]
print("calculate method runs...")
return JsonResponse({'lsection':lsection})
"calculate method runs..." is printed only when I do not use ajax.
html
<form method="post" name="nameForm" id="idForm"
action = "/project/save/" enctype="multipart/form-data">
{% csrf_token %}
...
<input type="submit" name="save" value="Save">
</form>
ajax
var frm = $('#idForm');
frm.submit(function (e) {
e.preventDefault();
$.ajax({
type: frm.attr('save_form'),
url: frm.attr('/project/save/'),
//data: frm.serialize(), // I tried this as well
data: {},
success: function (data) {
alert(data)
},
error: function (data) {
alert("ajax fails")
},
});
});
Question:
I do not get lsection list on html, I try alert(data) I get an empty html.
How can I get lsection on html?
You need attribute's values:
type: frm.attr('method'),
url: frm.attr('action'),
Correct code:
$.ajax({
type: frm.attr('method'),
url: frm.attr('action'),
data: frm.serialize(),
success: function (data) {
alert(data)
},
error: function (data) {
alert("ajax fails")
}
});
.attr
While receiving JSON response in ajax success you used data.
to show the lsection.
alert(data.lsection)
update alert like above
This is the view, the success function should make changes in the <p id="mondiv"></p>tags.
<input type="button" value="Refuser" class="refuser" id="{{ demande.id }}">
<p id="mondiv"></p>
<script>
$(".refuser").click(function () {
$.ajax({
url: '{{ path('verifier_demandes') }}',
type: 'POST',
data: {'id_demande': this.id},
dataType: 'json',
success: function (data) {
$.each(data,function (i,e) {
$('#mondiv').append('<ul>'
+'<li>'+e.id+'</li>'
+'<li>'+e.etat+'</li>'
+'<li>'+e.user.nom+'</li>'
+'<li>'+e.user.prenom+'</li>'
+'<li>'+e.user.username+'</li>'
+'</ul>');
})
},
error: function(data) {
alert('error');
}
})
}
);
This is the controller, the entity gets deleted like intended but since then i can't change my view element ( the success function )
if($request->isXmlHttpRequest())
{
if ($request->get('id_demande')) {
$id_gerant = $request->get('id_demande');
$gerant = new Gerant();
$gerant = $em->getRepository("GestionBoutiquesBundle:Gerant")->findOneBy(array('id' => $id_gerant));
$em->remove($gerant);
$em->flush();
$demandes = new Gerant();
$demandes=$em->getRepository('GestionBoutiquesBundle:Gerant')->findBy(array('etat'=>false));
$ser= new Serializer(array(new ObjectNormalizer()));
$data=$ser->normalize($demandes);
return new JsonResponse($data);
}
}
I have looked from both sides, the controller sending back the Json response, and from the view, but couldn't find any result.
EDIT: knowing that the $demandes i'm trying to send back with Json is an array of users, each user has an id, etat, nom, prenom, username..
You made a POST request so you should be able to access to the post data as follow:
$id_gerant = $request->request->get('id_demande');
if($id_gerant) {
....
instead of:
if ($request->get('id_demande')) {
$id_gerant = $request->get('id_demande');
....
As described here in the doc Symfony Request Object
Hope this help
I have 2 checkboxes. I need to capture the checkboxes clicked and send it to REST SERVICE, I am able to capture checkboxes clicked at fnt end but I don't know how to capture it in the REST (VB.Net).
Below is the front end file:
<form>
<input type="checkbox" class = "checkBoxProp" id = "1" name="checkBoxProp" value="1">Graph1<br>
<input type="checkbox" class = "checkBoxProp" id = "2" name="checkBoxProp" value="2">Graph4<br>
<input id="btnGetResponse" type="button" value="ClickMe!"/>
</form>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
$("#btnGetResponse").click(function()
{
var ids = $('.checkBoxProp:checked').map(function() {
return this.value;
}).get();
console.log(JSON.stringify(ids.join()));
$.ajax({
type: "POST",
url: "http://localhost:51349/SMS_Rest.svc/v1/usercheckboxes",
data: {ids: ids} ,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(response)
{
alert('success');
},
failure: function(response)
{
alert('fail');
}
});
});
So how to capture JSON at REST.
Public Function CheckBoxDetails(ByVal requestData As **WHAT TO WRITE HERE**) As String Implements iSMS_Rest.CheckBoxDetails
// SOME LOGIC
End Function
Your method at server-side should be a POST and you should capture the data from body of the request instead of querystring. So, the argument list should have [FromBody] attribute and it would be abetter way if you can create a input model class with similar structure as the input data and have similar properties, use that object as the parameter to the POST method.
I am attempting to make a HTTP request to a WebAPI using a AngularJS service and load a HTML page with two nested ng-repeat's (posts and replies). I can get the {{ post.displayName }} to populate in my browser, but replies aren't loading. Can anyone assist?
the JSON from the WebAPI:
[{"postId":1,"displayName":"Scott","message":"message1","replies":{"replyId":1,"displayName":"wayne","message":"test11"}},{"postId":2,"displayName":"Bill","message":"message12","replies":{"replyId":1,"displayName":"bob","message":"test21"}},{"postId":3,"displayName":"Wayne","message":"message12","replies":{"replyId":1,"displayName":"bob","message":"test21"}},{"postId":4,"displayName":"Bonnie","message":"message12","replies":{"replyId":1,"displayName":"bob","message":"test21"}},{"postId":5,"displayName":"Gina","message":"message12","replies":{"replyId":1,"displayName":"bob","message":"test21"}}]
My Service:
// This handles the database work for posting
gwApp.service('postService', function ($http, $q) {
// ---
// PUBLIC METHODS.
// ---
this.getPosts = function () {
var request = $http({
method: "get",
url: "http://localhost:59327/posts/details",
params: {
action: "get"
}
});
return (request.then(handleSuccess, handleError));
};
// ---
// PRIVATE METHODS.
// ---
// Transform the error response, unwrapping the application dta from
// the API response payload.
function handleError(response) {
// The API response from the server should be returned in a
// nomralized format. However, if the request was not handled by the
// server (or what not handles properly - ex. server error), then we
// may have to normalize it on our end, as best we can.
if (
!angular.isObject(response.data) ||
!response.data.message
) {
return ($q.reject("An unknown error occurred."));
}
// Otherwise, use expected error message.
return ($q.reject(response.data.message));
}
// Transform the successful response, unwrapping the application data
// from the API response payload.
function handleSuccess(response) {
return (response.data);
}
});
My Controller:
//This controller retrieves data from the services and associates then with the $scope
//The $scope is ultimately bound to the posts view
gwApp.controller('PostController', function ($scope, postService) {
$scope.posts = [];
loadRemoteData();
// public methods
// private methods
function applyRemoteData(newPosts) {
$scope.posts = newPosts;
}
function loadRemoteData() {
// $scope.posts = postService.getPosts();
postService.getPosts()
.then(
function (posts) {
applyRemoteData(posts);
}
);
}
});
My HTML code snippet:
this returns 3 blank table rows
<tr data-ng-repeat="reply in post.replies">
<td>
{{ reply.message }}
</td>
</tr>
This returns the valid data from my JSON:
<tr data-ng-repeat="post in posts">
<td>
PostId: {{ post.postId }} {{ post.displayName }}
</td>
</tr>
Any help would be much appreciated!
PLease see here: http://plnkr.co/edit/pMeopZwm2ZybIXvTRucy?p=preview
Your each post has only one object called replies, more likely that should be array of replays so you can access it like below :
<table>
<tr data-ng-repeat="post in posts">
<td>
PostId: {{ post.postId }} {{ post.displayName }}
<ul>
<li>{{post.replies.displayName}}: {{post.replies.message }}</li>
</ul>
</td>
</tr>
</table>
The answer by sss did work initially, but I got the best result when updating my JSON to use a list for the replies:
[{"postId":1,"displayName":"Scott","message":"message1","replies":[{"replyId":1,"displayName":"wayne","message":"test111"},{"replyId":2,"displayName":"bob","message":"test112"},{"replyId":3,"displayName":"bon","message":"test113"},{"replyId":4,"displayName":"ethan","message":"test114"}]},{"postId":2,"displayName":"Bill","message":"message12","replies":[{"replyId":1,"displayName":"wayne","message":"test211"},{"replyId":2,"displayName":"bob","message":"test212"}]},{"postId":3,"displayName":"Wayne","message":"message12","replies":[{"replyId":1,"displayName":"wayne","message":"test311"},{"replyId":2,"displayName":"bob","message":"test312"},{"replyId":3,"displayName":"bon","message":"test313"}]},{"postId":4,"displayName":"Bonnie","message":"message12","replies":[{"replyId":1,"displayName":"wayne","message":"test411"},{"replyId":2,"displayName":"bob","message":"test412"},{"replyId":3,"displayName":"bon","message":"test413"},{"replyId":3,"displayName":"bon","message":"test414"},{"replyId":4,"displayName":"ethan","message":"test415"}]},{"postId":5,"displayName":"Gina","message":"message12","replies":[{"replyId":1,"displayName":"wayne","message":"test511"},{"replyId":2,"displayName":"bob","message":"test512"},{"replyId":3,"displayName":"bon","message":"test513"},{"replyId":4,"displayName":"ethan","message":"test514"}]}]