javascripthtmlhotwire

Refresh (variable) part of an html page with hotwire (Flask app)



Context

I am building a simple "todo" flask app with an SQLAchemy database. The tasks are sorted by sections (check the image below to see how it is organized).

Once I implemented all the functionalities I wanted, I ran into the issue which was the whole page got refreshed each time I triggered a button (add/edit/delete/update_status). Then I found out hotwire which is amazing to handle this.

This my taskManager.html organization:

<!--New task button-->
<turbo-frame id="data_frame-add">
<button class="btn-add" onclick="openAddForm()">...</button>
<div class="form-popup" id="myForm">
    <form name="AddTaskForm" action="{{ url_for('add_task') }}" class="form-container" method="POST">
        <label for="section"><b>Section</b></label>
        <input type="text" id="section" name="section" required>
        <label for="content"><b>Task</b></label>
        <input type="text" id="content" name="content required>
        <button type="submit" class="btn">Add</button>
    </form>
</div>
</turbo-frame>

<!--Display sections and tasks-->
<div class="flex-row">
    {% for section in sections %}
        <turbo-frame id="data_frame-{{ section }}">
            <div class="flex-column">
                <h2>{{ section }}</h2>
                {% for task in tasks %}
                    {% if task.section == section %}
                        <p>{{ task }}</p>
                        <button class="satus"></button>
                        <button class="edit"></button>
                        <div class="form-popup" id="form-{{ task.id }}">...</div>
                        <button class="delete"></button>
                        <div class="form-popup" id="form-{{ task.id }}">...</div>
                    {% endif %}
                {% endfor %}
            </div>
        </turbo-frame>
    {% endfor %}
</div>

Page organization

Using a turbo frame with id data_frame-{{ section }} (one for each section) allowed to refresh only the concerned section when hitting status, edit and delete buttons (for example, hitting delete button of task 2 of Section 2 will only refresh the turbo frame data_frame-Section 2). However as the New task button is out of theses turbo frames, It works differently and this is a different challenge...


Issue

When adding a new task, I would like the section of the task (entered here <input type="text" id="section" name="section"...>) to be saved in a variable which will be used to target a specific <turbo-frame id="data_frame-{{ section }}"> and refresh it without refreshing the whole page. At the moment as the New task button is wrapped with <turbo-frame id="data_frame-add"> it is self contained (meaning if I'm adding a task 5 to Section 1 only the turbo frame with id data_frame-add is refreshed not the data_frame-Section 1 so I need to manually refresh the page to see changes)


What I tried

I added data-turbo-frame to the form:

<form name="AddTaskForm" action="{{ url_for('add_task') }}" class="form-container" method="POST" data-turbo-frame="data_frame-Section 1">

in order to be able to refresh the "data_frame-Section 1" when I add a New task in section Section 1, and it works! But I would like to make this data-turbo-frame="data_frame-<section>" with <section> a variable that get the value of <input type="text" id="section" name="section"...>

To achieve this I removed data-turbo-frame="data_frame-Section 1" in the form:

<form name="AddTaskForm" action="{{ url_for('add_task') }}" class="form-container" method="POST">

and added a Javascript part:

var sectionValVar = document.getElementById("section").value;
const sectionValPref = "data_frame-";
let sectionVal = sectionValPref + sectionValVar;
$(".AddTaskForm").attr("data-turbo-frame", sectionVal);

sectionVal is supposed to get the variable value "data_frame-<section>" and last line add "data-turbo-frame" = "data_frame-<section>" to the <form name="AddTaskForm"...>

But this doesn't work. I'm not sure if this even possible to make as it looks tricky... But if someone has any hint or fix for this It would be amazing !

Thank you !


Other ressources

This is my add_task route in my python flask app:

@app.route('/add', methods=['GET', 'POST'])
def add_task():
content = request.form.get('content')
section = request.form.get('section')
task = Task(content=content, section=section)
db.session.add(task)
db.session.commit()
return redirect(url_for('taskManager'))

Solution

  • This looks like it would work but I don't see an event listener to set the form data-turbo-frame attribute whenever the input value changes. You need to update the attribute either before the form submits or whenever the input gets updated.

    this is how you could do it with jquery

    $("#section").change(function() {
       let sectionAndPrefix = "data_frame-" + $("#section").val()
       $(".AddTaskForm").attr("data-turbo-frame", sectionAndPrefix);
    })
    

    in vanilla javascript

    const sectionInput = document.querySelector("#section")
    sectionInput.addEventListener("input", function() {
      const taskForm = document.querySelector(".AddTaskForm")
      const sectionAndPrefix = "data_frame-" + sectionInput.value
      taskForm.setAttribute("data-turbo-frame", sectionAndPrefix)
    })