I have a form in a bootstrap modal, and I want to keep that modal open after submission. I am using CreateView and trying to pass an additional variable to the template in front-end where I could check if the flag is set or not, but the flag is always False even after submission. Here is what I have:
url.py
from django.urls import path
from .views import MescData
urlpatterns = [
path('mesc', MescData.as_view(), name='mesc')
]
views.py
from django.urls import reverse
from django.views.generic.edit import CreateView
from .forms import MescForm
from .models import Mesc
class MescData(CreateView):
model = Mesc
form_class = MescForm
template_name = 'Materials/mesc_data.html'
successful_submit = False # Flag to keep the add entry modal open
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['successful_submit'] = self.successful_submit
return context
def get_success_url(self):
return reverse('mesc')
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
self.successful_submit = True
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form, **kwargs):
# self.successful_submit = True
return super(MescData, self).form_valid(form, **kwargs)
And in Template, I'm checking it like this:
{% if successful_submit %}
<h1>Flag is set</h1>
{% endif %}
Is there a way to pass non-form-related data in CreateView to the template without having to change the url.py (i.e. adding variable data to url path)?
EDIT:
I tried printing the self.successful_submit in form_valid() and post() methods and it is indeed being updated to True, but in the template it is still being passed on as False.
This is the core problem: "I have a form in a bootstrap modal, and I want to keep that modal open after submission."
Simple answer: Use Ajax.
We do now have HTML over the wire as a paradigm gaining popularity, but I'm not familiar enough with it to discuss it. So I will use Ajax to show a solution. This particular solution uses a general ajax template, and the result of a post is a rendered Django template that you can use to just replace the HTML in your already rendered page.
Also, few people like JavaScript, but that is not a good enough reason to avoid using it. It is basically mandatory in any web application you're running. Even HTML over the wire uses a minimal amount of JavaScript to accomplish its goals.
First, write your Ajax view. I'm using django-rest-framework classes for this, and I'm providing an example for filling out a Calibration record. You can use whatever form you want; this is just my recipe for handling modals that you want keep open. In this view, I return a JSON response if the POST was successful. Otherwise, I return a rendered Django template.
from rest_framework.generics import (CreateAPIView, RetrieveAPIView,
UpdateAPIView, get_object_or_404)
from rest_framework.renderers import JSONRenderer, TemplateHTMLRenderer
from rest_framework.response import Response
class CalibrationCreateAjaxView(CreateAPIView, UpdateAPIView, RetrieveAPIView):
renderer_classes = (TemplateHTMLRenderer,)
template_name = "documents/form/cal.html"
def post(self, request, *args, **kwargs):
context = self.get_context(request)
calibration_form = context['calibration_form']
if calibration_form.is_valid():
calibration_form.save()
request.accepted_renderer = JSONRenderer()
return Response(status=201)
return Response(context, status=400)
def get(self, request, *args, **kwargs):
return Response(self.get_context(request))
#staticmethod
def get_context(request):
pk = request.GET.get("pk")
calibration_entry = get_object_or_404(CalibrationEntry, pk=pk) if pk else None
return {
'calibration_form': CalibrationFormAjax(request.POST or None, instance=calibration_entry)
}
I have my view template as well. It takes advantage of request.is_ajax, which is being deprecated. You'll need to add some middleware to keep using it. Here's my middleware. Add it to your settings file as well.
class IsAjaxMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
"""
request.is_ajax is being removed in Django 4
Since we depend on this in our templates, we are adding this attribute to request
Please review:
https://docs.djangoproject.com/en/4.0/releases/3.1/#id2
"""
def __call__(self, request):
request.is_ajax = request.headers.get('x-requested-with') == 'XMLHttpRequest'
return self.get_response(request)
general/ajax_modal.html
<!-- {% block modal_id %}{% endblock %}{% block modal_title %}{% endblock %} -->
{% block modal_body %}
{% endblock modal_body %}
general/modal.html
<div class="modal fade" id="{% block modal_id %}{{ modal_id }}{% endblock modal_id %}" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">
{% block modal_title %}{{ modal_title }}{% endblock modal_title %}
</h4>
</div>
<div class="modal-body">
{% block modal_body %}
{% endblock modal_body %}
</div>
</div>
</div>
</div>
Even though, we're using Crispy Forms, you can get away without using it. I also have a general templatetag library that renders any errors on a form. You can write your own.
documents/form/cal.html
{% extends request.is_ajax|yesno:'general\ajax_modal.html,general\modal.html' %}
{% load crispy_forms_tags general %}
{% block modal_id %}new-cal-modal{% endblock modal_id %}
{% block modal_title %}Enter Calibration Information{% endblock modal_title %}
{% block modal_body %}
<div id="new-cal-form-container">
<form action="{% url 'calibration-create' %}" method="post" id="new-cal-modal-form" autocomplete="off" novalidate>
{% if request.is_ajax %}
{% crispy calibration_form %}
{% form_errors calibration_form %}
{% endif %}
<button type="submit" name="Submit" value="Save" class="btn btn-success button" id="submit">save</button>
</form>
</div>
{% endblock modal_body %}
So now that the Ajax view is all set up, I go back to the main page that will render the modal dialog when the user clicks a button. I have a block called "extraContent" in which I include the template of the modal form.
{% block extraContent %}
{% include 'documents/form/cal.html' %}
{% endblock extraContent %}
And now, the JavaScript, which requires jQuery, that I've added to the template. I guess I made my own jQuery plugin on top of that...
$.fn.modalFormContainer = function(optionsObject) {
//call it on the modal div (the div that contains a modal-dialog, which contains modal-header, modal-body, etc
// we expect there to be a form within that div, and that form should have an action url
// when buttons that trigger the modal are clicked, the form is fetched from that action url and replaced.
// optionsObject has formAfterLoadFunction and ajaxDoneFunction
var options = $.extend({}, $.fn.modalFormContainer.defaults, optionsObject);
var $modalFormContainer = $(this);
// get the buttons that trigger this modal to open
// add a click event so that the form is fetched whenever the buttons are clicked
// if data-pk is an attribute on the button, apply that to the querystring of the
// ajaxURL when fetching the form
var modalID = $modalFormContainer.prop("id");
var modalFormButtonSelector = "[data-target=#" + modalID + "][data-toggle=modal]";
function handleModalButtonClick(event) {
//does the button have an associated pk? if so add the pk to the querystring of the ajax url
// this is wrapped in a form so that it gets replaced by the ajax response.
var $button = $(this);
if (!$button.hasClass("disabled") && !$button.prop("disabled")) { //only do it if the button is "enabled"
var $placeholder = $("<form><h1>loading...</h1></form>");
var $modalForm = $modalFormContainer.find("form");
var ajaxURL = $modalForm.prop("action");
$modalForm.replaceWith($placeholder);
var pk = $button.data().pk;
if (pk) {
if (ajaxURL.indexOf("?") > 0) {
ajaxURL += "&pk=" + pk;
} else {
ajaxURL += "?pk=" + pk;
}
}
//fetch the form and replace $modalFormContainer's contents with it
$.ajax({
type: "GET",
url: ajaxURL
}).done(function(response) {
// re-create the form from the response
$modalFormContainer.find(".modal-body").html(response);
$modalForm = $modalFormContainer.find("form"); //we would still need to find the form
options.formAfterLoadFunction($modalForm);
});
} else {
return false; //don't trigger the modal.
}
}
//using delegation here so that dynamically added buttons will still have the behavior.
// maybe use something more specific than '.main-panel' to help with performance?
$(".main-panel").on("click", modalFormButtonSelector, handleModalButtonClick);
$modalFormContainer.on("submit", "form", function(event) {
// Stop the browser from submitting the form
event.preventDefault();
var $modalForm = $(event.target);
var ajaxURL = $modalForm.prop("action");
$modalForm.find("[type=submit]").addClass("disabled").prop("disabled", true);
var formData = $modalForm.serialize();
var internal_options = {
url: ajaxURL,
type: "POST",
data: formData
};
// file upload forms have and enctype attribute
// we should not process files to be converted into strings
if ($modalForm.attr("enctype") === "multipart/form-data") {
internal_options.processData = false;
internal_options.contentType = false;
internal_options.cache = false;
formData = new FormData($modalForm.get(0));
internal_options.data = formData;
}
$.ajax(internal_options).done(function(response) {
// blank out the form
$modalForm.find("input:visible, select:visible, textarea:visible").val("");
// remove errors on the form
$modalForm.find(".has-error").removeClass("has-error");
$modalForm.find("[id^=error]").remove();
$modalForm.find(".alert.alert-block.alert-danger").remove();
// hide the modal
$(".modal-header .close").click();
options.ajaxDoneFunction(response);
}).fail(function(data) {
// re-create the form from the response
$modalFormContainer.find(".modal-body").html(data.responseText);
options.formAfterLoadFunction($modalForm);
});
});
return this;
};
$.fn.modalFormContainer.defaults = {
formAfterLoadFunction: function($form) { return; },
ajaxDoneFunction: function(response) { return; }
};
$("#new-cal-modal").modalFormContainer({
formAfterLoadFunction: function($modalForm) {
$(".datetimeinput").datepicker('destroy');
$(".datetimeinput").datepicker();
},
ajaxDoneFunction: function(event) {
location.reload();
}
});
So upon reviewing this, I've realized that this recipe is far more complicated than I have tricked myself into believing. I sincerely apologize for that. I hope that you can review the code and get an idea of what is happening. There are some edge cases, such as dealing with dates and file uploads, that this recipe handles right now, but you may not actually need them. I should mention that the application that this came from is using Bootstrap 3, so its styling is not updated to the current Bootstrap 5 as of this writing. I should add that the main content of the app has a class of "main-panel" as used in this not-so-generic jQuery plugin.
I'm worried that I've gone and overwhelmed you into maintaining your position of trying to keep using a standard POST request. I guess you could just re-render the template with your POST since it'll be standard practice in your project. You could still get away without using a query string that way.
Related
I am trying to use Ajax to submit a like button, I believe everything is in order but I keep getting PAge 404 error after submitting the like button,
I am not sure what is the reason. Need help to identify the error.
I have made some amendments to the URL path moving it from app urls.py to the main project urls.py but the error showing now is
LikeView() missing 1 required positional argument: 'pk'
Here is the view
class PostDetailView(DetailView):
model = Post
template_name = "post_detail.html"
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data()
stuff = get_object_or_404(Post, id=self.kwargs['pk'])
total_likes = stuff.total_likes()
liked = False
if stuff.likes.filter(id=self.request.user.id).exists():
liked = True
context["total_likes"] = total_likes
context["liked"] = liked
return context
def LikeView(request, pk):
# post = get_object_or_404(Post, id=request.POST.get('post_id'))
post = get_object_or_404(Post, id=request.POST.get('id'))
like = False
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
like = False
else:
post.likes.add(request.user)
like = True
context["total_likes"] = total_likes
context["liked"] = liked
if request.is_ajax:
html = render_to_string('like_section.html', context, request=request)
return JsonResponse({'form': html})
Here is the url.py in the main project, I have moved it from the app called Score the main 'url.py'
urlpatterns = [
path('like/', views.LikeView, name='like_post'),
here is the updated template
<form class="mt-0" action="{% url 'like_post' %}" method='POST'>
{% csrf_token %}
<strong> Likes
: {{total_likes}} </strong>
{% if user.is_authenticated %}
{% if liked %}
<button id='like' type='submit' name='post_id' class= "btn btn-danger btn-sm" value="{{post.id}}"> Unlike </button>
{% else %}
<button id='like' type='submit' name='post_id' class= "btn btn-primary btn-sm" value="{{post.id}}"> Like </button>
{% endif %}
{% else %}
<p><small> Login to Like </small></p>
{% endif %}
</form>
here is the ajax
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(event){
$(document).on('click','#like', function(event){
event.preventDefault();
$var pk= $(this).attr('value');
$.ajax({
type:'POST',
url:'{% url "like_post" %}',
data:{'id': pk, 'csrfmiddlewaretoken':'{{csrf_token}}'},
dataType:'json',
success:function(response){
$('#like-section').html(response['form'])
console.log($('#like-section').html(response['form']));
},
error:function(rs, e){
console.log(rs.responseText);
},
});
});
});
</script>
Thank you
As error indicates, ListView class requires a positional argument which is pk. So your view class should change.
def LikeView(request):
Use this code as ListView definition.
I have a made a quiz page, which checks whether the answer of the user is correct or not using a "checkans" function. I want to return a "Correct" message if the answer is correct and an "Incorrect" message if the answer is not correct. Now I can "kind of" do it, but not exactly what I want. Now it returns the message after redirecting to a whole new page, with the Question Box and everything else totally disappeared, only with the message.
I want the message to be shown on the same original question page, somewhere under the question box or within the question box, without redirecting to another page or refreshing the page, after submitting the answer. I don't know how to do it.
Here is my view:
class QuizView(generic.ListView):
template_name = 'geniusdennis/quiz.html'
queryset = Spanish.objects.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# grab the max id in the database
max_id = Spanish.objects.order_by('-id')[0].id
random_id = random.randint(1, max_id + 1)
random_spanish_question = Spanish.objects.filter(id__gte=random_id)[0]
context['random_spanish_question'] = random_spanish_question
return context
Here is my function for checking the answer:
def checkans(request, spanish_id):
random_spanish_question = get_object_or_404(Spanish, pk=spanish_id)
query = request.GET.get('ans')
coreng = random_spanish_question.english_set.get()
if query == str(coreng):
return render(request, 'geniusdennis/quiz.html',{
'message': "Correct!",
})
else:
return render(request, 'geniusdennis/quiz.html', {
'message': "Incorrect.",
'correct_answer': "The correct answer is " + str(coreng),
})
And here is my HTML page:
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'geniusdennis/style.css' %}">
{% if random_spanish_question %}
<div class="flexcontainer" style="justify-content: center;">
<div class="sectiontitle">Quiz time
</div>
<div class="question_card">
<div class="question_word">{{ random_spanish_question }}</div>
<form action="/checkans/{{random_spanish_question.id}}/" method="get">{% csrf_token %}
<label for="ans">Answer:</label>
<input type="text" name="ans"/>
<input type="submit" value="Submit"/>
</form>
<input type="submit" value="Skip"/>
</div>
</div>
{% else %}
{% if message %}
<div class="message">
{{ message }}
</div>
<div class="ans">
{{ correct_answer }}
</div>
{% endif %}
{% endif %}
What you need is ajax, so you need some js code here.
<scrip src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
$('form').on('submit', function(e) { // or you can get the form by id if you set it
e.preventDefault(); // avoid to execute the actual submit of the form.
var form = $(this);
var url = form.attr('action');
$.ajax({
type: 'GET',
url: url,
data: form.serialize(), // serializes the forms elements.
success: function(data)
{
... // whatever you want to do
var alertMessage = data.message;
if (data.correct_answer) {
alertMessage += ' ' + data.correct_answer;
}
alert(alertMessage); // show response
}
});
});
</script>
html forms will go to action url. If you want some changes or functions in your page without reload, you need to use js.
Quite commonly in web applications, you need to display a one-time
notification message (also known as “flash message”) to the user after
processing a form or some other types of user input.
For this, Django provides full support for cookie- and session-based
messaging, for both anonymous and authenticated users. The messages
framework allows you to temporarily store messages in one request and
retrieve them for display in a subsequent request (usually the next
one). Every message is tagged with a specific level that determines
its priority (e.g., info, warning, or error).
for implementing messages refer to: https://docs.djangoproject.com/en/1.11/ref/contrib/messages/
So I have a homepage that consists of a base listview which includes all of the objects from my db(as shown in the cbv .all() query). What I wanted to do is include a search filter, hence I thought it would be a good idea to isolate it into a separate "list.html" fragment in order to make it reusable later on. Currently, I have an ajax call that sends information to the cbv and the return is a render to list.html fragment. However, when I visit the homepage, the page doesn't get rendered to begin with.
Help or advice would be very much appreciated, thank you again.
urls.py
urlpatterns = [
url(r'^$', exp_view.DrugListView.as_view() , name = 'drug_list')]
here is my CBV template:
views.py
class DrugListView(ListView):
context_object_name = 'drugs'
model = Drug
template_name = 'expirations/drug_list.html'
def get(self, request):
if self.request.is_ajax():
text_to_search = self.request.GET.get('searchText')
print(text_to_search)
return render(request, "expirations/list.html", {'drug':Drug.objects.filter(name__contains = text_to_search).order_by('name')})
else:
return render(request, "expirations/list.html", {'drug':Drug.objects.all().order_by('name')})
here is
drug_list.html
{% extends 'expirations/index.html' %}
{% block content %}
{% include 'expirations/list.html' %}
{% endblock %}
{% block javascript %}
<script>
$(document).ready(function(){
var input = $("#searchText")
input.keyup(function() {
$.ajax({
type: "GET",
url: "{% url 'drug_list' %}",
data: {'searchText' : input.val()},
success: function(data){
$("#list_view").load("expirations/list.html")
}
})
});
})
</script>
{% endblock %}
here is list.html:
{% for drug in drugs %}
<div class = '.col-sm-12'>
<ul class = 'list-group'>
<li class = "list-group-item" >
<span class = "badge">First Expiration: {{ drug.early_exp|date:"l M d Y" }}</span>
{{ drug.name }}
{% regroup drug.expiration_dates.all by facility as facility_list %}
{% for x in facility_list %}
<span class="label label-info">{{ x.grouper }}</span>
{% endfor %}
</li>
</ul>
</div>
{% endfor %}
You've misunderstood what you need to do in your success callback.
That function has no access to Django templates. But it doesn't need to, because Django has already sent the fragment in the response, which is available in the data parameter. You just need to insert that into the existing page.
For that you'll need a container; so you should modify your drug_list.html so that there is a div around the include:
{% block content %}
<div id="list_contents">
{% include 'expirations/list.html' %}
</div>
{% endblock %}
and now your success function can be:
success: function(data) {
$("#list_contents").html(data)
}
I do not think that it will work this way. For Ajax calls and CBV look at example in the docs.
Then you would need to return the new, filtered data (in html format) to the ajax call and replace the html in the sucess function via JS.
Daniel Roseman definitely helped solve the puzzle, here is the updated code:
views.py: updated if/else statement to include a default list view of the general template. also added the context name 'drugs' instead of 'drug'
class DrugListView(ListView):
context_object_name = 'drugs'
model = Drug
template_name = 'expirations/drug_list.html'
def get(self, request):
if self.request.is_ajax():
text_to_search = self.request.GET.get('searchText')
print(text_to_search)
return render(request, "expirations/list.html", {'drugs':Drug.objects.filter(name__contains = text_to_search).order_by('name')})
else:
return render(request, "expirations/drug_list.html", {'drugs':Drug.objects.all().order_by('name')})
drug_list.html: changed success callback function load html as suggested by daniel.
{% extends 'expirations/index.html' %}
{% block content %}
<div id = 'list_view'>
{% include 'expirations/list.html' %}
</div>
{% endblock %}
{% block javascript %}
<script>
$(document).ready(function(){
var input = $("#searchText")
input.keyup(function() {
$.ajax({
type: "GET",
url: "{% url 'drug_list' %}",
data: {'searchText' : input.val()},
success: function(data){
$("#list_view").html(data)
}
})
});
})
</script>
{% endblock %}
The Goal:
I want to use FullCalendar(https://fullcalendar.io/) to display event objects from my database. FullCalendar accepts an array of event objects as a property.
The Problem I'm having:
I can send context data back to the template with the event objects but as far as I know I can only interact with the data using Django's template tagging system. *EDIT: When I replace the hardcoded array with ptoHistory I receive the following error in the chrome console:
jquery-3.1.1.min.js:2 Uncaught ReferenceError: ptoHistory is not
defined
at HTMLDocument. ((index):141)
at j (jquery-3.1.1.min.js:2)
at k (jquery-3.1.1.min.js:2)
index.html:
{% extends 'base.html' %}
{% block content %}
//ACCESSING THE CONTEXT DATA LIKE THIS WORKS BUT I CAN'T USE ptoHistory IN MY FULLCALLENDAR SCRIPT
{% for history in ptoHistory %}
<li>{{obj.leave_type}}</li>
{% endfor %}
<div class="container">
<div id="calendar">
<!-- Calendar is injected here -->
</div>
<!----------------------- SCRIPTS ----------------------------->
<script>
$(document).ready(function() {
$('#calendar').fullCalendar({
defaultView:'month',
editable: true,
// MY ARRAY OF OBJECTS WILL REPLACE THIS HARDCODED ARRAY
events: [
{
title: 'All Day Event',
start: '2017-01-12',
},
{
title: 'Meeting',
start: '2017-01-13T10:30:26',
end: '2014-06-13T12:30:00'
},
],
});
});
</script>
{% endblock content%}
IndexView.py:
class IndexView(FormView):
template_name = 'accounts/index.html'
form_class = PtoRequestForm
success_url = 'login/'
def form_valid(self, form):
form.save()
return super(IndexView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['ptoHistory'] = PtoHistory.objects.all()
print(context['ptoHistory'])
return context
Could someone point me in the right direction?
You are right, it won't work. Django template system is able to process python objects because they are executed already before the template is finished rendering.
You could still assign python list in javascript, though, but not with python objects but json string. The solution is basically compose for only what you need in the views using values() then . I don't know what fields do you need for PtoHistory, but you could do:
# views.py
import json
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
ptoHistory = json.dumps(list(PtoHistory.objects.values()))
# or do this if you only need several fields' value
# ptoHistory = json.dumps(list(PtoHistory.objects.values('field1', 'field2')))
context['ptoHistory'] = ptoHistory
return context
// in javascript
$(document).ready(function() {
$('#calendar').fullCalendar({
defaultView:'month',
editable: true,
var histories = {{ ptoHistory|safe }};
When my Django form fails to validate it automatically goes back to the page where the form failed but I'd like it to go to an html anchor on the page. Here's the code I have:
def detail(request, post_id):
p = get_object_or_404(Post, pk=post_id)
# get all the comments for the post
comments = p.comment_set.all().order_by('pub_date')
# to add a comment to the post
if request.method=='POST':
form = CommentForm(request.POST)
if form.is_valid():
c = Comment()
c.body = request.POST['body']
c.post = p
c.save()
return HttpResponseRedirect(reverse('journal.views.detail', kwargs={'post_id': post_id}) + "#comment-" + str(c.id))
else:
form = CommentForm()
return render(request, 'posts/detail.html', {'post': p, 'comments': comments, 'form': form})
The form is a comment form at the bottom of a blog post. When the form is invalid I'd like it to jump to the comment form's html anchor at the bottom of the page. Right now it goes to the top of the page and you have to scroll down to notice that there's an error.
I tried a couple things that didn't work. Please help!
I think that is fair enough:
<script>
function goToId() {
if (!window.location.hash) window.location = window.location + '#your-id';
}
{% if form.is_bound %}
$(window).on("load", goToId);
{% endif %}
</script>
One approach is to pass an extra key-value pair in the dict to contain the anchor name, and use JavaScript in the template to jump to it if needed.
Python side:
// ...
return render(request,
'post/detail.html',
{
// ...
'anchor': 'comment'
})
The template:
function goToOnLoadAnchor()
{
window.location.hash('{{ anchor }}'); // Or something with the same effect
}
{% ... %}
<body onload="goToOnLoadAnchor">
{% ... %}
You can also take advantage of custom template tags to add more flexibility.
CBV & jQuery
FormView
def form_invalid(self, form):
return self.render_to_response(self.get_context_data(form=form, anchor='my-form'))
template
<form ... id='my-form'> ... </form>
<script>
$(function () {
{% if anchor %}
$(document.body).animate({
'scrollTop': $('#{{ anchor }}').offset().top
}, 2000);
{% endif %}
});
</script>
Uses jQuery for scrolling (instead of jumping).
With Vanilla JavaScript:
{% if form.is_bound %}
<script>
document.addEventListener("DOMContentLoaded", function () {
document.getElementById("your_id").scrollIntoView();
});
</script>
{% endif %}
It works well with the addEventListener("DOMContentLoaded") listener. Otherwise it can give some weird results.