For loop inside of Javascript dynamic Html content in Django - javascript

I will have a strange question.
Version: Django version 3.0.8
This is my Javascript code:
fetch(`/loadbox/${message.id}`)
.then(response => response.json())
.then(dialog => {
let detailcontent=
`<div class="hepsi">
<div class="sender">
<h3>${dialog.sendername}</h3><br>
#${dialog.sender}</div>
<p class="posting msj"> ${dialog.content}</p>
<p class="posting rep"> ${dialog.reply}</p> // Here I have a list named replies and I want
to loop through all elements and display every reply for that particular message.
<br>
<div class="m-form">
<form class="form-control">
<input class="msj-i" type="text" placeholder="Start a new message"></input>
<input type="submit" value="Send" class="msj-s"></input>
</form> </div></div>` ;
document.querySelector('#message-view').innerHTML=detailcontent;
document.querySelector('.msj-s').onclick = function() {
sender=`${dialog.sender}`
reply_message(sender);
}
})
}
so where I fetch the data /loadbox/message.id, I have this data for example:
{"replies": ["hi there", "haha", "last"],"sendername": "Bilge", "sender": "bilge", "content": "hi"}
As you see replies is a list and I want it to loop through in the HTML template. However, I use dynamic content in my javascript code. Normally in Django template, I would easily do
{% for reply in replies %}
{{reply }}
{ % endfor %}
However, I am writing a dynamic html with this method:
content = `"some content"`
document.querySelector(".message-view").innerHtml = content
So I need to loop this through inside of the content I think. I tried:
{for (${reply} of ${replies})}
<p class="posting rep"> ${reply} </p>
it gave this error.
Uncaught (in promise) ReferenceError: reply is not defined
Or I tried :
{% for ${reply} in ${replies} %}
{{reply}}
{% endfor %}
but it gives the error:
Couldn't parse the remainder of ${replies}
So I am not sure if this is even possible, If you have any suggestions for changing my logic or etc I would be appreciated.
For demonstration; If click on a message on the left, it opens up the message on the right, you can reply to it, And I want it to display all of the replies there. Currently, it is undefined because I put whole list not the elements.

Inside template literals you can use the Array.prototype.map() method to loop over the replies and return a string for each reply. Now you'll have an array with strings in them. Last step is to turn the array into a combined string with the Array.prototype.join() method. Use an empty string as separator to join the strings together.
fetch(`/loadbox/${message.id}`)
.then(response => response.json())
.then(dialog => {
let detailcontent = `
<div class="hepsi">
<div class="sender">
<h3>${dialog.sendername}</h3>
<br>
#${dialog.sender}
</div>
<p class="posting msj">${dialog.content}</p>
${dialog.replies.map(reply =>
`<p class="posting rep">${reply}</p>`
).join('')}
<br>
<div class="m-form">
<form class="form-control">
<input class="msj-i" type="text" placeholder="Start a new message">
<input type="submit" value="Send" class="msj-s">
</form>
</div>
</div>
`;
document.querySelector('#message-view').innerHTML = detailcontent;
document.querySelector('.msj-s').onclick = function() {
sender = `${dialog.sender}`
reply_message(sender);
}
});

Related

Send data from Javascript → Python and back without reloading the page

