flaskjinja2flask-wtformswtforms

How to update form values when using widgets


I am building a very simple color input form, and using the built in wtforms widget. This simply displays the color picker and doesn't allow for text input, but want to give the user the ability to do either. So I created another field with just text value.

How do I update my color/color1 fields when input from the other changes? I can't see anyway of attaching an "event" to the form entry. I'm very new to Python/Flask/WTForms/Jinja so the solution is probably obvious but I can't work it out!

This is my simple form class

class UserInput(FlaskForm):
  color = StringField('color', default="#00ff00")
  color1 = StringField('color1', widget=widgets.ColorInput(), default='#00ff00')
  submit = SubmitField('Go')

And this is my html code

<form action="" method="post">
     <p>
      {{form.color1.label}} <br>
      {{form.color}} {{form.color1}}
     </p>
     <p>
      {{form.submit()}}
     </p>
   </form>

Solution

  • Since jinja is rendered server-side, you cannot react immediately to user input so that one input field updates the other. To achieve this, you need JavaScript that is executed client-side.

    The following example registers a listener for the change event in both input fields. If the user changes the value of one field, the value of the other is automatically updated.

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Index</title>
    </head>
    <body>
        <form action="" method="post">
            {{ form.csrf_token }}
            <div>
                {{form.color1.label}}
                {{form.color}} {{form.color1}}
            </div>
            <div>
                {{form.submit()}}
            </div>
         </form>
    
         <script>
            (function() {
                const elems = ['color1', 'color'].map((id) => document.getElementById(id));
                elems.forEach(elem => {
                    elem.addEventListener('change', (event) => {
                        const value = event.target.value;
                        elems.forEach(el => el.value = value);
                    });
                });
            })();
         </script>
    </body>
    </html>