Error label innerHTml disappears when image preview is loaded - javascript

I'm working on a django project where I need to permit the user to upload images to the server. Everything's fine except when I want to show an image preview before submitting.
I'm using django-crispy-forms to render the form:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ["image", "caption"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = "post"
self.helper.form_action = 'add-post'
self.helper.add_input(Submit("add-post", "Add Post", css_class='btn-primary btn-dark mt-3'))
This is the HTML template used for rendering the form:
<div class="row justify-content-center mt-4">
<div class="col-md-8">
{% crispy form %}
</div>
</div>
This based on the developer inspector in firefox yields the following input HTML:
<input type="file" name="image" class="custom-file-input" accept="image/*" id="id_image" required="">
<label class="custom-file-label text-truncate" for="id_image">---</label>
After hitting the submit button, the label gets filled with image name. That's exactly what I want.
However, as mentioned I wanted an image preview before submitting so I took look at the following stackoverflow answer and got what I want:
preview image before form submit
Show an image preview before upload
I managed to get an image preview using the following js and HTML:
<div class="row justify-content-center mt-4">
<img id="image-preview">
</div>
<div class="row justify-content-center mt-4">
<div class="col-md-8">
{% crispy form %}
</div>
</div>
JS:
document.addEventListener('DOMContentLoaded', function() {
// Show image preview before submitting
document.getElementById("id_image").onchange = function () {
var src = URL.createObjectURL(this.files[0]);
document.getElementById("image-preview").src = src;
}
})
and now for some reason the label disappears. I tried filling it while reading the image file in the js above but I failed.
Any help is appreciated.

Related

How to dynamically submit form input values with OnChange event using JQuery in django

