I'm having some trouble capturing a change in the second dropdown of a cascading dropdown. Basically my set up is this: the user first picks an ETF from the first dropdown. Upon picking an ETF, a change function detects the selection, sends it to Python using AJAX and populates the second dropdown based on the holdings within the ETF (cascading). This part works perfect. For the second dropdown, I want the user to select a visual and then that selection would be sent to Python as well and then a graph would be produced. I have not gotten to that point yet, but I'm trying to just flash a message letting me know that it is working, but for some reason the second change function does not work.
Here is the HTML:
<body> id="body" name="body">
<div class="container">
<h1 align="center">Welcome to the Alpaca Pair Trading Application! </h1>
<div class="row">
<div class="col-md-6">
<label>Select ETF</label>
<select name="etfs" data-live-search="true" id="etfs" class="form-control" class="selectpicker" title="Select ETF">
{% for etf in etfs %}
<option value="{{etf}}">{{etf}}</option>
{% endfor %}
</select>
</div>
<div class="col-md-6">
<label>Select Visual</label>
<select name="visuals" data-live-search="true" id="visuals" class="form-control" class="selectpicker" title="Select Visual"> </select>
</div>
</div>
</div>
<br>
{% for message in get_flashed_messages() %}
<p> {{ message }} </p>
{% endfor %}
<br>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.1/js/bootstrap-select.min.js">
</script>
</body>
<script type="text/javascript">
$("#etfs").change(function() {
$.ajax({
type: "POST",
url: "{{ url_for('visuals') }}",
data: {
etf: $("#etfs").val()
},
success: function(data) {
var html = "";
for (var count = 0; count < data.length; count++) {
html += '<option value="' + data[count].id + '">' + data[count].visual + "</option>";
}
$("#visuals").html(html);
}
});
});
</script>
<script type="text/javascript">
$(document).on("change", "$visuals", function() {
$.ajax({
type: "POST",
url: "{{ url_for('graphs') }}",
data: {
visual: $("#visuals").val()
},
success: function(data) {
var html = "";
}
}
});
</script>
Here is the python code:
app=Flask(__name__)
#app.route("/")
def index():
etfs=['DIA','XLV','XLU','XLE','VNQ','GLD/SLV','QQQ','XLP','XLF']
return render_template("index.html", etfs=etfs)
#app.route("/visuals", methods=["POST","GET"])
def visuals():
etf_dict={'DIA':['UNH','GS','HD','MSFT','CRM'],'XLP':['PG','KO','PEP','WMT','COST'],
'XLV':['JNJ','UNH','PFE','ABT','ABBV'],'XLF':['BRK.B','JPM','BAC','WFC','C'],
'XLU':['NEE','DUK','SO','D','EXC'],'VNQ':['AMT','PLD','CCI','EQIX'],
'QQQ':['AAPL','MSFT','AMZN','GOOG','FB'],'XLE':['XOM','CVX','COP','MPC','SLB'],
'GLD/SLV':['GLD','SLV']}
if request.method == 'POST':
etf = request.form['etf']
holdings=etf_dict[etf]
options_list=[]
if etf=='GLD/SLV':
options_dict={}
options_dict['id']=etf
options_dict['visual']='GLD vs SLV'
options_list.append(options_dict)
else:
for i in holdings:
options_dict={}
options_dict['id']=etf
options_dict['visual']=i+' vs '+etf
options_list.append(options_dict)
options_list.append({'id':etf,
'visual': 'All Holdings vs ' +etf})
return jsonify(options_list)
#app.route("/graphs", methods=["POST","GET"])
def graphs():
print("inside graphs function")
if request.method == 'POST':
print("received post request from front end")
graph = request.form['visual']
flash("This worked! The graph chosen is" + graph)
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Here is what the web app screen looks like. As you can see, when I select "XLE", it updates the second dropdown correctly, so I know the change function is working properly for the first dropdown.
Web App dropdowns
The problem is when I select a visual in the second dropdown, the other change function does not seem to be running..
I figured out that I should be using a form tag to submit the data from each of the dropdowns instead of trying to detect a change in the second dropdown..
Here is the updated HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Alpaca Pair Trading App</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.1/css/bootstrap-select.min.css">
<link rel="stylesheet" href={{url_for( 'static',filename='css/main.css' )}}>
</script>
</head>
<body>
<form action='/graphs' method='POST'>
<div class="container">
<h1 align="center">Welcome to the Alpaca Pair Trading Application! </h1>
<div class="row">
<div class="col-md-6">
<label>Select ETF</label>
<select name="etfs" data-live-search="true" id="etfs" class="form-control" class="selectpicker" title="Select ETF">
{% for etf in etfs %}
<option value="{{etf}}">{{etf}}</option>
{% endfor %}
</select>
</div>
<div class="col-md-6">
<label>Select Visual</label>
<select name="visuals" data-live-search="true" id="visuals" class="form-control" class="selectpicker" title="Select Visual"> </select>
</div>
</div>
</div>
<br>
<div class="container">
<div class="row">
<div class="col-md-4">
<input type="submit" name="next" value="Go" class="btn btn-primary">
</div>
</div>
</div>
</form>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.1/js/bootstrap-select.min.js">
</script>
<script type="text/javascript">
$("#etfs").change(function() {
$.ajax({
type: "POST",
url: "{{ url_for('visuals') }}",
data: {
etf: $("#etfs").val()
},
success: function(data) {
var html = "";
for (var count = 0; count < data.length; count++) {
html += '<option value="' + data[count].visual + '">' + data[count].visual + "</option>";
}
$("#visuals").html(html);
}
});
});
</script>
</body>
</html>
I'm trying to avoid inline javascript and would like to convert it to Alpine.js code. Is there a way to rewrite the following piece of code in Alpine.js?
<script type="text/javascript">
window.addEventListener('DOMContentLoaded', function () {
const message = "Do you really want to remove the selected e-mail address?";
const actions = document.getElementsByName('action_remove');
if (actions.length) {
actions[0].addEventListener("click", function (e) {
if (!confirm(message)) {
e.preventDefault();
}
});
}
});
document.addEventListener('DOMContentLoaded', function () {
$('.form-group').removeClass('row');
})
</script>
Here is the full context (I'm working with Django templates):
{% extends "account/base.html" %}
{% load tailwind_filters %}
{% load crispy_forms_tags %}
{% block head_title %}
Account
{% endblock %}
{% block inner %}
<h1>E-mail Addresses</h1>
{% if user.emailaddress_set.all %}
<p>The following e-mail addresses are associated with your account:</p>
<form action="{% url 'account_email' %}" class="email_list" method="post">
{% csrf_token %}
<fieldset class="blockLabels">
{% for emailaddress in user.emailaddress_set.all %}
<div class="radio">
<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
<input id="email_radio_{{forloop.counter}}" type="radio" name="email" {% if emailaddress.primary or user.emailaddress_set.count == 1 %}checked="checked" {%endif %} value="{{emailaddress.email}}" />
{{ emailaddress.email }}
{% if emailaddress.verified %}
<span class="verified">Verified</span>
{% else %}
<span class="unverified">Unverified</span>
{% endif %}
{% if emailaddress.primary %}<span class="primary">Primary</span>
{% endif %}
</label>
</div>
{% endfor %}
<div class="form-group">
<button class="secondaryAction btn btn-primary" type="submit" name="action_primary">Make Primary</button>
<button class="secondaryAction btn btn-primary" type="submit" name="action_send">Re-send Verification</button>
<button class="primaryAction btn btn-primary" type="submit" name="action_remove">Remove</button>
</div>
</fieldset>
</form>
{% else %}
<p><strong>Sad news:</strong>You currently do not have any e-mail address set up. You should add an e-mail address so you can receive notifications, reset your password, etc.</p>
{% endif %}
<h2>Add E-mail Address</h2>
<form method="post" action="{% url 'account_email' %}" class="add_email">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" name="action_add" type="submit">
Add E-mail
</button>
</form>
{% endblock %}
{% block inline_javascript %}
{{ block.super }}
<script type="text/javascript">
window.addEventListener('DOMContentLoaded', function () {
const message = "Do you really want to remove the selected e-mail address?";
const actions = document.getElementsByName('action_remove');
if (actions.length) {
actions[0].addEventListener("click", function (e) {
if (!confirm(message)) {
e.preventDefault();
}
});
}
});
document.addEventListener('DOMContentLoaded', function () {
$('.form-group').removeClass('row');
})
</script>
{% endblock %}
You can try something like this:
Initialize the parent form element with x-data and set the state variable confirmMsg to null.
On form submit you prevent the actual submit with #submit.prevent and check whether a confirm message (confirmMsg) was set. If yes, you prompt the user to confirm the set message. If the users confirms, you reset the confirmMsg to null and submit the form with $el.submit().
On the buttons, you can just set the respective confirmMsg with #click = "confirmMsg = 'Are you sure?'".
Here is a code example:
<script src="//unpkg.com/alpinejs" defer></script>
<form
x-data="{confirmMsg: null}"
#submit.prevent="
if (confirmMsg && !confirm(confirmMsg)) return;
confirmMsg = null;
alert('Submitting form...'); $el.submit()"
>
<button
#click="confirmMsg = 'Do you really want to remove the selected e-mail address?'"
type="submit"
name="action_remove"
>
Remove
</button>
</form>
I have a lot of forms on the page and when one of them is submitted I want to send request via ajax to the view and have an id of the article and other info. So I need to check if form that has been clicked is the same as event.target. I did something like this but don't know if it is correct(first console.log works but second not):
<div id = "list">
{% for article in news %}
<h1>{{ article.title }}</h1>
<p>{{ article.published }}</p>
<img src = "{{ article.url }}">
<p>
<button>Upvote</button>
<button>Downvote</button>
</p>
<div id="span">
{% with article.upvotes.count as total_upvotes and article.downvotes.count as total_downvotes %}
<span upvote-id = "{{ article.id }}">{{ total_upvotes }}</span><span> upvote{{ total_votes|pluralize}}</span>
<span downvote-id = "{{ article.id }}">{{ total_downvotes }}</span><span> downvote{{ total_votes|pluralize}}</span>
{% endwith %}
</div>
<form method = 'post' action = '{% url "news:news_list" %}' form-id = '{{ article.id }}' class="form">
{{ form.as_p }}
{% csrf_token %}
<input type = "submit" value = "post">
</form>
{% endfor %}
</div>
{% endblock %}
{% block domready %}
const
list = document.getElementById('list'),
items = document.getElementsByClassName('vote');
forms = document.getElementsByClassName('form');
list.addEventListener('click', voteFunc);
list.addEventListener('submit', commentFunc);
function commentFunc(event){
event.preventDefault();
const clickedForm = event.target;
console.log('event triggered');
for (let form in forms){
if (form == clickedForm){
console.log('form is event.target')
$.ajax({
url: '{% url "news:news_list" %}',
type: 'POST',
data: {'id':$(event.target).attr('form-id'), 'title':$(this).elemets['title_field'].text(), 'body':$(this).elemets['body_field'].text()},
dataType: 'json'
})
}
}
}
Hope to hear advice how to implement it better and what event.target contains
You can write event handler for form submit event .So, whenever submit button(post) is clicked this event will get called then use .serialize() method to get all inputs inside your form and also attach form-id using &name=value and then you can pass same to backend.
Demo Code :
//when form will get submit
$("form.form").submit(function(e) {
//serialize will get all inputs as name=value separted wth `& `
console.log("data to send --> " + $(this).serialize() + "&id=" + $(this).attr('form-id'))
$.ajax({
type: "POST",
url: '{% url "news:news_list" %}',
data: $(this).serialize() + "&id=" + $(this).attr('form-id'), //send same
dataType: 'json'
});
e.preventDefault();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="list">
<a href="{{ article.resource }}">
<h1>A1</h1>
</a>
<p>abcd..</p>
<img src="{{ article.url }}">
<p>
<button>Upvote</button>
<button>Downvote</button>
</p>
<div id="span">
<span upvote-id="1">23</span><span> 54</span>
<span downvote-id="1">2</span><span> 56</span>
</div>
<form method='post' action='{% url "news:news_list" %}' form-id='1' class="form">
<p><label>somehting:</label>
<input type="text" name="something"></p>
<input type="submit" value="post">
</form>
<a href="{{ article.resource }}">
<h1>A</h1>
</a>
<p>abcd..</p>
<img src="{{ article.url }}">
<p>
<button>Upvote</button>
<button>Downvote</button>
</p>
<div id="span">
<span upvote-id="2">23</span><span> 54</span>
<span downvote-id="2">2</span><span> 56</span>
</div>
<form method='post' action='{% url "news:news_list" %}' form-id='2' class="form">
<p><label>somehting:</label>
<input type="text" name="something"></p>
<input type="submit" value="post">
</form>
</div>
So I have been trying to implement a way to post a project post that is able to upload multiple images at the same time.
Both of my forms are not getting validated even tho they are correct and complete.
I am not sure what am I doing wrong.
My codes are below:
views.py
class CreateProjectsView(View):
def get(self, request):
p_photos = P_Images.objects.all()
#project_form = ProjectsForm(initial=self.initial)
project_form = ProjectsForm()
context = {
'p_photos': p_photos,
'project_form': project_form,
}
return render(self.request, 'projects/forms.html', context)
def post(self, request):
project_form = ProjectsForm(request.POST, request.FILES)
multi_img_form = P_ImageForm(request.POST, request.FILES)
if project_form.is_valid() and multi_img_form.is_valid():
instance = project_form.save(commit=False)
instance.user = request.user
instance.save()
images = multi_img_form.save(commit=False)
images.save()
data = {
'is_valid': True,
'name': images.p_file.name,
'url': images.p_file.url
}
else:
data = {
'is_valid': False,
}
return JsonResponse(data)
forms.html
{% extends "projects/test.html" %}
{% block javascript %}
<form action="{% url 'create_post:create_projects' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for hidden in project_form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in project_form %}
{{ field.errors }}
{{ field }} <br />
{% endfor %}
<input type="submit" value="OK">
{% load static %}
{# JQUERY FILE UPLOAD SCRIPTS #}
<script src="{% static 'projects/js/jquery-file-upload/vendor/jquery.ui.widget.js' %}"></script>
<script src="{% static 'projects/js/jquery-file-upload/jquery.iframe-transport.js' %}"></script>
<script src="{% static 'projects/js/jquery-file-upload/jquery.fileupload.js' %}"></script>
{# PHOTOS PAGE SCRIPTS #}
<script src="{% static 'projects/js/basic-upload.js' %}"></script>
{# 1. BUTTON TO TRIGGER THE ACTION #}
<button type="button" class="btn btn-primary js-upload-photos">
<span class="glyphicon glyphicon-cloud-upload"></span> Upload photos
</button>
{# 2. FILE INPUT TO BE USED BY THE PLUG-IN #}
<input id="fileupload" type="file" name="p_file" multiple
style="display: none;"
data-url="{% url 'create_post:create_projects' %}"
data-form-data='{"csrfmiddlewaretoken": "{{ csrf_token }}"}'>
{# 3. TABLE TO DISPLAY THE UPLOADED PHOTOS #}
<table id="gallery" class="table table-bordered">
<thead>
<tr>
<th>Photo</th>
</tr>
</thead>
<tbody>
{% for p_photo in p_photos %}
<tr>
<td>{{ p_photo.file.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<h1>hahahaha</h1>
</form>
{% endblock %}
basic-upload.js
$(function () {
/* 1. OPEN THE FILE EXPLORER WINDOW */
$(".js-upload-photos").click(function () {
$("#fileupload").click();
});
/* 2. INITIALIZE THE FILE UPLOAD COMPONENT */
$("#fileupload").fileupload({
dataType: 'json',
done: function (e, data) { /* 3. PROCESS THE RESPONSE FROM THE SERVER */
if (data.result.is_valid) {
$("#gallery tbody").prepend(
"<tr><td><a href='" + data.result.url + "'>" + data.result.name + "</a></td></tr>"
)
}
}
});
});
No errors are getting posted are shown on my terminal. Its just that nothing simply happens. Nothing is getting uploaded to my database.
I have a problem with my web site.
I have a button that displays the form and the problem is that on IE and on the edge when I click this button it shows me the form but refreshed the page automatically after the click.
I do not know how to solve this problem. Can you help me please ?
Here is my code html :
<div class="col-xs-3" id="div6">
<div style="text-align: center; color: #333; font-weight:bold;">Actualité</div>
{% if user.is_authenticated %}
{% if request.user.is_staff %}
<form action="#modification">
<div style="text-align: center;">
<button type="submit" id="modifier" class="btn btn-primary">Modifier l'actu</button>
</div>
</form>
<form action="#modification" id="formActu" style="display: none" method="post">
{% csrf_token %}
<div id="modification"></div>
<div style="text-align: center;">
<button type="submit" class="btn btn-success" id="valider" style="margin-top: 1%;">Valider</button>
</div>
</form>
{% endif %}
{% endif %}
{% if not actu %}
<div id="messageNonActu" style="margin-top: 5%;">Pas d'actualité pour le moment !</div>
{% endif %}
{% for actuCommentaire in actu %}
<div id="commentaireActu" style="margin-top: 5%;">{{actuCommentaire.commentaire}}</div>
{% endfor %}
</div>
Here is my script :
$(document).ready(function(){
var compteur = 0;
$('#modifier').click(function(){
if(compteur === 0){
$('#modification').append('{% for field in form %}<label class="my_class" for="{{ field.name|escapejs }}">{{ field.label|escapejs }} :</label>{{ field|escapejs }}{% endfor %}');
$('#formActu').show()
$('#exampleTextarea').css({resize: 'none'});
$('#commentaireActu').hide();
$('#messageNonActu').hide();
$('#modifier').removeClass('btn btn-primary').addClass('btn btn-danger');
$('#modifier').html("Annuler la modif");
compteur++;
}else{
if($('#formActu').is(':visible')){
$('#formActu').hide();
$('#commentaireActu').show();
$('#modifier').removeClass('btn btn-danger').addClass('btn btn-primary');
$('#modifier').html("Modifier l'actu");
}else{
$('#commentaireActu').hide();
$('#formActu').show();
$('#modifier').removeClass('btn btn-primary').addClass('btn btn-danger');
$('#modifier').html("Annuler la modif");
}
}
});
});
Image 1 with just button
After clicking I have a preview of the second image but it refreshed the page in less than a second after clicking on the button
Image 2 with form
Then I get the visual of the first image directly
Try this and check:(Just return false from the end of method because IE considers button click as submit that's why your page reloads)
$('#modifier').click(function(){
//your logic
return false;
}