1

index.html

  <div class="div-inputs">
    <input type="text" id="input" placeholder="Enter expression" value="1+2"/>
    <input type="submit" id="btn" value="Execute"/>
  </div>
  <input type="text" id="output" readonly="readonly">
  
  <script src="{{url_for('static', filename='js/jquery-3.2.1.min.js')}}"></script>
  <script type="text/javascript">
    document.querySelector('#btn').addEventListener('click', (e) => {
      let equation = document.querySelector('#input').value;
      $.ajax({
        url: "/",
        type: "POST",
        data: equation,
        success: function(){
          console.log("successfull POST");
          let result = {{evaluate}}
          document.querySelector('#output').value = result;
        }
      });
    });
  </script>

main.py

from flask import Flask
from flask import url_for, jsonify, render_template, request, json
from math import *

app=Flask(__name__)

@app.route('/', methods=["GET","POST"])
def index() :
  
    evaluate = ""
    if request.method == 'POST':
        toEvalFromJS = request.get_json()
        evaluate = eval(str(toEvalFromJS))
        return render_template('index.html', evaluate=evaluate)
    
    return render_template('index.html')

if __name__ == "__main__":
    app.run(port=10, debug=True)

error

(index):26 successfull POST
(index):28 Uncaught ReferenceError: Cannot access 'result' before initialization
    at Object.success ((index):28:53)
    at i (jquery-3.2.1.min.js:2:28017)
    at Object.fireWith [as resolveWith] (jquery-3.2.1.min.js:2:28783)
    at A (jquery-3.2.1.min.js:4:14035)
    at XMLHttpRequest.<anonymous> (jquery-3.2.1.min.js:4:16323)

I know what the error means but I could only get such far.

I have already read the following questions:

but they did not help me solve my problem.

What I want to do: User input an expression string, then click on the submit button and get the evaluated string back.

How could I get the evaluated string?

I am new to flask, I do it just for practice

