84

I want to add a placeholder attribute on to the field in WTForms. How can I do it?

abc = TextField('abc', validators=[Required(), Length(min=3, max=30)], placeholder="test")

The above code is not valid

How can I add a placeholder attribute with value?

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
TheOneTeam
  • 25,806
  • 45
  • 116
  • 158

4 Answers4

156

Updated for WTForms 2.1

You can now as of WTForms 2.1 (December 2015) set rendering keywords by using the render_kw= parameter to the field constructor.

So the field would look like:

abc = StringField('abc', [InputRequired()], render_kw={"placeholder": "test"})

Note while this is possible; it does start to bridge the line between code and presentation; so use it wisely!


(Old answer, still true for versions older than WTForms 2.1)

placeholder is not supported in the Python constructor in WTforms 2.0.x and below.

However, you can do this easily in your template:

{{ form.abc(placeholder="test") }}
Samuel Harmer
  • 4,264
  • 5
  • 33
  • 67
Crast
  • 15,996
  • 5
  • 45
  • 53
  • 2
    u are right, placeholder should be style and should not add in the structural elements. – TheOneTeam Mar 23 '12 at 08:00
  • 1
    But what if I use a macro and and being called inside a loop that iterates through form fields. How can i check inside the macro that a field has additional html attributes? – Marconi Oct 07 '12 at 02:08
  • 17
    A placeholder is the same thing as a label. It is, therefore, content and not style. Setting it in a template makes it impossible to have generic templates. – Milimetric Jun 28 '13 at 17:19
  • 12
    The `description` keyword argument of WTForms fields is allowed to be set at field construction, and is not inspected, just copied directly onto the field, and thus can be any value, not just a string, even a custom attribute. If you want to carry over your own metadata, you can simply use this to carry over any data you may want: `TextField(..., description={'placeholder': foo', 'class': bar}` (or even a custom class) then use this attribute in your template for any special metadata you want. – Crast Jul 26 '13 at 23:06
  • 3
    To add the label of the field you can write: `{{ form.abc(placeholder=form.abc.label.text) }}` – danbruegge Jun 10 '14 at 12:54
  • 1
    To summarize, use: `name = StringField('Name', description='John Smith')`, and in the template: `{{ form.name(placeholder=form.name.description) }}` – bcb Feb 02 '15 at 04:53
  • 2
    I am down voting this answer, I cannot understand how someone can think that placeholder is a display setting. :D Plus solution does not really work for anything that is more than hard-coded form in your template. – Drachenfels Apr 21 '15 at 13:17
  • @Drachenfels, I agree, what is difference from placeholder and label. – Nikolai Golub Oct 02 '15 at 14:47
10

Correct answer is as follows:

abc = TextField('abc', validators=[Required(), Length(min=3, max=30)], description="test")

As one can read in documenatation:

description – A description for the field, typically used for help text.

Then in your template:

{% import 'forms.html' as forms %}

{% for field in form %}
    {{ forms.render_field(field) }}
{% endfor %}

Where render_field is a macro that is defined in forms.html:

{% macro render_field(field) -%}

{% if field.type == 'CSRFTokenField' %}
    {{ field }}

    {% if field.errors %}
        <div class="warning">You have submitted an invalid CSRF token</div>
    {% endif %}
{% elif field.type == 'HiddenField' %}
    {{ field }}
{# any other special case you may need #}
{% else %}
    <div class="form-group">
        <label for="{{ field.label.field_id }}" class="col-sm-2 control-label">{{ field.label.text }}</label>
        <div class="col-sm-10">
            {{ field(placeholder=field.description) }}
            {% if field.errors %}
                <div class="alert alert-danger" role="alert">
                {% for err in field.errors %}
                    <p>{{ err|e }}</p>
                {% endfor %}
                </div>
            {% endif %}
        </div>
    </div>
{% endif %}

{%- endmacro %}
Drachenfels
  • 3,037
  • 2
  • 32
  • 47
6
{{ form.username(class="input", placeholder="Please enter your username") }} 
JeffUK
  • 4,107
  • 2
  • 20
  • 34
2

My solution is use a custom widget:

from flask.ext.wtf import Form
from wtforms import StringField, validators
from wtforms.widgets import Input


class CustomInput(Input):
    input_type = None

    def __init__(self, input_type=None, **kwargs):
        self.params = kwargs
        super(CustomInput, self).__init__(input_type=input_type)

    def __call__(self, field, **kwargs):
        for param, value in self.params.iteritems():
            kwargs.setdefault(param, value)
        return super(CustomInput, self).__call__(field, **kwargs)


class CustomTextInput(CustomInput):
    input_type = 'text'


class EditProfileForm(Form):
    first_name = StringField('First name',
                             validators=[validators.DataRequired()],
                             widget=CustomTextInput(placeholder='Enter first name'))

Maybe it's not ellegant, but it allows to use Flask-Bootstrap and define your forms in the forms code, not in the template

Nikolai Golub
  • 3,327
  • 4
  • 31
  • 61