How to prevent page reload on form input with FastAPI
Asked Answered
R

1

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.

Reseat answered 15/8, 2022 at 1:0 Comment(2)
Does this answer your question? How to send a FastAPI response without redirecting the user to another page?Aphonic
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.Acetate
A
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>
Aphonic answered 15/8, 2022 at 9:47 Comment(2)
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 numberReseat
You can use document.getElementById("responseArea").innerHTML += data + "<br>". In that case, you may also want to move Type a number label into a different <div> element.Aphonic

© 2022 - 2024 — McMap. All rights reserved.