I'm currently developing a simple true odd finder calculator using python, django and jquery. I need to have form input submit actions executed by jQuery as the user types in the input values. The goal is to get rid of submit buttons in the frontend html. As of nowadays calculator based web applications don't require submit buttons. The functionality behavior should look like here. I did a research and found out that i need to use JQuery. After implementing the functionality in my app, am able to type the first form input element, however upon clicking the second form input so as to start typing, my application crashes with server error 500, if i go back then type the second form input, it updates output.
How can i implement form input onChange using jquery to match the referenced functionality above.
My template and JQuery code
{% extends "base.html" %}
{% block title %}Two Way True Odd Finder{% endblock %}
{% block content %}
<script type="text/javascript" src = "https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('.form-control').change(function () {
$('#myform').submit();
});
});
</script>
<div class="container m-5">
Dashboard Home
3 Way True Odds Finder Calculator
</div>
<div class="container m-5 text-justify">
<div class="row">
<div class="col-4">
<form action="" method="post" id="myform">
{% csrf_token %}
<div class="mb-3">
<label for="Odd1" class="form-label">Odd 1</label>
<input type="number" class="form-control" name="odd1" id="Odd1" min=" " value=" " step=".001" required='required'>
</div>
<div class="mb-3">
<label for="Odd2" class="form-label">Odd 2</label>
<input type="number" class="form-control" name="odd2" id="Odd2" min=" " value=" " step=".001" required='required'>
</div>
<!--<button type="submit" class="btn btn-primary">Submit</button>-->
</form>
</div>
<div class="col-8">
<div class="row">
<div class="col-sm-6">
<div class="card shadow-sm p-3 mb-5 bg-white rounded">
<div class="card-body">
<h5 class="card-title">Results</h5>
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th scope="col">#</th>
<th scope="col">Odd1</th>
<th scope="col">Odd2</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Initial Odds With Juice</th>
<td>{{HomeOdd}}</td>
<td>{{AwayOdd}}</td>
</tr>
<tr>
<th scope="row">Implied Probability Win %</th>
<td>{{Home_implied_probability}}</td>
<td>{{Away_implied_probability}}</td>
</tr>
<tr>
<th scope="row">True Odds Without Juice</th>
<td class="text-success">{{Home_True_Odd}}</td>
<td class="text-success">{{Away_True_Odd}}</td>
</tr>
</tbody>
</table>
<div class="container text-justify">
<p>Total Implied probability is {{TotalImpliedProbability}}%</p>
<p>Inverted probability is {{Inverted_Probability}}%</p>
<p>Bookie juice is {{Juice}}%</p>
<p>True probability is {{True_Probability}}</p>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card shadow-sm p-3 mb-5 bg-white rounded">
<div class="card-body">
<h5 class="card-title">Enjoyed the calculator?</h5>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container bg-light p-5">
<h3>HOW IT WORKS</h3>
</div>
</div>
{% endblock %}
django view
def two_way_calc(request):
if request.method == 'POST':
odd1 = float(request.POST.get('odd1'))
odd2 = float(request.POST.get('odd2'))
func_def = odd_finder_true_2(odd1, odd2)
context = {
'Juice': func_def['Juice'],
'TotalImpliedProbability': func_def['TotalImpliedProbability'],
'HomeOdd': func_def['HomeOdd'],
'AwayOdd': func_def['AwayOdd'],
'Home_True_Odd': func_def['Home_True_Odd'],
'Away_True_Odd': func_def['Away_True_Odd'],
'True_Probability': func_def['True_Probability'],
'Home_implied_probability': func_def['Home_implied_probability'],
'Away_implied_probability': func_def['Away_implied_probability'],
'Inverted_Probability': func_def['Inverted_Probability'],
}
return render(request, 'three_way_temp.html', context)
else:
return render(request, 'three_way_temp.html', {})
urls.py
from django.urls import path
from .views import *
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('two_way_calc/', views.two_way_calc, name='two_way_calc'),
]
Python function behind the calculation
def odd_finder_true_2(first_odd, second_odd):
home_implied_probability = round((100/first_odd), 2)
away_implied_probability = round((100/second_odd), 2)
total_implied_probability = home_implied_probability + away_implied_probability
inverted = (100/total_implied_probability) * 100
juice = total_implied_probability - inverted
hundred_odd_home = total_implied_probability/home_implied_probability
hundred_odd_away = total_implied_probability/away_implied_probability
prob_true = 1/(round(hundred_odd_home, 2)) + 1/(round(hundred_odd_away, 2))
my_dict_two = {
'Juice': round(juice, 2),
'TotalImpliedProbability': round(total_implied_probability, 2),
'HomeOdd': first_odd,
'AwayOdd': second_odd,
'Home_True_Odd': round(hundred_odd_home, 2),
'Away_True_Odd': round(hundred_odd_away, 2),
'True_Probability': round(prob_true, 1),
'Home_implied_probability': home_implied_probability,
'Away_implied_probability': away_implied_probability,
'Inverted_Probability': round(inverted, 2)
}
return my_dict_two
so there are a few things that i should say before your answer
First of all it's better approach to use AJAX instead of submit your form because AJAX prevents the page from being reloaded and by using it you do not need to send a whole complete http request and receive a complete response
It will only send needed information to server and get exact answer and js helps you to inject that data in your page
Second you don't actually and necessarily need jQuery, you can do what you want in pure js using fetch API however jQuery is an option too
Now the answer
5xx Errors are server side errors and thank to you for sending your complete code i can show you error
So the problem is when you trigger submit event, you are changing one input and this causes your form to be submitted,
BUT, WHAT ABOUT SECOND INPUT?
it has no value and thus your server can't do the calculations right
How to fix this?
It's up to your algorithm, if you can find a default value to be set on the second input, it can solve the probe
Or
You can check it in you jQuery code
$(".form-control").on("change",function(){
if($("Odd2").value !== "")
$('#myform').submit()
}) ;
Also The jQuery code you've write is correct but it can be better
You are using change event which fires after the value is changed AND when user left the input (blur event)
You will get a better and more live result using input event
$(".form-control").on("input",...)
===============
Updated, based on your last comments, my guess was right and you want the answer to be shown to the user immediately after he entered the number
So you need to use another event called input here's the result (you can add a simple alert before that if to see how input event fires
$(".form-control").on("input",function(){
if($("Odd2").value !== "")
$('#myform').submit()
}) ;
Let me know if your code is changed a lot and provide me your new jQuery code if this didn't solve your problem
============
Updated
Again my guess was right and you need to prevent page from being reloaded (or redirected) this can be done by AJAX
you have multiple choices in AJAX
you can use pure JS (xmlHttpRequest object)
you can use Fetch API in js
you can use Axios.js which is a great library designed to handle Ajax requests and responses
Or finally you can use jQuery
Because in your project you are already using jQuery, I'll do it using jQuery but i don't approve it, Fetch is best option in my opinion, anyway :
$(".form-control").on("input",function(){
if($("Odd2").value !== "")
var fData = new FormData($('#myform'));
var jqxhr = $.ajax({
url: 'AddressToYourBackendFile',
method : 'POST',
data : fData
});
jqxhr.done( res => {
console.log('your response is ready :\n' + res);
});
}) ;

