Form not submitting unless submitting twice. Ajax, vanilla js, django - javascript

I am trying to submit a Django form with AJAX and Vanilla JavaScript however the form is not actually submitting unless I click the submit button twice.
I have an event listener on the form that stops the default submission to avoid page reload and then I open a XMLHttpRequest. On the first submission I get a 200 response but the data hasn't actually been sent to the database. However if I click the submit button again I get the desired 201 (item created) response from the server and it reloads my posts and adds the new one perfectly.
I am still a bit unfamiliar on working with asynchronous data and cannot figure out why it's not working. If I remove the e.preventDefault the form submits correctly and the new post shows up after the page reloads.
relevant JS snippet:
const postCreateFormEl = document.getElementById("post-create-form")
const postsEl = document.getElementById("posts")
const handlePostSubmit = function(e){
e.preventDefault()
const myForm = e.target
const myFormData = new FormData(myForm)
const url = myForm.getAttribute("action")
const method = myForm.getAttribute("method")
const xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest")
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
xhr.onload = function() {
const serverResponse = xhr.response
console.log(serverResponse, xhr.status)
const postsEl = document.getElementById("posts")
loadPosts(postsEl)
}
xhr.send(myFormData)
}
postCreateFormEl.addEventListener("submit", handlePostSubmit)
django views snippet:
def home_view(request, *args, **kwargs):
form = PostForm()
return render(request, "pages/home.html", context={'form': form})
def post_create_view(request, *args, **kwargs):
form = PostForm(request.POST or None)
next_url = request.POST.get("next") or None
if form.is_valid():
obj = form.save(commit=False)
obj.save()
if request.is_ajax():
return JsonResponse({"success": "Object created"}, status=201)
if next_url != None and is_safe_url(next_url, ALLOWED_HOSTS):
return redirect(next_url)
form = PostForm()
return render(request, 'components/form.html', context={"form": form})
On the first submit in the console it returns the event target and on the second on it returns a jsonresponse as intended. Any direction is appreciated!
edit:
home.html:
<form class='form' id='post-create-form' method='POST' action='/create-post'>
<input type='hidden' value='/' name='next'/>
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<input type='submit' value='submit'>
</form>
urlpatterns:
urlpatterns = [
path('admin/', admin.site.urls),
path('ckeditor/', include('ckeditor_uploader.urls')),
path('', home_view),
path('posts/<int:post_id>', post_detail_view),
path('posts', post_list_view),
path('create-post', post_create_view),
]
forms.py:
class PostForm(forms.ModelForm):
content = forms.CharField(widget=CKEditorUploadingWidget())
class Meta:
model = Post
fields = '__all__'
def clean_content(self):
content = self.cleaned_data.get("content")
if len(content) > MAX_POST_LENGTH:
raise forms.ValidationError("This post is too long")
return content

Most WYSIWYG editors don't edit the actual input element that will be submitted when the user gives input. Instead what they do is that they attach an event on the submission of such forms and then when the form is being submitted they set the value of the input. CKEditor is no exception to this and does the same.
Hence what is happening here is that you submit the form and your on submit handler fires first. Unfortunately this means that the input is never filled when you submit the form by ajax, but the input does get filled after your ajax call. Which is why your second submit is successful.
What you can do to resolve this is to make CKEditor update the values of your input on submission yourself. Modify your script like so and also make sure that the your script is somewhere below where you load the forms media:
const handlePostSubmit = function(e){
e.preventDefault()
for (instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].updateElement();
}
// Rest of your function
}

Related

stop dynamic add field to form when form is invalid

Followed below code to add and display etra fields to forms dynamically with "Add another" button. Code is working but problem is on "Add another" button click (when form in invalid) additional form fields are added to form but not displayed. Want to make sure when form is invalid extra fields are not added to form.
Forms
class MyForm(forms.Form):
original_field = forms.CharField()
extra_field_count = forms.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
extra_fields = kwargs.pop('extra', 0)
super(MyForm, self).__init__(*args, **kwargs)
self.fields['extra_field_count'].initial = extra_fields
for index in range(int(extra_fields)):
# generate extra fields in the number specified via extra_fields
self.fields['extra_field_{index}'.format(index=index)] = \
forms.CharField()
View
def myview(request):
if request.method == 'POST':
form = MyForm(request.POST, extra=request.POST.get('extra_field_count'))
if form.is_valid():
print "valid!"
else:
form = MyForm()
return render(request, "template", { 'form': form })
HTML
<form>
<div id="forms">
{{ form.as_p }}
</div>
<button id="add-another">add another</button>
<input type="submit" />
</form>
JS
<script>
let form_count = Number($("[name=extra_field_count]").val());
// get extra form count so we know what index to use for the next item.
$("#add-another").click(function() {
form_count ++;
let element = $('<input type="text"/>');
element.attr('name', 'extra_field_' + form_count);
$("#forms").append(element);
// build element and append it to our forms container
$("[name=extra_field_count]").val(form_count);
// increment form count so our view knows to populate
// that many fields for validation
})
</script>

Uncaught TypeError: FormData constructor: Argument 1 is not an object

