2

I have a simple program using FastAPI that multiplies a float value by 2 and displays it in HTML; this value is inputted through a HTML form. I'm wondering how I can get the value to display dynamically, without the page reloading when you press enter or press the submit button.

Here are the scripts (main.py and double.html):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World</title>
</head>
<body>
    <h1>Hello World!</h1>
    <form method="post">
        <input type="number" step="0.00000001" name="num"/>
        <input type="submit"/>
    </form>
    <p>Result: {{ result }}</p>
</body>
</html>
from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
import uvicorn

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.get("/", response_class=HTMLResponse)
async def double_num(request: Request):
    result = "Type a number"
    return templates.TemplateResponse('double.html', context={'request': request, 'result': result})

@app.post("/", response_class=HTMLResponse)
async def double_num(request: Request, num: float = Form(...)):
    result = num*2
    return templates.TemplateResponse('double.html', context={'request': request, 'result': result})

if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)

I realize similar questions have been asked but they seem to involve jquery/javascript and don't work for me due to the FastAPI/python backend.

joebob
  • 33
  • 5
  • 1
    Does this answer your question? [How to send a FastAPI response without redirecting the user to another page?](https://stackoverflow.com/questions/71665139/how-to-send-a-fastapi-response-without-redirecting-the-user-to-another-page) – Chris Aug 15 '22 at 06:05
  • To answer your last statement; the javascript function would _call your Python/FastAPI backend_. You won't be able to do what you want easily without using Javascript (which is the standard way of doing something like that). You call your backend code through Javascript and replace the content of the page, as shown in the duplicate linked. – MatsLindh Aug 15 '22 at 07:02

1 Answers1

2

As described in this answer, to keep the page from reloading/redirecting when submitting an HTML <form>, you would need to use a Javascript interface/library, such as Fetch API, to make an asynchronous HTTP request. If you would like to prevent the page from reloading when the user hits the enter key as well, you could use a similar approach to this answer that provides a solution on how to handle the <form> submission on the submit event (using the Event.preventDefault() method). Example below:

app.py

from fastapi import FastAPI, Request, Form
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.get("/")
async def double_num(request: Request):
    return templates.TemplateResponse('double.html', context={'request': request})
 
@app.post("/")
async def double_num(num: float = Form(...)):
    return num * 2

templates/double.html

<!DOCTYPE html>
<html>
   <head>
      <title>Hello World</title>
      <script>
        document.addEventListener("DOMContentLoaded", (event) => {
          document.getElementById("myForm").addEventListener("submit", function (e) {
            e.preventDefault(); // Cancel the default action
            submitForm();
          });
        });
      </script>
   </head>
   <body>
      <h1>Hello World!</h1>
      <form id="myForm">
         <input type="number" name="num">
         <input class="submit" type="submit" value="Submit">
      </form>
      <div id="responseArea">Type a number</div>
      <script>         
         function submitForm() {
            var formElement = document.getElementById('myForm');
            var data = new FormData(formElement);
            fetch('/', {
                    method: 'POST',
                    body: data,
                })
                .then(response => response.text())
                .then(data => {
                    document.getElementById("responseArea").innerHTML = data;
                })
                .catch(error => {
                    console.error(error);
                });
         }
      </script>
   </body>
</html>
Chris
  • 18,724
  • 6
  • 46
  • 80
  • Slightly unrelated, but what would change in the js script if you wanted to vertically list/display all values entered into the form. Currently, it replaces the previous value in the html each time you submit a new number – joebob Aug 18 '22 at 13:22
  • You can use `document.getElementById("responseArea").innerHTML += data + "
    "`. In that case, you may also want to move `Type a number` label into a different `
    ` element.
    – Chris Aug 18 '22 at 13:38