I'm trying to make a stock finance like website where anyone can get fake money and buy stocks. So in the buy page, I am trying to implement a feature where as the user types the stock symbol and the number of shares, in real time, the pricing shows up in the h1 tags that have an id of "render". This can be achived if user input is sent to my app.py and after looking up the price using an api and some math, app.py send the price back to javascript to update the page.
I've been trying to use fetch() and AJAX but I don't understand any of the tutorials or stack overflow questions. Can someone give me a reliable solution and explain it to me?
HTML:
{% extends "layout.html" %}
{% block title %}Buy{% endblock %}
{% block main %}
<form action="/buy" method="post">
<div class="mb-3">
<input class="form-control mx-auto w-auto" autocomplete="off" name="symbol" placeholder="Symbol" value="{{ input_value }}" id="symbols">
</div>
<div class="mb-3">
<input class="form-control mx-auto w-auto" autocomplete="off" autofocus name="shares" placeholder="Shares" id="shares">
</div>
<div class="mb-3">
<button class="btn btn-primary" type="submit">Buy</button>
</div>
</form>
<h1 id="render">
</h1>
<script>
</script>
{% endblock %}
App.py:
#app.route("/buy", methods=["GET", "POST"])
#login_required
def buy():
"""Buy shares of stock"""
if request.method == "GET":
return render_template("buy.html", input_value = "")
else:
return render_template("buy.html", input_value = request.form.get("symbol"))
I'm trying to use the function above for rendering the template
Accepting response and sending back information:
#app.route("/show_price", methods=["GET", "POST"])
def show_price():
#logic stuff
return #price
TL;DR at bottom
I found a solution to the problem by using this as my app.py:
#app.route("/show_price", methods=["GET", "POST"])
#login_required
def show_price():
# https://www.makeuseof.com/tag/python-javascript-communicate-json/
data = request.get_json()
if data[1].isdigit() == True:
data = jsonify() # the data
return data
else:
return ""
and using fetch() in my javascript:
{% extends "layout.html" %}
{% block title %}Buy{% endblock %}
{% block main %}
<form action="/buy" method="post">
<div class="mb-3">
<input id="symbols">
</div>
<div class="mb-3">
<input id="shares">
</div>
<h2 id="render">
</h2>
<div class="mb-3">
<button class="btn btn-primary" type="submit">Buy</button>
</div>
</form>
<script>
let input1 = document.getElementById('symbols');
let input = document.getElementById('shares');
input.addEventListener('keyup', function(event) {
value = [
input1.value, input.value
]
fetch("http://127.0.0.1:5000/show_price",
{
method: 'POST',
headers: {
'Content-type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(value)}).then(res =>{
if(res.ok){
return res.json()
} else {
document.querySelector('h2').innerHTML = "Keep typing...";
}
}).then(jsonResponse=>{
word = "That would be " + jsonResponse
document.querySelector('h2').innerHTML = word;
})
.catch((err) => console.error(err));
});
</script>
{% endblock %}
so as the user is typing in the the shares field the event listener will get the symbols and shares fields, use fetch() to get the data over to def show_price() with a jsonified array of symbol and shares. If there is an error the div id="render" will display "Keep typing". After python gets the information it will look it up using a function, then it will return the price of the shares in json format. Then javascript will get the data and use some javascript to change the html.
TL;DR
Basically I used fetch() to get the data to python, did some algorithm stuff and python return it to javascript. https://www.makeuseof.com/tag/python-javascript-communicate-json/ is really useful in teaching you how to use fetch().

PayPal API Cross Site Request Cookie Issue (Django, JavaScript Issue)

I'm creating a site for my senior project and have run into some trouble creating a payment portal for my site. The site was working correctly the other night, and without making any changes, the buttons now fail to render and I am being spammed with errors regarding indicating a cookie in a cross site address. Attached is the checkout.html file which the PayPal js is included within, along with the error codes I am receiving from the console. Any help would be much appreciated!
I have tried to edit the global config and played around with the SameSite attribute, but to no avail :(
{% extends 'main.html' %}
{% load static %}
<!DOCTYPE html>
<head>
<link rel="stylesheet" type="text/css" href="{% static 'css/checkout.css' %}">
</head>
<body>
{% block content %}
<div class="row">
<div class="col-sm-6 mt-4 mb-4">
<div class="box-element" id="form-wrapper">
<h2>Recipient Information</h2>
<form id="form">
<div id="recipient-info">
<div class="form-field">
<input required class="form-control" type="text" name="recipient_first_name" placeholder="Recipient First Name..">
</div>
<div class="form-field">
<input required class="form-control" type="text" name="recipient_last_name" placeholder="Recipient Last Name..">
</div>
<br>
<div class="form-field">
<input required class="form-control" type="email" name="recipient_email" placeholder="Recipient Email..">
</div>
<div class="row ml-auto">
<label class = "mt-1" for="pickup_location">Select a pickup location: </label>
<select class="mt-2 ml-2" name="pickup_location" size="4" multiple>
<option value="nabatieh">Nabatieh</option>
<option value="tyre">Tyre</option>
<option value="saida">Saida</option>
<option value="beirut">Beirut</option>
</select><br><br>
</div>
</div>
<hr>
<input id="form-button" class="btn btn-success btn-block" type="submit" value="Continue">
</form>
</div>
<br>
<div class="box-element hidden" id="payment-info">
<h2>PayPal Options</h2>
<div id="paypal-button-container"></div>
</div>
</div>
<div class="col-sm-6 mt-4 mb-4">
<div class="box-element">
<a class="btn btn-outline-dark" href="{% url 'cart' %}">← Back to Cart</a>
<hr>
<h3>Order Summary</h3>
<hr>
{% for item in items %}
<div class="cart-row">
<div style="flex:2"><img class="row-image" src="{{item.product.imageURL}}"></div>
<div style="flex:2"><p>{{item.product.name}}</p></div>
<div style="flex:1"><p>{{item.product.price|floatformat:2}}</p></div>
<div style="flex:1"><p>{{item.quantity}}</p></div>
</div>
{% endfor %}
<h5>Items: {{order.get_cart_items}}</h5>
<h5>Total: ${{order.get_cart_total|floatformat:2}}</h5>
</div>
</div>
</div>
<script src="https://www.paypal.com/sdk/js?client-id=ASbTo6tPEAlA-TQ3zYFDMuuDgSWzmybQ3E3LeE3xi2s3hN6UmiREE30_atIal3f9ui2r3Eh6oGpxmil-&currency=USD"></script>
<script>
var total = '{{order.get_cart_total}}'
paypal.Buttons({
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: parseFloat(total).toFixed(2)
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {
submitFormData()
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
var transaction = orderData.purchase_units[0].payments.captures[0];
alert('Transaction '+ transaction.status + ': ' + transaction.id + '\n\nSee console for all available details');
// Replace the above to show a success message within this page, e.g.
// const element = document.getElementById('paypal-button-container');
// element.innerHTML = '';
element.innerHTML = '<h3>Thank you for your payment!</h3>';
actions.redirect('home.html');
});
}
}).render('#paypal-button-container');
</script>
<script type="text/javascript">
var form = document.getElementById('form')
var total = '{{order.get_cart_total|floatformat:2}}'
form.addEventListener('submit', function(e){
e.preventDefault()
console.log('Form submitted...')
document.getElementById('form-button').classList.add('hidden')
document.getElementById('payment-info').classList.remove('hidden')
})
function submitFormData(){
console.log('Payment button has been clicked...')
var orderData = {
'total':total,
}
var recipientInformation = {
'recipient_first_name':null,
'recipient_last_name':null,
'email':null,
'pickup_location':null,
}
recipientInformation.recipient_first_name = form.recipient_first_name.value
recipientInformation.recipient_last_name = form.recipient_last_name.value
recipientInformation.recipient_email = form.recipient_email.value
recipientInformation.pickup_location = form.pickup_location.value
var url = "/process_order/"
fetch(url, {
method:'POST',
headers:{
'Content-Type':'applicaiton/json',
'X-CSRFToken':csrftoken,
},
body:JSON.stringify({'form':orderData, 'recipient-information':recipientInformation}),
})
.then((response) => response.json())
.then((data) => {
console.log('Success:', data);
alert('Transaction completed');
window.location.href = "{% url 'store' %}"
})
}
</script>
{% endblock content %}
</body>
</html>
Error from console
Error information expanded
Here is how the portal was working the other night, note I made no changes on my end from then until now.
https://gyazo.com/7cfcc6a9d226af3604a7a597d39e7316
I don't know what your issue is, but it is wrong and bad design to use actions.order.create / capture on the client side and then perform server operations after the fact. This should never be done.
If server operations are to take place (such as saving form data or writing ot a database), the payment creation/capture should be done on the server, via API. With any luck, the problem in your question can be resolved as a byproduct of starting over and implementing things correctly:
Follow the PayPal Checkout integration guide and make 2 routes (url paths) on your django server, one for 'Create Order' and one for 'Capture Order'. You can use the Checkout-Python-SDK for the routes' API calls to PayPal, or your own HTTPS implementation of first getting an access token and then doing the call. Both of these routes should return only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should verify the amount was correct and store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic (such as sending confirmation emails or reserving product) immediately before forwarding your return JSON to the frontend caller. In the event of an error forward the JSON details of it as well, since the frontend must handle such cases.
Pair those 2 routes with this frontend approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server . (If you need to send any additional data from the client to the server, such as an items array or selected options or form data in your case, add a body parameter to the fetch with a value that is a JSON string or object of your serialized data)

