简体   繁体   中英

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

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; 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.com/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

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;
  }
});

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 computed:

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.com/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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM