javascripthtmlpython-3.xflaskinterface

Automated Flask Interface Querying After Form Submission


I'm creating a basic Flask app to showcase my silly mini projects that I've done for fun in python (originally created for CLI). One of these is a program to visualize John Conway's Game Of Life (GOL). If you don't know what that is, you don't need to, just know that it's a continuously changing binary matrix.

After the user submits a form that specifies an integer for the size of the grid and a float between 0 and 1 to auto-generate the density of living cells in the initial state, I want the grid to display below the form and switch to its next state every 0.1 seconds (without refreshing the page).

I have a fairly decent knowledge of Vanilla Python and HTML, but I'm new to Flask and know nothing about JavaScript. I ChatGPTed a little JS that I thought would work, but nothing displays below the form after it is submitted. As a side note, if possible, I'd like to not start querying Flask until after the form is submitted, which ChatGPT's JS does not do. Here's the code relevant to the GOL piece of the app:

In app.py:

import game_of_life
from markupsafe import Markup

game = None
@app.route("/create_GOL", methods=['post'])
def create_game():
    density, size = request.form['density'], request.form['size']
    global game
    # initializes game in GameOfLife class
    game = game_of_life.GameOfLife(int(size), float(density))
    return render_template("index.html")

@app.route("/next_state", methods=['get'])
def next_state():
    # runs method to update "game" to the next state in-place
    game.find_next_state()
    # returns the HTML-friendly version of the matrix
    return Markup('<br>'.join(''.join(('  ', '██')[j] for j in i) for i in game.m))

In index.html:

<form action="create_GOL", method="post">
    <label for="density">Density (0-1): </label>
    <input type="number" name="density", step="any", min=0, max=1>
    <label for="size">Size: </label>
    <input type="number" name="size", min=1, max=100>
    <input type="submit" value="Submit">
</form><br>
<div id="game-state"></div>
<script>
    function updateGameState() {
        fetch('/next_state')
            .then(response => response.json())
            .then(data => document.getElementById('game-state').innerText = data.state)
    }
    setInterval(updateGameState, 100);
</script>

How should I modify my code to: 1. Actually display something 2. Not waste queries on nothing until the form is actually submitted

Or, should I use a different approach?


Solution

  • Okay, so there are few issues in your code.

    You have not posted the python code with route '/', which i am assuming is returning index.html.

    When you submit the form and hit the 'create_GOL' route, you again send the same index.html file which resets the update loop and so you dont see anything. After the submit event you immediately start the next loop without initializing the game.

    I have modified both the python and javascript code, this should work ( i tested it on random 2d array )

    from flask import Flask,render_template,request, jsonify
    import numpy as np
    
    app = Flask(__name__)
    game = None
    
    @app.route('/')
    def index():
        return render_template('index.html')
    
    @app.route("/create_GOL", methods=['post'])
    def create_game():
        global game
        jsonData = request.get_json()
        density, size = float(jsonData['density']), int(jsonData['size'])
        game = game_of_life.GameOfLife(size,density)
        return {
                'response' : 'Created'
            }
    
    @app.route("/next_state", methods=['get'])
    def next_state():
        global game
        game.find_next_state()
        ret = '<br>'.join(''.join(('  ', '██')[j] for j in i) for i in game.m)
        return jsonify(ret)
    
    
    <body>
        <form action="create_GOL" , method="post" onsubmit="doSubmit();return false">
            <label for="density">Density (0-1): </label>
            <input id='density' type="number" name="density" , step="any" , min=0, max=1>
            <label for="size">Size: </label>
            <input id='size' type="number" name="size" , min=1, max=100>
            <input type="submit" value="Submit">
        </form><br>
        <div id="game-state"></div>
        <script>
    
            function updateGameState() {
                fetch('/next_state')
                    .then(response => response.json())
                    .then(data => { document.getElementById('game-state').innerHTML = data })
            }
    
            function doSubmit() {
                const jsonData = { density: document.getElementById('density').value, size: document.getElementById('size').value };
    
                // Set up options for the fetch request
                const options = {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json' // Set content type to JSON
                    },
                    body: JSON.stringify(jsonData) // Convert JSON data to a string and set it as the request body
                };
    
                fetch('/create_GOL', options);
                setInterval(updateGameState, 1000);
            }
        </script>
    </body>