I develop website on Flask and AngularJS. I need to send a form whith AJAX using AngularJS but it requires a custom attribute for input field. For example I have a form in Jinja2 template:
<form method="post" action="/">
{{ form.hidden_tag() }}
{{ form.name(placeholder="Name") }}
</form>
So how can I add an attribute from AngularJS lets say "ng-model" for my "name" field?
Thanks for your help!
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.
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.
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') }}
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="">