How to change visibility/display of a html block using flask or vanilla js with submit event?

I created a small flask app and deployed it on Heroku. I'm bad with backend and flask I just can't figure out how to properly display html block of code that should display under the form when it is submitted.
Link to the app: http://alumil-alloys.herokuapp.com/
Link to github repo: https://github.com/nemanjaKostovski/MLmodel
HTML:
<form class="form" action="{{ url_for('main') }}" method="POST">
<label class="label"><input class="input" type="number" step="0.01" name="Rm" placeholder="Rm" required></label>
<label class="label"><input class="input" type="number" step="0.01" name="Rp" placeholder="Rp" required></label>
<label class="label"><input class="input" type="number" step="0.01" name="A%" placeholder="A%" required></label>
<label class="label"><input class="input" type="number" step="0.01" name="Wb" placeholder="Wb" required></label>
<input class="button" type="submit" value="Search">
</form>
<div class="result">
{% if result %}
{% for variable, value in original_input.items() %}
<b>{{ variable }}</b> : {{ value }}
{% endfor %}
<br>
<br> <h3>Suggested alloy:</h3>
<p class="big">{{ result }}</p>
{% endif %}
</div>
<script src="{{url_for('static',filename='main.js')}}"></script>
CSS:
.result {
visibility: hidden;
text-align: center;
padding: 5px;
margin: 5px;
width: 450px;
border: 5px solid gray;
background-color: #ffd300;
}
JS:
const form = document.querySelector('.form');
const result = document.querySelector('.result');
function showResult(){
result.style.visibility = 'visible';
}
form.addEventListener('submit', showResult);
Flask:
#app.route('/', methods = ['GET', 'POST'])
def main():
if flask.request.method == 'GET':
return(flask.render_template('main.html'))
if flask.request.method == 'POST':
max_strength = flask.request.form['Rm']
yield_strength = flask.request.form['Rp']
elongation = flask.request.form['A%']
webster = flask.request.form['Wb']
input_variables = pd.DataFrame([[max_strength, yield_strength, elongation, webster]],
columns = ['Rm', 'Rp', 'A%', 'Wb'],
dtype = float)
prediction = model.predict(input_variables)[0]
return flask.render_template('main.html',
original_input = {'Rm':max_strength,
'Rp':yield_strength,
'A%':elongation,
'Wb':webster},
result = prediction
)
Sorry, I missed the fact that you were already returning your result as key word arguments (you were just doing it differently from me) so I'm going to edit the answer (deleted most of the previous answer and responding based on the information you have provided in a comment.
Based on your current design
The first time you load your page, the result div will not show up
Then you execute a search and the result div will show up. It may or may not contain results
The result div will now always be visible unless you reload the page. If you do a new search, the contents of the result div will be cleared
If you're okay with the above behavior, then you don't even need the JS script. Just modify your code to
{% if result %}
<div class="result">
......
{% endif %}
You can do away with your JS and the visibility style in your CSS
The explanation is
When you load your page, the system executes a GET and your flask code says to return just the template without any variables. The page will thus check for result and since it is not available, it will not display the result div
Then you enter variables and submit the form which does a POST and your flask now returns a result variable which means the result div will now be displayed to the user
If user wants to clear the result div from the UI, they can reload the page or you can add a button or delete icon that when it is clicked, you remove the div from the page

