javascript - How to pass HTML string from flask app to HTML template as a DOM element and then appendChild - Stack Overflow

CONTEXTI've created a Flask web app that has a form on the webpage, and then I want the user to se

CONTEXT

I've created a Flask web app that has a form on the webpage, and then I want the user to see the results of that form immediately to the right of the form. When the form is submitted, a python script is activated on the backend, and this produces an HTML result file (authorList.html) - which is what I want to display next to the form.

PROBLEM

From what I understand, this requires me to use Ajax, so I can get results without refreshing the page, but I'm not as familiar with how to pass that HTML result file from the flask app.py to the HTML template and then append that as a node to the DOM tree.

1) I have my code set up so far such that the upload route is invoked, which then renders index.html, and then the JS code in there refers back to the results route, which then renders index.html again, passing in the HTML result file as a string. I've partially set up the HTML-string-to-DOM-element conversion step in index.html under where it says <p>Results should display here.</p> (as this is the part of the page where I want the HTML result file to display) but am not sure if I'm on the right track and how to proceed with doing appendchild.

2) Also, when I try running this code below the way it is, I get a JS error of Uncaught SyntaxError: Unexpected token ; pointing to this line of index.html: var d = document.createElement('div'); d.innerHTML = ; return d.firstChild;...is it because I'm not passing in the data variable correctly on the Flask app side? (RESOLVED AND UPDATED IN CODE ACCORDINGLY)

(note: I am not familiar with JS, so if this seems simple, I apologize in advance!)

SOURCE CODE

app.py:

@app.route("/", methods=['GET', 'POST'])
def upload():
    return render_template('index.html', template_file=app.config['TEMPLATE_FILE'])


@app.route("/results", methods=['POST'])
def results():
    data = []
    if request.method == 'POST':
            if request.form['affiliation'] == "letter":
                affiliation = "let"
            elif request.form['affiliation'] == "number":
                affiliation = "num"

            proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(file.filename, period, space, affiliation), shell=True, stdout=subprocess.PIPE)
            while proc.poll() is None:
                time.sleep(0.5)

            # Convert resulting HTML file to a string and pass to index.html
            with open('authorList.html') as f:
                data.append("".join(f.readlines()))
    return render_template('index.html', data=''.join(data))

index.html:

<html>

<head>

<script src=".9.1/jquery.min.js"></script>
 <script>
    $(document).ready(function() {
        $('form').submit(function (e) {
            var url = "{{ url_for('results') }}"; 
            $.ajax({
                type: "POST",
                url: url,
                data: $('form').serialize(), 
                success: function (data) {
                    console.log(data) 
                }
            });
            e.preventDefault(); 
        });
    });
</script>

</head>

<body>

<div class="container">
  <div class="row">
    <div class="col-sm-6">
          <div>
            <br>
            <p>Download the template file below and re-upload with your custom author information:</p>
            <a href="static/ExampleAuthorList.txt" download="Authors_Template.txt"><button type="button">Download</button></a><br><br>
            <form action="" id="myform" method=post enctype=multipart/form-data>
            <div id="buttonDiv">
              <p><input type=file name=file value="Choose File">
              <p>Mark affiliations with:</p>
              <input type="radio" name="affiliation" value="number" id="number" class="form-radio" checked><label for="number">Number</label><br>
              <input type="radio" name="affiliation" value="letter" id="letter" class="form-radio"><label for="letter">Letter</label>
              <br><br>
            </div>
            <input type=submit value=Upload></p>
            </form>
          </div>
    </div>
    <div class="col-sm-6">
        <div>
          <p>Results should display here.</p>
          <script>
            var d = document.createElement('div'); d.innerHTML = "{{ data }}"; return d.firstChild;
            # Need code for appending child
          </script>
        </div>
    </div>
  </div>
</div>

</body>

</html>

UPDATE

I tried the following change in my JS code (in index.html) but am still not seeing any results appear on the homepage.

  <script>
    var data 
    $(document).ready(function() {
        $('form').submit(function (e) {
            var url = "{{ url_for('results') }}"; // send the form data here.
            $.ajax({
              type: "POST",
              url: url,
              data: $('form').serialize(), 
              success: function (data) {
                var d = document.createElement('div');
                d.innerHTML = data;
                $(".my-results").html(data);
              }
            });
            e.preventDefault(); // block the traditional submission of the form.
        });
    });
</script>

.
.
.
.
        <div>
          <br>
          <p class="my-results">Results should display here.</p>
        </div>
    </div>

UPDATE 2: full app.py

