I have a form in my Flask app. I need the form submission to be mediated by Javascript (JS). The desired behavior is that on submission of the form, we get an immediate redirect, then JS talks to Flask, Flask sends a response, JS updates the DOM.
Problem: The form doesn't submit to JS. On submission, the text in the input box clears, and the page sits there. No errors log to the console
It appears to ignore my JS, though the <script> tags are in order, their placement in layout.html is correct, and http://localhost:5000/static/index.js displays the file. I inserted a console.log statement to test if the appropriate JS function was being fired. Got nothing. I enclosed everything in $(document).ready(). Still nothing. Placing a method="post" attribute in the <form> tag and a name attribute in the form's input box allowed me to submit directly to Flask, but I need it to go to JS. All of this is playing out on my machine, so no CORS. Where am I going wrong?
Form that must go to JS then Flask
<div class="container">
<div class="display-3">
<strong>Create A Channel</strong>
</div>
<form id="channelForm" class="mt-4">
<label for="channelName">Channel Name</label>
<input id="channelName" class="form-control" type="text" placeholder="Enter Channel Name">
<input type="submit" class="btn btn-primary" value="Create Channel">
</form>
</div>
Abbreviated JS
document.querySelector('#channelForm').onsubmit = () => {
console.log("here");
window.location.href = 'messages.html';
// other stuff that links to Flask route
};
Flask route
#app.route('/channels', methods=["GET", "POST"])
def channels():
channelList = []
if request.method == "POST":
channel = request.form.get("channel")
if not (channel is None) and not (channel in channelList):
channelList.append(channel)
print(channelList)
return jsonify({"success": True, "channel":channelList})
else:
return jsonify({"success": False})
else:
return render_template("channels.html")
Head section from layout.html
<head>
{% block head %}
<!-- Bootstrap 4 -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
<!-- Additional CSS -->
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
<!-- JS -->
<script src="{{ url_for('static', filename='index.js') }}"></script>
{% endblock %}
</head>
EDIT
When I hit the button to submit the form, I see "GET /static/index.js HTTP/1.1" 200 - in the terminal. Still nothing in the console, but I thought a GET request was unusual, given I was hitting a button to submit a form. Was expecting POST.
The problem is your form is being submitted through HTTP protocol (traditional form submission) rather than JS. To prevent this, you need to stop your button click from submitting the form:
var form = document.querySelector('#channelForm'),
button = form.querySelector('input.btn-primary');
button.addEventListener('click', function(e) {
e.preventDefault();
console.log('process form data!');
})
Also, place your JavaScript include after your HTML inclusions, so the elements targeted by your JS are on the page when the JS loads.
What might be happening is that the response from your server is quicker than you realize. You're not preventing the default onsubmit functionality, so you might not actually see the JS run in your console. To see this, you can try to turn on a preserve logs between navigations setting. But what you likely need is to prevent the default and then directly send your request to your server:
document.querySelector('#channelForm').onsubmit = (event) => {
event.preventDefault()
...
// AJAX request to your server with your data
};
Related
I've been teaching myself python and Django and wanted to show a clickable map on a webpage. I don't know javascript but I found jvectormap and it seems to be easy and works well.
However I am confused about template tags. With the code below I can show a world map, and using the onregion function can get the country code and send it to an alert, if I comment out the alert, I can send using the Django URL tag to another web page.
{% load static %}
{% load static %}
{{params}}
<!DOCTYPE html>
<html>
<head>
<title>jVectorMap demo</title>
<link href="{% static 'css/jquery-jvectormap-2.0.5.css' %}" rel="stylesheet" type="text/css" media="screen"/>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="{% static 'js/jquery-jvectormap-2.0.5.min.js' %}"></script>
<script src="{% static 'js/jquery-jvectormap-world-mill-en.js' %}"></script>
</head>
<body>
<div id="world-map" style="width: 600px; height: 400px"></div>
<script>
$(function(){
$('#world-map').vectorMap({ map: 'world_mill_en',
onRegionClick:function(event, code){
var message = (code);
alert( code );
window.location.assign ("{% url 'mainapp:home2' %}")
}
});
});
</script>
</body>
</html>
So a couple of questions:
I thought I should be able to use the template tag in the alert like alert( {{params}} ) but that doesn't seem to work. I thought you might be able to pass the string in params that way. I saw found a different post that suggested wrapping the variable with a tag
'''
{{ params }}
'''
is that a good approach to use tags to get variables into the javascript?
More importantly with the URL template tag, I would like to pass the country code or other information back to the view. In the documentation it looks like you can either use:
{% url 'some-url-name' v1 v2 %}
or
{% url 'some-url-name' arg1=v1 arg2=v2 %}
How would I modify my current view and URL with that approach?
def home(request):
params='this is the home page '
return render(request,'mainapp/home.html',{'params':params})
Do I just start by replacing the return with a redirect?
return redirect('some-view-name', params= code )
and then set up a url path where the code is passed as an id or a slug or something?
Is there different way I should be trying where the onregion or an java onclick script sends something back to django?
Sorry for the multiple questions, but in learning mode...
To answer my own question, I found the following on stack overflow that were helpful in understanding.
Django Template Variables and Javascript
Get javascript variable's value in Django url template tag
and several others.
The short answer is that the template tags once rendered are just text in the html file.
1) You can assign a javascript variable something from a template tag, but that is probably not a great idea in some cases since it could be misused.
2) Since the template tag is just text in the file, there are ways to put a placeholder and then use the .replace function to alter the placeholder and then reverse the URL. The usual way seems to have the placeholder be a number that would never be accessed and and replace the placeholder with a pk or an object id.
3) Some people point out that using GET statement is better and simpler. Somehow AJAX can be used to do this. I'm just learning how to spell Ajax so I will leave it to others to explain.
I have a button in my html that when clicked does some calculations and makes a post call to one of my API with some params and then from django I fetch the params and put them in the specific placeholders of the template and render the template.
The problem is I can see the template being rendered (if I check the 'network' section of Google inspect element) but I don't see anything in the page. I expect the template to be rendered in a new tab with the values I fetched from the post params placed in respective placeholders in the template.
Here is what I send through ajax post (I am using angular js in my project but I can also do it with plain js too)
var toSend = {
"user": username,
"password": password,
"text": text
"context": $scope.newContext,
}
$http({
method: 'POST',
url: '/correction',
data: toSend
}).
then(function(response) {
console.log(response.data);
})
Here is my django function defined for the API which receives the post request
#csrf_exempt
def get_correction(request):
if request.method == 'POST':
context = {}
try:
print("recieved request")
user_request = json.loads(request.body.decode('utf-8'))
'''
some logic I use to check given the
username and password, whether it is a
valid user or not
'''
text_header = user_request.get("text")
userid = user_request.get("user")
context["userid"] = userid
context["text_header"] = text_header
except Exception as e:
print(e)
return render(request, 'correction_page.html', context)
Here I store the text and user in a context dictionary to be sent along with the correction_page.html.
Here is how my correction page looks like
<!DOCTYPE html>
{% load static %}
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
</head>
<body>
<h1>Welcome!</h1>
<span> {{text_header}} </span>
<script type="text/javascript" src="{% static 'js/fetch_info.js' %}"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> -->
</body>
</html>
Here the text_header placeholder indeed gets the value (as I can see in Google chrome network section in inspect element.)
But in the UI, I do not see anything happening. I was hoping that the template would be served in a new tab but nothing happens.
What am I doing wrong?
Here is my suggestion:
Django is server-side, opening in a new tab is client-side. So, you wont be able to open in new tab just by server response. You need to handle it in client side , i.e. angularJS
Make $http call, and once resolved, open a new tab and redirects to /correction/data which should provide all these data and return a template with necessary data , which have been saved after doing POST to /correction
something like:
$http({
method: 'POST',
url: '/correction',
data: toSend
}).
then(function(response) {
$window.open(url , '_blank');
})
and this url would refer to /correction/data and you'll get your data
Relevant information:
1) First post on here, go easy.
2) I'm a noob.
3) Trying to learn python/Django.
What I'm trying to do:
1) Create an english -> pig latin translator (written in python) and have it work in browser.
How I want it to work:
1) User clicks "translate" button, which then uses my existing python function to translate their input.
2) The translation is then displayed below the input.
What I've done so far:
1) Created .py file that successfully translates english -> pig latin in the console.
def pigLatin(sentence):
translation = " "
for word in sentence.split():
if word[0] in "aeiou":
translation += word + "yay "
if word[0] and word[1] not in "aeiou":
translation += word[2:] + word[0:2] + "ay"
print("hai")
else:
translation += word[1:] + word[0] + "ay "
return translation
sentence = input("Enter word/sentence you want translated to pig latin below: ")
print(pigLatin(sentence))
2) Used Jinja/added some HTML and bootstrap (please see below)
What it looks like in browser
3) Created a Django project, and installed my pig latin app, my folders are structured as so:
--mysite
|
|--pigLatinApp
|----templates
|------pigLatinApp
|--------home.html
|--------header.html
3) Attempted to use Ajax to get my button working, my HTML files and views.py are as follows:
header.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Pig Latin Translator</title>
{% load staticfiles %}
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}" type="text/css">
</head>
<body class="body">
<section id="translatorSection">
<!------------------Log Sum Container-------------------------->
<div class="container" id="translatorContainer">
{% block content %}
{% endblock %}
</div>
</section>
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js">
</script>
</body>
</html>
home.html
{% extends "pigLatinApp/header.html" %}
{% block content %}
<div class="row">
<div class="col-md-6">
<form>
<h3>Pig Latin Translator</h3>
<p>Enter below what you want translated!</p>
<input type="string" class="form-control" placeholder="Type what you want translated here!" id="inputSumA">
<button id="translateToPig" class="btn btn-success form-control">Translate</button>
<div id="displayTranslation">
<p>{{ output }}</p>
</div>
</form>
</div>
</div>
<script>
$("#translateToPig").click(function() {
$.get("/output/", function(data) {
$("#displayTranslation").html(data);
}, "html");
});
</script>
{% endblock %}
views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
return render(request, 'pigLatinApp/home.html')
def output(request):
if request.is_ajax():
py_obj = pigLatinTranslator.test_code(10)
return render(request, 'pigLatinApp/output.html', {'output': py_obj.a})
What actually happens when I click the button:
1) Nothing... The page seems to refresh.
Any and all help would be appreciated, cheers!
Here:
<script>
$("#translateToPig").click(function() {
$.get("/output/", function(data) {
$("#displayTranslation").html(data);
}, "html");
});
</script>
Your click event handler doesn't prevent the event's default action, so your form is submitted by the browser. Since your form has no 'action' attribute it's submitted to the current url, so the index view is called and the page is reloaded.
You can prevent this by calling preventDefault() on the event ie:
<script>
$("#translateToPig").click(function(evt) {
evt.preventDefault(); # prevents posting the form
evt.stopPropagation(); # make sure it doesn't bubble up
$.get("/output/", function(data) {
$("#displayTranslation").html(data);
}, "html");
});
</script>
Now there are a couple things that could be improved in your code but that's another topic. At least I think you should first try to get something working without ajax, so you learn the whole request/response cycle stuff, working with GET and POST, using django forms etc.
I recommend trying a normal request i.e. not ajax and also creating a form.py where you can create a form class for the search. In your views import the pig latin translator function and call it in your output function.
When I reload a page called Donkey.aspx, there's a breakpoint being hit on the method below.
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (application.Request.Url.AbsolutePath.EndsWith(".blopp")) { ... }
}
The problem is that all I get to see is a hit on the Donkey.aspx and another file (the one with id __browserLink_initializationData) referenced by it, only. However, there's a bunch of other references to files and those are not causing hits on the method. The end of the produced page looks like this.
<script type="text/javascript" src="/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="jquery.placeholder.js"></script>
<script src="beep.blopp" type="text/javascript"></script>
<script src="typeahead.bundle.min.js" type="text/javascript"></script>
<script type="text/javascript" src="utils.js"></script>
</div>
<div class="aspNetHidden">
<input type="hidden" name="__VIEWSTATEGENERATOR"
id="__VIEWSTATEGENERATOR" value="54ACFC5B" />
</div>
</form>
<!-- Visual Studio Browser Link -->
<script type="application/json" id="__browserLink_initializationData">
{"appName":"Chrome","requestId":"f51b45a6ac174b6e8880184492a80734"}
</script>
<script type="text/javascript" src="http://localhost:64593/9...7/browserLink" async="async">
</script>
<!-- End Browser Link -->
</body>
Accessing another page gives me the expected behavior, namely a bunch of hits on the event handler above, including CSS-files etc. Notable is that the files do indeed get loaded, as verified by the functionality of the scripts, console status codes (200 and 304 all around). The master page is virtually empty.
I'm at loss on what's wrong and even more uncertain on how to trouble-shoot it.
Static files are usually cached by browser. Turn off browser cache or reload all from server (Ctrl+F5).
I am desperately trying to integrate a time picker in my app, but without success. I tried to use this code to do it: https://github.com/weareoutman/clockpicker
I have all the files and the paths are properly set. Here is the weird thing. If I integrate this code:
<!-- Or just a input -->
<input id="demo-input" />
<!-- jQuery and Bootstrap scripts -->
<script type="text/javascript" src="../assets/js/jquery.min.js"></script>
<script type="text/javascript" src="../assets/js/bootstrap.min.js"></script>
<!-- ClockPicker script -->
<script type="text/javascript" src="../dist/bootstrap-clockpicker.min.js"></script>
<script type="text/javascript">
$('.clockpicker').clockpicker()
.find('input').change(function(){
// TODO: time changed
console.log(this.value);
});
$('#demo-input').clockpicker({
autoclose: true
});
if (something) {
// Manual operations (after clockpicker is initialized).
$('#demo-input').clockpicker('show') // Or hide, remove ...
.clockpicker('toggleView', 'minutes');
}
</script>
I have declared the css file in the header too:
<!-- ClockPicker Stylesheet -->
<link rel="stylesheet" type="text/css" href="../dist/bootstrap-clockpicker.css">
into the input.html file of my app and starts this app, I can see the box where the clock is supposed to be active but nothing happens when I click on the box, while the clock should appear.
However, if I run the html file in the browser, as a stand alone unit, then the clock works as it should be.
In the app, the input file is called from a higher up file view.py:
#app.route('/input')
def addresses_input():
return render_template("input.html")
I coded in python and used flask
Anyone has an idea? I have been struggling with this for quite some time now and I cannot find an answer...
Thanks!
Your #route should look like
from wtforms.fields.html5 import DateField
class TimeForm(Form):
time_picker = DateField('DatePicker', format='%Y-%m-%d')
#app.route('/current_page', methods=['POST'])
def show_time():
time_form = TimeForm() # this could be the class as well
if time_form.validate_on_submit():
return time_form.time_picker.data.strftime('%Y-%m-%d')
return render_template('bootstrap_template.html', form=form)
You can get the time picker in the jinja2 template in following way
bootstrap_template.html
<form action="some_action" method="post">
{{ time_form.time_picker(class='datepicker') }}
{{ time_form.hidden_tag() }}
<input type="submit"/>
</form>
Please note - you will need to add your js library and css file in template file.