Как передать строку HTML из приложения фляги в шаблон HTML как элемент DOM и затем appendChild - PullRequest
3 голосов
/ 23 апреля 2019

КОНТЕКСТ

Я создал веб-приложение Flask, которое имеет форму на веб-странице, и затем я хочу, чтобы пользователь сразу увидел результаты этой формы справа от формы. Когда форма отправляется, сценарий python активируется на бэкэнде, и в результате получается HTML-файл результатов (authorList.html), который я хочу отобразить рядом с формой.

ПРОБЛЕМА

Насколько я понимаю, это требует от меня использования Ajax, поэтому я могу получать результаты, не обновляя страницу, но я не так хорошо знаю, как передать этот файл результатов HTML из фляги app.py в шаблон HTML и затем добавьте это как узел к дереву DOM.

1) Мой код настроен до сих пор таким образом, что вызывается маршрут upload, который затем отображает index.html, а затем код JS там ссылается на маршрут results, который затем отображает index.html снова, передавая файл результатов HTML в виде строки. Я частично настроил шаг преобразования HTML-строки в DOM-элемент в index.html, где он говорит <p>Results should display here.</p> (так как это часть страницы, где я хочу отображать HTML-файл результатов), но я не уверен, что я на правильном пути и как приступить к выполнению приложения.

2) Кроме того, когда я пытаюсь запустить этот код ниже, чем он есть, я получаю ошибку JS Uncaught SyntaxError: Unexpected token ;, указывающую на эту строку index.html: var d = document.createElement('div'); d.innerHTML = ; return d.firstChild; ... это потому, что я не правильно передать переменную data на стороне приложения Flask? (РАЗРЕШЕНО И ОБНОВЛЕНО В КОДЕКСЕ СООТВЕТСТВУЮЩИМ)

(примечание: я не знаком с JS, поэтому, если это кажется простым, я заранее извиняюсь!)

ИСТОЧНИК КОД

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

Я попробовал следующее изменение в своем коде JS (в index.html), но все еще не вижу никаких результатов на главной странице.

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

ОБНОВЛЕНИЕ 2: полный 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

Ответы [ 2 ]

1 голос
/ 27 апреля 2019

Для динамической загрузки файла вам необходимо использовать объект FormData в Javascript с запросом POST. Это решение отправляет два отдельных запроса: запрос POST с данными файла и запрос GET с дополнительными значениями. Имя файла хранится в flask.session для использования в контексте запроса GET при вычислении окончательных данных:

Во-первых, в вашем app.py вам нужны три маршрута: маршрут для визуализации index.html, маршрут для обработки данных файла и, наконец, маршрут для возврата 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>

Немного неясно, в ваших обновленных app.py откуда взяты значения форм period и space в HTML, однако в приведенном выше index.html предусмотрены два дополнительных флажка для получения этого значения от пользователь.

1 голос
/ 24 апреля 2019

Чтобы исправить синтаксическую ошибку, заключите в кавычки {{ data }}.

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

Без кавычек результат равен

d.innerHTML = ;

Но не волнуйтесь, вам все равно нужно переместить этот код.

JavaScript во втором теге <script> не знает о data, потому что он находится вне области видимости. Вам нужно переместить этот код в ваш $.ajax метод успеха. Это должно работать лучше:

$.ajax({
  type: "POST",
  url: url,
  data: $('form').serialize(), 
  success: function (data) {
    var d = document.createElement('div');
    d.innerHTML = data;
  }
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...