@app.route("/", methods=['GET', 'POST'])
def upload():
    return render_template('index.html', template_file=app.config['TEMPLATE_FILE'])

@app.route("/results", methods=['GET', 'POST'])
def results():
    if 'file' not in request.files:
        flash('No file chosen', 'danger')
        return redirect(request.url)
    file = request.files['file']
    if file.filename == '':
        flash('No selected file', 'danger')
        return redirect(request.url)
    filename = secure_filename(file.filename)
    if not allowed_file(file.filename):
        flash('Incorrect file extension. Must be .TXT!', 'danger')
    if places_exist(os.path.join(app.config['UPLOAD_FOLDER'], filename)) == False:
        flash('There is an affiliation missing from your Place list. Please re-try.', 'danger')
        return redirect(request.url)
    else:
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))

        os.chdir('/Users/cdastmalchi/Desktop/author_script/')

        if request.form['affiliation'] == "letter":
            affiliation = "let"
        elif request.form['affiliation'] == "number":
            affiliation = "num"

        if "Yes sep period" in request.form.getlist('period'):
            period = "y"
        else:
            period = "n"
        if "Yes sep space" in request.form.getlist('space'):
            space = "y"
        else:
            space = "n"

        proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(file.filename, period, space, affiliation), shell=True, stdout=subprocess.PIPE)
        # Wait until process terminates
        while proc.poll() is None:
            time.sleep(0.5)

        with open("authorList.html") as f:
            data = ''.join(f.readlines())
            print(data)
        return data

CONTEXT

I've created a Flask web app that has a form on the webpage, and then I want the user to see the results of that form immediately to the right of the form. When the form is submitted, a python script is activated on the backend, and this produces an HTML result file (authorList.html) - which is what I want to display next to the form.

PROBLEM

From what I understand, this requires me to use Ajax, so I can get results without refreshing the page, but I'm not as familiar with how to pass that HTML result file from the flask app.py to the HTML template and then append that as a node to the DOM tree.

1) I have my code set up so far such that the upload route is invoked, which then renders index.html, and then the JS code in there refers back to the results route, which then renders index.html again, passing in the HTML result file as a string. I've partially set up the HTML-string-to-DOM-element conversion step in index.html under where it says <p>Results should display here.</p> (as this is the part of the page where I want the HTML result file to display) but am not sure if I'm on the right track and how to proceed with doing appendchild.

2) Also, when I try running this code below the way it is, I get a JS error of Uncaught SyntaxError: Unexpected token ; pointing to this line of index.html: var d = document.createElement('div'); d.innerHTML = ; return d.firstChild;...is it because I'm not passing in the data variable correctly on the Flask app side? (RESOLVED AND UPDATED IN CODE ACCORDINGLY)

(note: I am not familiar with JS, so if this seems simple, I apologize in advance!)

SOURCE CODE

app.py:

@app.route("/", methods=['GET', 'POST'])
def upload():
    return render_template('index.html', template_file=app.config['TEMPLATE_FILE'])


@app.route("/results", methods=['POST'])
def results():
    data = []
    if request.method == 'POST':
            if request.form['affiliation'] == "letter":
                affiliation = "let"
            elif request.form['affiliation'] == "number":
                affiliation = "num"

            proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(file.filename, period, space, affiliation), shell=True, stdout=subprocess.PIPE)
            while proc.poll() is None:
                time.sleep(0.5)

            # Convert resulting HTML file to a string and pass to index.html
            with open('authorList.html') as f:
                data.append("".join(f.readlines()))
    return render_template('index.html', data=''.join(data))

index.html:

<html>

<head>

<script src="http://ajax.googleapis./ajax/libs/jquery/1.9.1/jquery.min.js"></script>
 <script>
    $(document).ready(function() {
        $('form').submit(function (e) {
            var url = "{{ url_for('results') }}"; 
            $.ajax({
                type: "POST",
                url: url,
                data: $('form').serialize(), 
                success: function (data) {
                    console.log(data) 
                }
            });
            e.preventDefault(); 
        });
    });
</script>

</head>

<body>

<div class="container">
  <div class="row">
    <div class="col-sm-6">
          <div>
            <br>
            <p>Download the template file below and re-upload with your custom author information:</p>
            <a href="static/ExampleAuthorList.txt" download="Authors_Template.txt"><button type="button">Download</button></a><br><br>
            <form action="" id="myform" method=post enctype=multipart/form-data>
            <div id="buttonDiv">
              <p><input type=file name=file value="Choose File">
              <p>Mark affiliations with:</p>
              <input type="radio" name="affiliation" value="number" id="number" class="form-radio" checked><label for="number">Number</label><br>
              <input type="radio" name="affiliation" value="letter" id="letter" class="form-radio"><label for="letter">Letter</label>
              <br><br>
            </div>
            <input type=submit value=Upload></p>
            </form>
          </div>
    </div>
    <div class="col-sm-6">
        <div>
          <p>Results should display here.</p>
          <script>
            var d = document.createElement('div'); d.innerHTML = "{{ data }}"; return d.firstChild;
            # Need code for appending child
          </script>
        </div>
    </div>
  </div>
</div>

</body>

</html>

UPDATE

I tried the following change in my JS code (in index.html) but am still not seeing any results appear on the homepage.

  <script>
    var data 
    $(document).ready(function() {
        $('form').submit(function (e) {
            var url = "{{ url_for('results') }}"; // send the form data here.
            $.ajax({
              type: "POST",
              url: url,
              data: $('form').serialize(), 
              success: function (data) {
                var d = document.createElement('div');
                d.innerHTML = data;
                $(".my-results").html(data);
              }
            });
            e.preventDefault(); // block the traditional submission of the form.
        });
    });
</script>

.
.
.
.
        <div>
          <br>
          <p class="my-results">Results should display here.</p>
        </div>
    </div>

UPDATE 2: full app.py

@app.route("/", methods=['GET', 'POST'])
def upload():
    return render_template('index.html', template_file=app.config['TEMPLATE_FILE'])

@app.route("/results", methods=['GET', 'POST'])
def results():
    if 'file' not in request.files:
        flash('No file chosen', 'danger')
        return redirect(request.url)
    file = request.files['file']
    if file.filename == '':
        flash('No selected file', 'danger')
        return redirect(request.url)
    filename = secure_filename(file.filename)
    if not allowed_file(file.filename):
        flash('Incorrect file extension. Must be .TXT!', 'danger')
    if places_exist(os.path.join(app.config['UPLOAD_FOLDER'], filename)) == False:
        flash('There is an affiliation missing from your Place list. Please re-try.', 'danger')
        return redirect(request.url)
    else:
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))

        os.chdir('/Users/cdastmalchi/Desktop/author_script/')

        if request.form['affiliation'] == "letter":
            affiliation = "let"
        elif request.form['affiliation'] == "number":
            affiliation = "num"

        if "Yes sep period" in request.form.getlist('period'):
            period = "y"
        else:
            period = "n"
        if "Yes sep space" in request.form.getlist('space'):
            space = "y"
        else:
            space = "n"

        proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(file.filename, period, space, affiliation), shell=True, stdout=subprocess.PIPE)
        # Wait until process terminates
        while proc.poll() is None:
            time.sleep(0.5)

        with open("authorList.html") as f:
            data = ''.join(f.readlines())
            print(data)
        return data
Share Improve this question edited Apr 26, 2019 at 15:51 claudiadast asked Apr 23, 2019 at 20:24 claudiadastclaudiadast 6093 gold badges13 silver badges34 bronze badges 4
  • The trouble is that the returned value, data, in the success handler of the $.ajax request is actually your entire index.html file again. Is data in the ajax request scope supposed to be storing the value of ''.join(data), created on the Python side? – Ajax1234 Commented Apr 25, 2019 at 23:10
  • @Ajax1234, yes so how I'd like it is to have success in the $.ajax return the value of ' '.join(data) from the python side (which is the form's html result that I want to display on the same page). – claudiadast Commented Apr 25, 2019 at 23:16
  • Ok, just one more thing: file is not defined in app.py, can you clarify? – Ajax1234 Commented Apr 25, 2019 at 23:34
  • Ah yes, sorry that is in my results route, for which I did not include the full function initially, but it's all there now in Update 2! – claudiadast Commented Apr 26, 2019 at 15:53
Add a ment  | 

2 Answers 2

Reset to default 2

To upload a file dynamically, you need to use the FormData object in Javascript with a POST request. This solution sends two separate requests: the POST request with the file data, and a GET request with the additional values. The filename is stored in flask.session to be used in the context of the GET request when the final data is puted:

First, in your app.py, you need three routes: a route to render index.html, a route to handle the file data, and lastly, a route to return the html:

app.py:

import flask, string, random
import json
app = flask.Flask(__name__)
app.secret_key = ''.join(random.choice(string.ascii_letters) for _ in range(20)) #needed to use flask.session
@app.route('/', methods=['GET'])
def home():
   return flask.render_template('index.html')