lali002
  • 23
  • 4
  • always put code, data and full error message as text (not screenshot, not link) in question (not in comment). It will be more readable and easier to use in answer, and more people will see it - so more people can help you. – furas Mar 14 '22 at 12:55
  • @furas I have fixed it. – lali002 Mar 14 '22 at 13:05
  • what do you have in `{{evaluate}}` ? Put some text instead of `{{evaluate}}` and check if it works. – furas Mar 14 '22 at 13:06
  • you use `{{evalute}}` in wrong way. Flask puts value in `{{evalute}}` before it sends page to server. You can't use it to get value from ajax. You should use `data` in `'success: function(data) {... use ... data... }` to get data from server. BTW: it looks strange - page `index.html` runs ajax to get the same `index.html`. Ajax usually is used to get some part of HTML or JSON data which is simpler to generate HTML on page. – furas Mar 14 '22 at 13:09
  • evaluate come from `return render_template('index.html', evaluate=evaluate)` . If I add some text instead of `{{evaluate}}` it is working, but then it cannot eval the string – lali002 Mar 14 '22 at 13:12
  • maybe it will work better when you put `;` at the end `let result = {{evaluate}}` and you use `" "` to create string `let result = "{{evaluate}}";` because at this moment it may redner as `let result = document.querySelector('#output').value = result;` and this is wrong. – furas Mar 14 '22 at 13:12
  • @furas this way `let result = "{{evaluate}}";` it is working, but I cannot get the output – lali002 Mar 14 '22 at 13:15
  • you use `{{evaluate}` in wrong way. `{{evaluate}}` is part of Flask and it will not work in JavaScript as you expect. Flask first get template and replace `{{evaluate}}` with empty value and it sends to browser - and ajax get new page but it doesn't replace current page so it still have empty value in `{{evaluate}}` – furas Mar 14 '22 at 13:15
  • you mix two separated words - you have to use `function(data) {...}` and you will get new FULL index.html in `data` (with new value in place `{{evaluate}}`)> But you should rather send AjAX to different url which will send only valeu without full HTML. – furas Mar 14 '22 at 13:16
  • @furas so I have to post to other page, then get the data from that page? – lali002 Mar 14 '22 at 13:17
  • yes, you have to post to other URL which sends back only expected value- and you should get it as `data` in `function(data) {... document.querySelector('#output').value = data }` – furas Mar 14 '22 at 13:18
  • you have another problem - `ajax` doesn't send it as JSON but as normal `form` and you have it in `request.form` and `get_json()` gives None. It may need to convert `equation` to JSON in `$.ajax` before you send it. OR you have to get it as `reques.form` but it may convert `+` to space because usually browser use `+` instead of space when it sends some data - and later tools like Flask convert `+` back to space. – furas Mar 14 '22 at 13:32
  • @furas I am using `data: JSON.stringify(equation)` and `toEvalFromJS = request.get_json()` `print(toEvalFromJS)` but it print None – lali002 Mar 14 '22 at 13:40
  • in `$.ajax` it may need to add header something like `applications/json`. OR use `request.get_data()` – furas Mar 14 '22 at 13:45

1 Answers1

2

Main problem is that you use {{evaluate}} in wrong way. You expect that JavaScript will get data from server and it will replace {{evaluate}} with new value. But it is not true.

Flask replaces {{evaluate}} with empty string and it sends HTML with empty string - and browser when you load page index.html and browser has HTML with empty string (it doesn't know that there was {{evaluate}}).

When $.ajax gets it agains then Flask replaces {{evaluate}} in template index.html and it sends new HTML with new value in place of {{evaluate}} but it can't replace empty string which you already have in original HTML in browser - it doesn't work this way.

JavaScript get new HTML in data in sucess: function(data){...} and you have to write code which uses this data. But it could be simpler if you would send ajax to separated URL which send back only result (without other HTML). And then it can display data

sucess: function(data){
   document.querySelector('#output').value = data;
}

Later there is another problem(s).

Ajax sends it with standard header for POST form and Flask see it and it convert data to request.form and it has empty request.get_json (because all is in request.form).

But some chars has special meaning in form and url (ie. + is used instead of space) and it automatically unescapes data and it puts space instead of +.

To get + you have to get raw data using request.get_data()

Or you would have to send ajax with header application/json to use get_json().

      $.ajax({
        ...
        contentType: "application/json; charset=utf-8",        
        ...        
      })

Minimal working code

I use render_template_string instead of render_template to put all in one file - and now everyone can simply copy and run it.

I also use https://cdnjs.cloudflare.com to load jquery so it doesn't need local file with jquery.

from flask import Flask, url_for, jsonify, render_template_string, request, json
from math import *  # `import *` is not preferred

app = Flask(__name__)

@app.route('/')
def index():
    return render_template_string("""
  <div class="div-inputs">
    <input type="text" id="input" placeholder="Enter expression" value="1+2"/>
    <input type="submit" id="btn" value="Execute"/>
  </div>
  <input type="text" id="output" readonly="readonly">
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

  <script type="text/javascript">
    document.querySelector('#btn').addEventListener('click', (e) => {
      let equation = document.querySelector('#input').value;
      $.ajax({
        url: "/data",
        type: "POST",

        contentType: "application/json; charset=utf-8",        

        data: JSON.stringify(equation),
        success: function(data){
          console.log("successfull POST");
          console.log(data);
          document.querySelector('#output').value = data;
        }
      });
    });
  </script>
""")

@app.route('/data', methods=['POST'])
def data():
    print('json:', request.get_json())  # `None` if there is no `contentType: "application/json; charset=utf-8"` in `$.ajax`

    print('data:', request.get_data())
    print('form:', request.form)

    equation = json.loads(request.get_data().decode())
    print('equation:', equation)
    
    if equation:
        result = eval(equation)
    else:
        result = "wrong data"
        
    return jsonify(result)

if __name__ == "__main__":
    #app.debug = True
    app.run(port=5010)
furas
  • 134,197
  • 12
  • 106
  • 148