How to bind text input to another div on the same html page

I've made a tag list that takes user input and binds it to another input field as a list of tags. However, after weeks messing around with Javascript and JQuery the only way (and importantly an elegant way) I've been able to do it is using Brython. Unfortunately, that seems to be disabling the jinja2 template in some way so nothing is submitted to the database. So, after all that, my question is, can such a seemingly simple task be done natively in Flask?
Here is the essence of how the Brython script works https://jsfiddle.net/5nt39deo/2/ but the code that the question is about is below:
<form action="" id="homeForm" class="centered" role="form" method="post">
{{ form1.hidden_tag() }}
{{ form1.toppings(type="text",id="homeInput",autocomplete="off",list="topps",placeholder="Toppings...") }}
<datalist id="topps">{% for top in topps %}<option value="{{ sym }}">{% endfor %}</datalist>
<button action="" type="submit" id="homeAddTags" class="button">Add</button><br><br>
{{ form1.tags(id="tagList") }}<br>
{{ form1.agree(id="homeAgreement") }}
{{ form1.agree.label() }}<br><br>
<button type="reset" id="homeResetTags" class="button">Reset</button>
{{ form1.sendtags(type="submit",class_="button",id="homeSubmit") }}<br><br>
</form>
<script type="text/python" id="script1">
from browser import document, html, window, console, alert
storage = []
def addTag(e):
item = document['homeInput'].value
if item not in storage and item != '':
storage.append(item)
document['homeInput'].value = ''
document['tagList'].textContent = storage
else:
alert('Tag already added.')
document['homeInput'].value = ''
def resetTags(e):
storage = []
document['homeInput'].value = ''
document['tagList'].textContent = storage
document['homeAddTags'].bind('click', addTag)
document['homeResetTags'].bind('click', resetTags)
</script>
Check out this native javascript solution
...
<input type="text" id="text" placeholder="Enter something" oninput="outputUpdate();">
...
<script type="text/javascript" id="script2">
function outputUpdate(e){
let currentText=document.getElementById('text').value;
document.getElementById('output').innerHTML = currentText;
</script>
https://jsfiddle.net/kalovelo/z5ngxp31/
You can try something like that which I have explained below.
<input type="text">
<div id="output"></div>
Here is the Javascript: Instead of calling a javascript function explicitly, You can simply attach a keyup event to your input box. You can check this out here Fiddle
<script type="text/javascript">
var $onKeyUpEvent = document.getElementsByTagName('input')[0];
$onKeyUpEvent.addEventListener('keyup', (v) => {
document.getElementById('output').innerHTML = v.target.value;
})
</script>

Trying to create a printable web page in octobercms

Hi I am trying to create a printable page from data send by a form in octobercms
I have created a plugin component which I have called PrintPageForm
<?php namespace Acme\PrintPage\Components;
use Cms\Classes\ComponentBase;
use Input;
class PrintPageForm extends ComponentBase
{
public function componentDetails()
{
// TODO: Implement componentDetails() method.
return
[
'name' => 'Print Page Form',
'description' => 'Detail page print form'
];
}
public function onHandleForm()
{
$var =
[
'overview' => Input::get('print_overview'),
'photos' => Input::get('print_photos')
];
I have this in the default htm file
<form action="/print" data-request-data="printpageform::onHandleForm" data-request-validate data-request-flash accept-charset="utf-8" class="form ajax-form">
<h3 class="sub-heading">Print Details</h3>
<p>To build a printer friendly formatted page, please select from the options shown below:</p>
<ul class="print-section">
<li>
<input type="checkbox" class="checkbox-input" value="1" name="print_overview" id="print_overview">
<label class="checkbox-label period" for="print_overview">Overview: Summary and key features alongside a photo of the property.</label>
</li>
<li>
<input type="checkbox" class="checkbox-input" value="1" name="print_photos" id="print_photos">
<label class="checkbox-label period" for="print_photos">Photos: Photo gallery of the property.</label>
</li>
</ul>
<input type="hidden" name="print" value="1">
<button class="btn button-large one-third palm-one-whole" type="submit" rel="print" >Print</button>
</form>
I am trying to access the value of print_overview and print_photo values in my print view page but can not figure out how to access these values I can see these values being passed in Debugbar as follows "request_query
array:2 [ "print_overview" => "1" "print" => "1" ]" and in my view file I have
{%if "print_overview" == "1" %}
{{ 'checked' }}
{% else %}
{{ 'Not Checked' }}
{% endif %}
but it does seem to matter what the value of print_overview is the page only echos out Not Checked I'm in a rut that I can't figure out any thoughts would be gratefully accepted.
Couple of pointers. When rendering a form in Twig, you should use either the {{ form_open() }} or {{ form_ajax() }} tags
Secondly, you can access the request data via the post() function in your component class; and you pass it to your view (the component partial) through the page property. So, your handler would like something like:
public function onHandleForm()
{
// Pass the variables to the view renderer
$this->page['print_overview'] = (bool) post('print_overview');
$this->page['print'] = (bool) post('print');
// Return a partial response http://octobercms.com/docs/ajax/update-partials#pushing-updates
return ['#view-response-element' => $this->makePartial('#response')]; 
}
While your response.htm partial file would look something like this:
{% if print_overview %}
"checked"
{% else %}
"not checked"
{% endif %}
As a note, if you are using the {% macro %} tags, these do not have access to the local scope of the partial file, i.e. they do not have access to the variables provided to the view. Any evaluation done within {% macro %} tags needs to be based on variables passed to it.
The best strategy for printing I find is to use JavaScript:
<!-- Link to print -->
<p>Print this invoice</p>
<!-- Invoice printer -->
<script type="text/template" id="invoiceTemplateContents">
Printable contents go in here
</script>
<!-- Script -->
<script>
function printInvoice() {
var printWindow = window.open('','','left=0,top=0,width=950,height=500,toolbar=0,scrollbars=0,status=0')
printWindow.document.write($('#invoiceTemplateContents').html())
printWindow.document.close()
printWindow.focus()
printWindow.print()
printWindow.close()
}
</script>

Categories

Resources