@app.route('/process_file', methods=['POST'])
def process_file():
  #here, you can run all the checks as before, but instead of flash, you can return jsonified results to read in the front-end
  if 'file' not in flask.request.files or not flask.request.files['file'].filename:
     return flask.jsonify({'result':'False', 'message':'no files selected'})
  file = flask.request.files['file']
  filename = secure_filename(file.filename)
  if not allowed_file(file.filename):
     return flask.jsonify({'result':'False', 'message':'Must be TXT file!'})
  if not places_exist(os.path.join(app.config['UPLOAD_FOLDER'], filename)):
     return flask.jsonify({'result':'False', 'message':'There is an affiliation missing from your Place list. Please re-try.'})
  file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
  flask.session['filename'] = filename
  return flask.jsonify({'result':'True'})

@app.route('/upload_vals')
def upload_vals():
  payload = json.loads(flask.request.args.get('payload'))
  #do something with payload
  #payload will now be in the form:
  #{'affiliation':'Number', 'period':'no', 'space':'yes'}
  proc = subprocess.Popen('python author_script.py {} -p {} -s {} -m {}'.format(flask.session['filename'], 'y' if _checked['period'] else 'n', 'y' if _checked['space'] else 'n', aff[:3]), shell=True, stdout=subprocess.PIPE)
  while proc.poll() is None:
     time.sleep(0.5)
  with open("authorList.html") as f:
    data = ''.join(f.readlines())
  return flask.jsonify({'data':data})

index.html:

<html>
 <head>
  <script src="http://ajax.googleapis./ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  </head>
  <body>
  </body>
    <div class='wrapper'>
      <p>Download the template file below and re-upload with your custom author information:</p>
      <a href="static/ExampleAuthorList.txt" download="Authors_Template.txt"><button type="button">Download</button></a><br><br>
        <form action="" id="myform" method=post enctype=multipart/form-data>
        <input type=file name=file value="Choose File">
           <p class='error_message'></p>
        </form>
         <div id="buttonDiv"> 
            <p>Mark affiliations with:</p> 
            <input type="radio" name="affiliation" value="number" data-key='affiliation' data-value='number' class="form-radio main_checkbox" checked><label for="number">Number</label><br> 
            <input type="radio" name="affiliation" value="letter" data-key='affiliation' data-value='letter' class="form-radio main_checkbox"><label for="letter">Letter</label><br> 
           <p>Separate initials with period:</p> 
           <input type="radio" name="period" value="separated" data-key='period' data-value='yes' class="form-radio period"><label for="period">Yes</label><br> 
           <input type="radio" name="period" data-key='period' data-value='no' value="separated" class="form-radio period" checked><label for="period">No</label> 
           <br> 
           <p>Separate initials with space:</p> 
           <input type="radio" name="space" value="separated" data-key='space' data-value='yes' class="form-radio spacing"><label for="space">Yes</label><br> 
           <input type="radio" name="space" data-key='space' data-value='no' value="separated" class="form-radio spacing" checked><label for="space">No</label><br> 
          <br><br> 
         </div> 
        <button class='submit_data'>Submit</button>
        <div>
         <br>
         <p class="my-results"></p>
        </div>
      </div>
    <script>
      $(document).ready(function(){
        $('.wrapper').on('click', '.submit_data', function(){
             var form_data = new FormData($('#myform')[0]);
             var flag = true;
             $.ajax({
               type: 'POST',
               url: '/process_file',
               data: form_data,
               contentType: false,
               cache: false,
               processData: false,
               success: function(data) {
                 if (data.result === 'False'){
                    $('.error_message').html(data.message)
                    flag = false;
                 }
              },
           });
           if (flag){
             var payload = {};
             $('.form-radio').each(function(){
                if ($(this).prop('checked')){
                  payload[$(this).data('key')] = $(this).data('value');
                }
             });
             $.ajax({
               url: "/upload_vals",
               type: "get",
               data: {'payload':JSON.stringify(payload)},
               success: function(response) {
                 $(".my-results").html(response.data);
               },
              });
           }
        });
      });
    </script>
</html>

It is slightly unclear in your updated app.py where the period and space form values originate from in the HTML, however, in the the index.html above, two additional checkboxes are provided to receive this value from the user.

To fix your syntax error put quotes around {{ data }}.

d.innerHTML = "{{ data }}";

Without the quotes the result is

d.innerHTML = ;

But don't worry because you need to move that code anyway.

The JavaScript in the second <script> tag doesn't know about the data because it is out of scope. You need to move that code into your $.ajax success method. This should work better:

$.ajax({
  type: "POST",
  url: url,
  data: $('form').serialize(), 
  success: function (data) {
    var d = document.createElement('div');
    d.innerHTML = data;
  }
});

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745276653a4620082.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信