Hi I have a fetch request to my php script but an error occurs when I try to submit form data via html. How can I solve my problem? thank
const formdata = new FormData(document.querySelector("form"));
const submit = function(e) {
e.preventDefault();
fetch('script.php', {
method: 'GET',
body: formdata,
})
.then(ans => ans.text())
.then(table => document.querySelector('#ans').innerHTML = table);
};
document.addEventListener('DOMContentLoaded', function(){
document.querySelector('#submitButton').addEventListener('click', submit);
});
<form id="form" method="GET">
<div class="inputX centered">
<label for><div>X:</div></label>
<div class="form_radio_group">
<div class="form_radio_group-item">
<input id="radio-1" type="radio" name="x_set" value="1" checked>
<label for="radio-1">1</label>
Your script is running before the document is rendered, so at the point where you call new FormData the form doesn't exist yet. Move it inside the submit handler, or better yet, just get it from the event:
const submit = function(e) {
const formdata = new FormData(document.querySelector("form"));
e.preventDefault();
fetch('script.php', {
or
const submit = function (e) {
const formdata = new FormData(e.target.form);
e.preventDefault();
fetch('script.php', {
I'd also recommend that you put an onsubmit on the form (or listen for submit events on the form) instead of listening for clicks on the submit button. This way your handler will fire even if the form is submitted via some other interaction, like hitting enter on an input, clicking a different submit button, etc.
To use FormData in a GET request without a body, construct a URLSearchParams instance and use its toString method to get a query string to append to your URL:
const formdata = new FormData(event.target);
const queryString = new URLSearchParams(formdata).toString();
fetch(`script.php?${queryString}`, {
It looks like the page does not have a form element present at the time the following line of code runs:
const formdata = new FormData(document.querySelector("form"));
If there are not any form elements in the page, querySelector will return null, which is not an object, which matches up with the error message. You can test by logging the form element (or null).
const form = document.querySelector("form");
console.log('form', form);

Django set form value equal to another form value in template

I want to get the following goal: I have two forms in one view, form1 and form2. The model are the following:
Class Model1(models.Model):
var_1=models.CharField()
var_2=models.CharField
Class Model2(models.Model):
var_1=models.CharField()
var_3=models.CharField
I have just set both form in the same view with a single submit button. Now I want to have the possibility to set var_1 from the form1 also for the var_1 of the Model2 (becouse are equal) when the client fill the form1. It's possible to get it?
This one my views.py
def example(request):
if request.method == 'POST':
form1 = Model1Form(request.POST)
form2 = Model2Form(request.POST)
if form1.is_valid() and form2.isvalid():
print("Il form รจ valido")
new_input1 = form1.save()
new_input2=form2.save()
else :
form1 = Model1Form()
form2 = Model2Form()
context= {
'form1': form1,
'form2':form2,
}
return render(request, "", context)
Suggest that you set the value of the field from the other form before saving.
if form1.is_valid() and form2.isvalid():
form2.cleaned_data['var_1'] = form1.cleaned_data['var_1']
form1.save()
form2.save()
I assume that you are not showing the var_1 field on both forms? If you were showing both this would overwrite the value of var_1 for form2.

Javascript OnClick before submit button

Im having an issue getting some bit of code to run before the page submits.
When i change the return value to true to submit the form, the code above doesn't run. The way it is now, the code runs and the page is refreshed. I want to pass a true variable to submit the form Any ideas?
function buildMessageC() {
//Create an ePOS-Print Builder object
var builder = new epson.ePOSBuilder();
//Create a print document
builder.addTextLang('en')
builder.addTextSmooth(true);
builder.addTextFont(builder.FONT_A);
builder.addTextSize(1, 1);
builder.addText(document.getElementById('receipt').textContent);
builder.addFeedLine(1);
builder.addCut(builder.CUT_FEED);
//Acquire the print document
var request = builder.toString();
//Set the end point address
var address = 'http://192.168.1.69/cgi-bin/epos/service.cgi?devid=counter_printer&timeout=10000';
//Create an ePOS-Print object
var epos = new epson.ePOSPrint(address);
//Send the print document
epos.send(request);
return false;
The form button
<sf:form onsubmit="return buildMessageC()">
<input class="addToOrderButton button blue large expand" value="Place Order" name="_eventId_placeOrder" type="submit"/>
</sf:form>
Clarification
function doSubmit(){
buildMessageC();
return false;
} gives me print out and reloads the same page not submiting the form
function doSubmit(){
buildMessageC();
return true;
} doesn't print yet submits the form

User input not saved in model with Javascript

Building a site so users can add topics onto a site and ask then Q&A. Using AJAX, the "Add Media" button on the home page loads my Add Media Form (text and an image) to the site. The box is rendering fine with the correct forms, but after hitting Submit the form doesn't save in the database. When just rendering the form onto a new HTML page without jquery, the model works just fine and the input is saved. How do I get the information submitted in a jquery lightbox to be saved in the database too?
So this is the html page
#home.html
Add Media
<div id="login-box" class="addmovie-popup">
This is the jquery that goes with it
#home.js
$(document).ready(function() {
$('.block3 a').click(function(ev) {
ev.preventDefault();
var url = $(this).attr('href');
$('#login-box').load('http://127.0.0.1:8000/home/add_movie/');
});
});
The URL conf
#urls.py
url(r'^add_media/$', 'add_media'),
The view for adding media
#views.py
def add_media(request):
if request.method == "POST":
form = MediaForm(request.POST, request.FILES)
if form.is_valid():
form.save(user = request.user)
return HttpResponseRedirect("/home//")
else:
form = MediaForm()
return render_to_response("qanda/add_media.html", {'form': form}, context_instance = RequestContext(request))
And the HTML form that it is rendering
#add_media.html
<h1> Add Media:</h1>
<form enctype = "multipart/form-data" action = "" method = "post">{% csrf_token %}
{{ form.as_p }}
<input type = "submit" value = "Add" />
<input type = "hidden" name = "next" value = "{{ next|escape }}" />
</form>
If you're loading HTML into your page dynamically action = "" would point to the current page, which clearly doesn't handle your POST requests.
Set the action to the correct URL.

Categories

Resources