Custom attributes for Flask WTForms
{{ form.username(placeholder='your name', size=20, **{'ng-model':'hello', 'class':'hello'}) }}
I think is better
Dashes are not permitted in Python identifiers, and only Python identifiers can be used as keyword_argument=value
pairs in a call.
But you have several options to work around that here; you can pass in the ng-
prefixed options in a **kwargs
mapping, have the Meta
class you use for the form translate _
to -
for ng_
attributes, or use a custom widget to do the same translation.
Pass in a **kwargs mapping
With **kwargs
you can pass in arguments that are not Python identifiers, as long as they are strings. Use that to render your form fields:
{{ form.name(placeholder="Name", **{'ng-model': 'NameModel'}) }}
You can put the same information in the render_kw
mapping on the field definition:
class MyForm(Form):
name = StringField(u'Full Name', render_kw={'ng-model': 'NameModel'})
and it'll be used every time you render the field; render_kw
is added to whatever arguments you pass in when you render, so:
{{ form.name(placeholder="Name") }}
would render both placeholder
and ng-model
attributes.
Subclass Meta and use that in your form
As of WTForm 2.0, the Meta
class you attach to your form is actually asked to render fields with the Meta.render_field()
hook:
import wtform.meta
class AngularJSMeta:
def render_field(self, field, render_kw):
ng_keys = [key for key in render_kw if key.startswith('ng_')]
for key in ng_keys:
render_kw['ng-' + key[3:]] = render_kw.pop(key)
# WTForm dynamically constructs a Meta class from all Meta's on the
# form MRO, so we can use super() here:
return super(AngularJSMeta, self).render_field(field, render_kw)
Either use that directly on your form:
class MyForm(Form):
Meta = AngularJSMeta
name = StringField(u'Full Name')
or subclass the Form
class:
class BaseAngularJSForm(Form):
Meta = AngularJSMeta
and use that as the base for all your forms:
class MyForm(BaseAngularJSForm):
name = StringField(u'Full Name')
and now you can use this is your template with:
{{ form.name(placeholder="Name", ng_model='NameModel') }}
Subclass widgets
You could subclass the widget of your choice with:
class AngularJSMixin(object):
def __call__(self, field, **kwargs):
for key in list(kwargs):
if key.startswith('ng_'):
kwargs['ng-' + key[3:]] = kwargs.pop(key)
return super(AngularJSMixin, self).__call__(field, **kwargs)
class AngularJSTextInput(AngularJSMixin, TextInput):
pass
This translates any keyword argument starting with ng_
to a keyword argument starting with ng-
, ensuring the right HTML attributes can be added. The AngularJSMixin
can be used with any of the widget classes.
Use this as a widget
attribute on your field:
class MyForm(Form):
name = StringField(u'Full Name', widget=AngularJSTextInput())
and again you can use ng_model
when renderig your template:
{{ form.name(placeholder="Name", ng_model='NameModel') }}
In all cases the attributes will be added as placeholder="Name" ng-model="NameModel"
in the rendered HTML:
<input id="name" name="name" ng-model="NameModel" placeholder="Name" type="text" value="">