Custom attributes for Flask WTForms Custom attributes for Flask WTForms flask flask

Custom attributes for Flask WTForms


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.metaclass 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="">


{{ form.username(placeholder='your name', size=20, **{'ng-model':'hello', 'class':'hello'}) }}

I think is better