How to create a button to change a text in my html with data coming from Django's models.py?

I'm starting to work with Django and I'm starting a test to solidify what I've been learning. The idea is a single page, which displays a sentence as soon as the site opens. Below the phrase, there is a button that I would like to change the phrase to some other phrase coming from a variable declared in models.py and which contains several phrases that were registered through Django's admin panel.
This is my models.py file:
from django.db import models
class Base(models.Model):
criado = models.DateField('Criado', auto_now_add=True)
modificado = models.DateField('Atualização', auto_now=True)
ativo = models.BooleanField('Ativo', default=True)
class Meta:
abstract = True
class Frase(Base):
frase = models.CharField('Frase', max_length=100)
dica = models.CharField('Dica', max_length=200, default='-')
class Meta:
verbose_name = 'Frase'
verbose_name_plural = 'Frases'
def __str__(self):
return self.frase
This is my views.py file:
from django.views.generic import TemplateView
from .models import Frase
class IndexView(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['frases'] = Frase.objects.order_by('?').all()
return context
This is my index.html
<div class="container px-4 px-lg-5 h-100">
<div class="row gx-4 gx-lg-5 h-100 align-items-center justify-content-center text-center">
<div class="col-lg-8 align-self-end">
<h1 class="text-white font-weight-bold" id="frase">{{ frases|first }}</h1>
<hr class="divider" />
</div>
<div class="col-lg-8 align-self-baseline">
<a class="btn btn-primary btn-xl" onclick="nova_frase()">Nova frase</a>
</div>
</div>
</div>
(...) <!--rest of the html code-->
<script type="text/javascript">
function nova_frase() {
document.getElementById('frase').innerText = 'a';
}
</script>
"document.getElementById('phrase').innerText = 'a';" was my last test to try to create some change to the page through the button.
I tried using .innerHTML, but with no success either.
I'm having difficulties finding texts to do this task (click the button and change the phrase being displayed by another phrase coming from the variable defined in models.py).
I'm using Python 3.9.5 and Django 3.2.5.
If anyone can help me, I would be very grateful.
You can't directly change the text based on the frontend javascript event. First send all the buttons from Django to frontend template and store it in the javascript list. Then, use the javascript to display it based on the changes.
The another approach would be using Ajax.

Pre-populate current value of WTForms field in order to edit it

I have a form inside a modal that I use to edit a review on an item (a perfume). A perfume can have multiple reviews, and the reviews live in an array of nested documents, each one with its own _id.
I'm editing each particular review (in case an user wants to edit their review on the perfume once it's been submitted) by submitting the EditReviewForm to this edit_review route:
#reviews.route("/review", methods=["GET", "POST"])
#login_required
def edit_review():
form = EditReviewForm()
review_id = request.form.get("review_id")
perfume_id = request.form.get("perfume_id")
if form.validate_on_submit():
mongo.db.perfumes.update(
{"_id": ObjectId(perfume_id), <I edit my review here> })
return redirect(url_for("perfumes.perfume", perfume_id=perfume_id))
return redirect(url_for("perfumes.perfume", perfume_id=perfume_id))
And this route redirects to my perfume route, which shows the perfume and all the reviews it contains.
This is the perfume route:
#perfumes.route("/perfume/<perfume_id>", methods=["GET"])
def perfume(perfume_id):
current_perfume = mongo.db.perfumes.find_one({"_id": ObjectId(perfume_id)})
add_review_form = AddReviewForm()
edit_review_form = EditReviewForm()
cur = mongo.db.perfumes.aggregate(etc)
edit_review_form.review.data = current_perfume['reviews'][0]['review_content']
return render_template(
"pages/perfume.html",
title="Perfumes",
cursor=cur,
perfume=current_perfume,
add_review_form=add_review_form,
edit_review_form=edit_review_form
)
My issue
To find a way to get the review _id in that process and have it in my perfume route, so I can pre-populate my EditReviewForm with the current value. Otherwise the form looks empty to the user editing their review.
By hardcoding an index (index [0] in this case):
edit_review_form.review.data = current_perfume['reviews'][0]['review_content']
I am indeed displaying current values, but of course the same value for all reviews, as the reviews are in a loop in the template, and I need to get the value each review_id has.
Is there a way to do this, before I give up with the idea of allowing users to edit their reviews? :D
Please do let me know if my question is clear or if there's more information needed.
Thanks so much in advance!!
UPDATE 2:
Trying to reduce further my current template situation to make it clearer:
The modal with the review is fired from perfume-reviews.html, from this button:
<div class="card-header">
<button type="button" class="btn edit-review" data-perfume_id="{{perfume['_id']}}" data-review_id="{{review['_id']}}" data-toggle="modal" data-target="#editReviewPerfumeModal" id="editFormButton">Edit</button>
</div>
And that opens the modal where my form with the review is (the field in question is a textarea currently displaying a WYSIWYG from CKEditor:
<div class="modal-body">
<form method=POST action="{{ url_for('reviews.edit_review') }}" id="form-edit-review">
<div class="form-group" id="reviewContent">
{{ edit_review_form.review(class="form-control ckeditor", placeholder="Review")}}
</div>
</form>
</div>
Currently this isn't working:
$(document).on("click", "#editFormButton", function (e) {
var reviewText = $(this)
.parents(div.card.container)
.siblings("div#reviewContent")
.children()
.text();
$("input#editReviewContent").val(reviewText);
});
and throws a ReferenceError: div is not defined.
Where am I failing here? (Perhaps in more than one place?)
UPDATE 3:
this is where the button opens the modal, and underneath it's where the review content displays:
<div class="card container">
<div class="row">
<div class="card-header col-9">
<h5>{{review['reviewer'] }} said on {{ review.date_reviewed.strftime('%d-%m-%Y') }}</h5>
</div>
<div class="card-header col-3">
<button type="button" class="btn btn-success btn-sm mt-2 edit-review float-right ml-2" data-perfume_id="{{perfume['_id']}}" data-review_id="{{review['_id']}}" data-toggle="modal" data-target="#editReviewPerfumeModal" id="editFormButton">Edit</button>
</div>
</div>
<div class="p-3 row">
<div class=" col-10" id="reviewContent">
<li>{{ review['review_content'] | safe }}</li>
</div>
</div>
</div>
You can do this with jQuery as when you open the form, the form will automatically show the review content in there. It will be done by manipulating the dom.
Also, add an id to your edit button, in this example, I have given it an id "editFormButton".
Similarly, add an id to the div in which review content lies so that it is easier to select, I have given it an id "reviewContent"
Similarly, add an id to edit_review_form.review like this edit_review_form.review(id='editReviewContent')
<script>
$(document).on("click", "#editFormButton", function (e) {
var reviewText = $(this)
.parents("div.row")
.siblings("div.p-3.row")
.children("div#reviewContent")
.children()
.text();
$("input#editReviewContent").val(reviewText);
});
</script>
Don't forget to include jQuery.
Also, you can do it with pure javascript. You can easily search the above equivalents on google. This article is a good start!

Loading animation is not working

So I am using the following jQuery snippet to make a loading animation run (created in CSS) when a file is selected in an input field and then an upload button is clicked.
The form has 3 input fields with 3 upload buttons and is quite flexible. For e.g if a user selects a file in the first input field but clicks on the second upload button the loading animation should run around the first input field only. Another example is that if a user selects a file in the first input field and the second input field but clicks on the third upload button then the loading animation should run in the first and second input fields only.
$(document).ready(function() {
$(document).on("click", ".UploadBtn", function() {
$(".p").each(function(file) {
if ($(this).val()) {
$(this).next(".loader").show();
$(this).next(".loader").find(".spinner").show();
$(this).next(".loader").find(".UploadBtn").hide();
}
});
});
});
The code fails to run in the IF statement function. If i change the code (mentioned below) it will make the animation run but it ends up making the loading animation run in all 3 selection fields even if only one file was selected. This is not what is required but does tell me that the part of the code which has the IF statement breaks down in the code above;
$(document).ready(function() {
$(document).on("click", ".UploadBtn", function() {
$(".p").each(function(file) {
if ($(this).val()) {
$(".loader").show();
$(".spinner").show();
$(".UploadBtn").hide();
}
})
});
});
I am a beginner level coder and I have spent hours trying to fix this issue but nothing worked. Your help is greatly appreciated!
HTML snippet added as per recommendation
(It also has some Python code which was done by my friend) form.photo1, 2 and 3 have class = "p" ;
<div class="mtl mbl">
{{ form.photo1 }}
</div>
<div class="loader">
<div class="spinner"></div>
loading</div>
<input type="submit" class="UploadBtn btn bm bco mbs mts" style="border-color:#f57c00;" value="Upload">
<div class="mtl mbl">
{{ form.photo2 }}
</div>
<div class="loader">
<div class="spinner"></div>
loading</div>
<input type="submit" class="UploadBtn btn bm bco mbs mts" style="border-color:#f57c00;" value="Upload">
<div class="mtl mbl">
{{ form.photo3 }}
</div>
<div class="loader">
<div class="spinner"></div>
loading</div>
<input type="submit" class="UploadBtn btn bm bco mbs mts" style="border-color:#f57c00;" value="Upload">

Count images inside a div, then add another/new img src (file)

I'm currently using Flask for my back-end coding, and working on my front-end as of now. Below are the images uploaded on the server. Now I need to edit those images, like I need to upload another image or change the existing image. We're using a cropper.js on this one, and I don't know how will I manage this one because I'm not that good when it comes to front-end scripting like javascript/jquery/ajax. Maximum images can upload is up to 8 images, I need to count the total existing images, then add another img src file, for example if I had 3 images, then I need to show 5 more img src file for adding new images. Any help will do and will be appreciated. Below is my code on HTML with Jinja2 template.
<div class="col-xs-3">
<label class="rs-thumb rs-thumb-cover">
<div class="rs-thumb-content" id="inputImage1-wrap"><img src="{{ resource.image_url }}" alt="" id="inputImage1-pic" width="100%"><i class="fa fa-picture-o"></i>
<span class="rs-cover">Cover</span>
</div>
</label>
</div>
{% for imgs in resource.images[1:] %}
<div class="col-xs-3">
<label class="rs-thumb rs-thumb-cover">
<div class="rs-thumb-content" id="inputImage1-wrap"><img src="{{ imgs.image }}" alt="" id="inputImage1-pic" width="100%"><i class="fa fa-picture-o"></i>
<!-- <span class="rs-cover">Cover</span> -->
</div>
</label>
</div>
{% endfor %}
Image for Edit Module on front-end
Html + JQuery solution, mostly tested so should work.
HTML
<div class="imgBox">
<!-- Your content will appear here -->
</div>
JQuery
// Get the amount of images on the current page
var amountOfImg = $("img").length;
var amountToCount = 8;
for (var i = 0; i < amountToCount - amountOfImg; i++) {
$(".imgBox").append("<input type='file' name='fileInput' /> <b id='removeImg'>Remove</b>")
}
$("#removeImg").click(function() {
// When the user clicks on the #removeImg text, find the closest "img" element and remove it.
$(this).closest("img").remove();
});

Categories

Resources