Show modal on form validation success - html

I'm trying to launch a modal if my flask-wtf form validation fails. The code snippet below otherwise works as expected other than it launches the modal even when I load the form before submission (appears that form.errors initialises to False before form is submitted).
What do I need to change get the modal to show iff form is submitted and form validation is successful?
<script>
var formSuccess = {% if form.errors %}false{% else %}true{% endif %};
$(document).ready(function() {
if (formSuccess) {
$('.modal').modal('show');
}
});
</script>

I resolved this by passing a validation_success variable when checking
if form.validate_on_submit() within the calling routine. I then pick this value up using jija:
<script>
var formSuccess = {% if validation_success %}true{% else %}false{% endif %};
$(document).ready(function() {
if (formSuccess) {
$('.modal').appendTo("body").modal('show');
}
});
</script>

Related

Getting selected value from dropdown menu to remain when page refreshes

I am trying to figure out a way in Django to maintain the selected value in a dropdown menu, after the page refreshes. Here is the current code I have. Whenever I make a selection the page refreshes, showing the correct data associated with the selection, but defaults to the name of the top selection in the dropdown menu. Thanks for the help.
<html>
<form method="GET" action=".">
<select name="option-menu" id="option-menu">
<option value="" disabled selected>Choose a Name</option>
{% for d in database %}
<option value="{{d.name}}">{{d.name}}</option>
{% endfor %}
</select>
<button type="submit" class="btn-filter">Filter</button>
</form>
</html>
--views.py--
option_menu = request.GET.get("option-menu")
qs = Database.objects.filter(database_id=id)
if option_menu != '' and option_menu is not None:
qs = qs.filter(name__icontains=option_menu)
return render(request,
template_name='main/database.html',
context={"queryset": qs
})
You could create a function in your template that activates periodically, or on the click of a specific element that sends the selection to the view which saves it to a cache file.
Function to check the selection:
<script>
function saveSelection() {
var selection
selection = $('#option-menu').val()
dict = {}
dict[selection] = selection
$.ajax({
url: '//' + '' + '/' + '' + "/",
type: 'POST',
headers: {'X-CSRFtoken': '{{ csrf_token }}'},
data: dict,
dataType: 'json'
})
}
</script>
You'll need to populate your own url in here, I couldn't glean it from your code above
In your view, you have a if statement to handle POST requests to save the selection, and you pass the selection into your context with a try block
try:
savedInfo = {}
csv_reader=csv.DictReader(saveFile)
for row in csv_reader:
if row['Selection'] == "Your selection text index 0":
selection = 0
elif row['Selection'] == "Your selection text index 1":
selection = 1
savedInfor[row['Selection']] == row['Selection']
context['saveInfo'] = savedInfo
except:
pass
if request.method == "POST":
data = request.POST
#save data
Then, on page load, you need a function to change the selection from the default value to the saved one
{% for info, value in saveInfo.items %}
<script>
$('document').ready(function(){
$('#option-menu').prop('selectedIndex', '{{value.1}}'
</script>
This probably isn't a perfect method for this, but it may push you in the right direction

multiple form generated jquery doesn't submit

I'm building a website (e-commerce like) with Django.
At some point I display a list of items and for each item there is a form with submit button Order and Quantity picker.
I've implemented filter function that delete the html code of my items list and rebuild it with the matching items with jquery.
The new forms generated by this function do nothing when the submit button is clicked
Here is a part of the code I use in my function (I use an ajax call to generate a json of matching items and then I display them in the table list) :
$.each(code_json, function(index, value){
var string = "<tr id="+ value.material +"><td>"+ value.manufNo +"</td><form method='POST' action='/add_to_cart/"+value.material+"/"+ value.node+"/{{language}}'><td><input type='submit' class='btn' value='Commander' style='color:blue;'></td><td><input id='qty' type='number' name='qty' class='form-control' value='1'></td></form></tr>";
$("#header").after(string);
});
I know that usually with Django you have to add {% csrf_token %} for each form. However it throw an error page usually when missing and here it doesn't show anything
Edit : I tried to bind an onclick event on the submit button dynamically created. In this I did a $.post in jquery to simulate the submit of the form but nothing happend
$(document).on('click', '.btnStandard', function(event) {
console.log($(this).attr('id'));
$.post('/add_to_cart/'+$(this).attr('id'),
{
qty: $("#qty"+$(this).attr('id')).val()
},function(data,status,xhr){
alert("Data : "+data+", status: "+status+", xhr: "+xhr);
});
It print in console $(this).attr('id') but it doesn't do anything else
Thank you for your help
It doesn't explain why I have this problem but I found a workaround to solve my problem.
Instead of dynamically generate forms, I generate them with template and then I hide them all. Later, I make those I need visible when I need thanks to css.

How do I call a button once without going into an infinite loop (Angular)?

Maybe the title is not very clear but here is what I have.
I have a scenario where I have a button in HTML, I call the service when the button is clicked and I want HTML B to know when the button from HTML A is clicked.
A.html
<button (click)="passData(a,b,c)">
B.html
<div *ngIf="fromA()" >
{{ showResult() }}
</div>
A.component.ts
passData(a: string, b:string, c:string) {
this.Xservice.getData.subscribe(data => res = data);
}
B.compoent.ts
fromA() {
this.xService.subscribe( data => res = data ); // This is observable
if (res !== undefined ) {
console.log("true returned");
return true;
}
}
So from this above I have checked the console and it is flooded with logs "true returned" and I was wondering how do I just to call this once when button from A.html is pressed?
You don't need to use *ngIf. You can just have
fromA():string {
if // Whatever logic you do
return "block";
else return "none";
}
Now I'm assuming what you mean is you want HTML B to only show when fromA() is clicked. So you can do this:
<div [display] = "fromA()">
{{ showResults() }}
</div>
Basically, you just set the property display to whatever result fromA() has. If the button is not clicked (returning none), then the HTML is not displayed (display=none). If it is clicked (returning inline), then the HTML is displayed (display=inline).
Of course, you could use any other display properties besides inline to show the HTML.

Using Ajax in django template to update an element

So I have a button that is suppose to "Add" an "Item" to a list and update the list on the same page without refreshing it.
Adding the Item without redirecting worked. but the element is not refreshed properly
Here is my script:
<div id="list">
{% for item in items %}
{{ item.name }}
{% endfor %}
</div>
<button id="add" type="submit">+</button>
<script>
$('#add').click(function () {
$.get('/url/page/', function (data) {
$('#list').html(data);
});
});
</script>
views.py
def add_to_list(request, item_id, list_id):
item, created = List.objects.get_or_create(list_id=list_id,
item_id=item_id)
return HttpResponse(request)
I feel like I have to add something in the views? and maybe use json code?
You should append the result of your request to the div element. Try this:
$('#add').click(function () {
$.get('/url/page/', function (data) {
$('#list').append(data);
});
});
Assuming that data that you receive from GET request is not an object but just a string. If it is an object add data.name:
$('#list').append(data.name)
Hope it helps.

How can I process HTML form validation before reCAPTCHA's validation?

I integrated the new hidden reCAPTCHA (v2) framework which by default verifies the user with the click event of the submit button. But this event is triggered before the built-in HTML form validation. I am looking for a way to make it in the expected order: form validation first, reCAPTCHA after.
You have to do it programmatically thanks to a new v2 grecaptcha method: grecaptcha.execute() so that recaptcha doesn't replace the button's default click event which was preventing the default HTML5 form validation.
The event path is:
Submit button click event: browser built-in form validation
Form submit event: call grecaptcha.execute()
reCAPTCHA callback: submit the form
$('#form-contact').submit(function (event) {
event.preventDefault();
grecaptcha.reset();
grecaptcha.execute();
});
function formSubmit(response) {
// submit the form which now includes a g-recaptcha-response input
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js"></script>
<form action="?">
<div class="g-recaptcha"
data-sitekey="your-key"
data-size="invisible"
data-callback="formSubmit">
</div>
<button type="submit">Submit</button>
</form>
Here is my solution to get HTML5 validation + Invisible recaptcha:
HTML:
<form id="my-form">
<!-- Your form fields ... -->
<div class="g-recaptcha"
data-sitekey="..."
data-callback="submitMyForm"
data-size="invisible">
</div>
<button type="submit">Submit</button>
</form>
JS:
var myForm = $('my-form');
function submitMyForm () {
myForm.trigger('submit', [true]);
}
$(function () {
myForm.on('submit', function (e, skipRecaptcha) {
if(skipRecaptcha) {
return;
}
e.preventDefault();
grecaptcha.execute();
});
})
Hi got a working solution here. Working with invisible Recaptcha.
jQuery(document).ready(function() {
var commentform = jQuery("#commentform");
commentform.on("click", "#submit-comment", function(e) {
if(commentform[0].checkValidity()) {
e.preventDefault();
grecaptcha.execute();
}
});
});
function submitCommentForm(data) {
document.getElementById("commentform").submit();
}
<form action="blaba.php" method="post" id="commentform" class="comment-form">
<div class="form-submit">
<div data-callback="submitCommentForm" data-sitekey="yourkey" class="g-recaptcha" data-size="invisible">
<button id="submit-comment">Leave a comment</button>
</div>
</form>
I had this problem as the default method seems to override the html5 form validation. I also wanted all code to be generic rather than hard coding any functions/element names. In the end I came up with the following code using the v3 api -
HTML
<form method="post" action="?" class="ui-recaptcha" name="my_form_name">
...
<input type="submit" value="Submit">
</form>
<script src="//www.google.com/recaptcha/api.js?render={key}" async defer></script>
Javascript (I'm using jQuery but would be fairly easy to adapt to vanilla js)
$('.ui-recaptcha').submit(e => {
var form = e.target;
if( $(form).data('recaptcha-done') )
return;
e.preventDefault();
grecaptcha.execute('{key}', {'action': $(form).attr('name')}).then(token => {
$(form).append($('<input>').attr({'type': 'hidden', 'name': 'g-recaptcha-response', 'value': token}));
$(form).data('recaptcha-done', true);
$(form).submit();
});
});
I found that just calling submit as in some examples above caused a loop for me, which would make sense seeing as the recaptcha handler runs on the submit event.
This runs recaptcha for any ui-recaptcha form, passes the form name attribute as the action which can be seen in reCaptcha console, and then inserts the token into the form. Once run it sets a data attribute on the form so the recursive call to submit doesn't try to run recaptcha again.
Here's my solution.
Uses reCaptcha v3 (invisible) docs
Uses native HTML5 form validation
Uses pure JS
Uses standard POST processing (can be modified to AJAX)
Add as many forms as needed, just change the 'UNIQUE_FORM_ID' in the two places, and update the POST_URL for the form.
Ensure you use your own key in the locations of 'RECAPTCHA_SITE_KEY'.
<form id="UNIQUE_FORM_ID" method="post" action="POST_URL">
<!-- ** Notice ** this hidden input field that will later send our g-recaptcha token back to our server -->
<input type="hidden" name="g-recaptcha-response" value="">
<!-- Add other hidden nonce fields -->
<!-- Required field -->
<input name="fullname" type="text" placeholder="Full Name" required>
<!-- Submit button -->
<!-- ** Notice ** the 'form' attribute; using SAME value as it's parent's form id, above. -->
<!-- ** Notice ** the 'onclick' attribute; be sure to pass event -->
<button type="submit" form="UNIQUE_FORM_ID" onclick="formSubmitBtn(event)">Send</button>
</form>
<!-- Only add scripts once -->
<!-- ** Notice ** to manually call grecaptcha, our site key must be included when loading api.js using the 'render' query param -->
<script src="https://www.google.com/recaptcha/api.js?render=RECAPTCHA_SITE_KEY"></script>
<script>
/**
* Handles form submissions for Google recaptcha v3.
* Allows for HTML5 form validation to complete before processing.
*/
function formSubmitBtn($event) {
/**
* Checks the validity of the form.
* Return if invalid; HTML5 validation errors should display.
*/
if (!$event.target.form.checkValidity()) {
return;
}
/**
* Form is client-side valid; taking over the remainder of processing.
*/
$event.preventDefault();
grecaptcha.ready(function() {
grecaptcha.execute("RECAPTCHA_SITE_KEY", { action: 'submit' }).then(function(token) {
/**
* Adds the token g-recaptcha-response token to our hidden form element.
* ** Notice ** we our referencing the specific form's input element by name here (do not use IDs).
*/
$event.target.form.elements['g-recaptcha-response'].value = token;
/**
* Use the form API directly to submit the form.
*/
$event.target.form.submit();
});
});
}
</script>
let siteKey = "...";
$("form").submit(function (eventObj) {
var myForm = this;
eventObj.preventDefault();
grecaptcha.execute( siteKey, {
action: "submit"
})
.then(function (token) {
$('<input />').attr('type', 'hidden')
.attr('name', "g_recaptcha_response")
.attr('value', token)
.appendTo(myForm);
myForm.submit();
});
});
This will execute recapcha, wait for response, add hidden attribute g_recaptcha_response to any form when browser try to submit it and then actually submit it. You need global variable siteKey
I was wanting the same behavior, but using the new recaptcha, the invisible one. After looking at some code and testing some stuff, I got into this. The main difference is that this uses the default browser validation as well:
var contact_form;
$(function() {
contact_form = $('#contact-form');
contact_form.submit(function (event) {
if ( ! contact_form.data('passed')) {
event.preventDefault();
grecaptcha.execute();
}
});
});
function sendContactForm(token) {
contact_form.data('passed', true);
contact_form.submit();
}
It basically stores the jquery form object in a global var, including, it uses sendContactForm as the callback, but when called by the recaptcha, it sets a data var named passed, which allows the form to not be prevented. It's exactly the same behavior as recaptcha would normally do, but with that condition.
Update: re-looking at my code right reminds me that it probably needs a way to restore data passed to false after grecaptcha's execution. Consider that if you'll implement this.
This solution is similar to solution by #PigBoT but with the addition of reportValidity() and is using ReCAPTCHA v3
Credit to https://github.com/ambethia/recaptcha/issues/302#issuecomment-621794131
<script src="https://www.google.com/recaptcha/api.js"></script>
<script type="text/javascript">
function contactOnSubmit(token) {
var contactForm = document.getElementById('contactUs');
if(contactForm.checkValidity()) {
//SERVER SIDE VALIDATION here,
//on success, contactForm.submit();
} else {
grecaptcha.reset();
contactForm.reportValidity();
}
}
</script>
Form (id="contactUs")
<button class="g-recaptcha" data-sitekey="..." data-callback="contactOnSubmit" data-action="submit">Submit</button>
"Can I Use" site currently reports 97% uses have support for checkValidity() https://caniuse.com/?search=checkValidity