Widgets

A widget is a representation of an HTML input element. The widget handles the rendering of the HTML, and the extraction of data from a data object that corresponds to how the widget’s values(s) would be submitted by a form.

Tip

Widgets should not be confused with the form fields. Form fields deal with the logic of input validation and are used directly in templates. Widgets deal with rendering of HTML form input elements on the web page and extraction of raw submitted data. However, widgets do need to be assigned to form fields.

Specifying widgets

Whenever you specify a field on a form, newforms will use a default widget that is appropriate to the type of data that is to be displayed. To find which widget is used on which field, see the documentation about Build-in Field types.

However, if you want to use a different widget for a field, you can just use the widget argument on the field definition. For example:

var CommentForm = forms.Form.extend({
  name: forms.CharField(),
  url: forms.URLField(),
  comment: forms.CharField({widget: forms.Textarea})
})

This would specify a form with a comment that uses a larger Textarea() widget, rather than the default TextInput() widget.

Setting arguments for widgets

Many widgets have optional extra arguments; they can be set when defining the widget on the field. In the following example, we set additional HTML attributes to be added to the TextArea to control its display:

var CommentForm = forms.Form.extend({
  name: forms.CharField(),
   url: forms.URLField(),
   comment: forms.CharField({
    widget: forms.Textarea({attrs: {rows: 6, cols: 60}})
  })
})

See the Built-in widgets for more information about which widgets are available and which arguments they accept.

Customising widget attributes

New in version 0.11.

Sometimes you just need to add some extra attributes to a field’s default widget. Instead of completely redefining the widget as shown above, you can provide extra attributes using the field’s widgetAttrs argument. For example, if we want focus to be given to the name field when the form is first rendered:

var CommentForm = forms.Form.extend({
  name: forms.CharField({widgetAttrs: {autoFocus: true}}),
  url: forms.URLField(),
  comment: forms.CharField({widget: forms.Textarea})
})

Widgets inheriting from the Select widget

Widgets inheriting from the Select() widget deal with choices. They present the user with a list of options to choose from. The different widgets present this choice differently; the Select() widget itself uses a <select> HTML list representation, while RadioSelect() uses radio buttons.

Select() widgets are used by default on ChoiceField() fields. The choices displayed on the widget are inherited from the ChoiceField() and setting new choices with ChoiceField#setChoices() will update Select.choices. For example:

var CHOICES = [['1', 'First'], ['2', 'Second']]
var field = forms.ChoiceField({choices: CHOICES, widget: forms.RadioSelect})
print(field.choices())
// => [['1', 'First'], ['2', 'Second']]
print(field.widget.choices
// => [['1', 'First'], ['2', 'Second']]
field.widget.choices = []
field.setChoices([['1', 'First and only']])
print(field.widget.choices)
// => [['1', 'First and only']]

Widgets which offer a choices property can however be used with fields which are not based on choice – such as a CharField() – but it is recommended to use a ChoiceField()-based field when the choices are inherent to the model and not just the representational widget.

Customising widget instances

Widgets are rendered with minimal markup - by default there are no CSS class names applied, or any other widget-specific attributes. This means, for example, that all TextInput() widgets will appear the same on your pages.

Styling widget instances

If you want to make one widget instance look different from another, you will need to specify additional attributes at the time when the widget object is instantiated and assigned to a form field (and perhaps add some rules to your CSS files).

For example, take the following simple form:

var CommentForm = forms.Form.extend({
  name: forms.CharField(),
  url: forms.URLField(),
  comment: forms.CharField()
})

This form will include three default TextInput() widgets, with default rendering – no CSS class, no extra attributes. This means that the input boxes provided for each widget will be rendered exactly the same:

var f = new CommentForm({autoId: false})
print(reactHTML(<RenderForm form={f}/>))
/* =>
<div>
  <div>Name: <input type="text" name="name"></div>
  <div>Url: <input type="url" name="url"></div>
  <div>Comment: <input type="text" name="comment"></div>
</div>
*/

On a real Web page, you probably don’t want every widget to look the same. You might want a larger input element for the comment, and you might want the ‘name’ widget to have some special CSS class. It is also possible to specify the ‘type’ attribute to take advantage of the new HTML5 input types. To do this, you use the Widget.attrs argument when creating the widget:

var CommentForm = forms.Form.extend({
  name: forms.CharField({
    widget: forms.TextInput({attrs: {className: 'special'}})
  }),
  url: forms.URLField(),
  comment: forms.CharField({widget: forms.TextInput({attrs: {size: '40'}})
})

Note

Widgets are rendered as ReactElement objects – in the example above, we used className instead of class as React has standardised on the JavaScript-safe versions of attribute names, which avoid conflicting with JavaScript reserved words.

The extra attributes will then be included in the rendered output:

var f = new CommentForm({autoId: false})
print(reactHTML(<RenderForm form={f}/>))
/* =>
<div>
  <div>Name: <input class="special" type="text" name="name"></div>
  <div>Url: <input type="url" name="url"></div>
  <div>Comment: <input size="40" type="text" name="comment"></div>
</div>
*/

You can also set the HTML id using Widget.attrs.

Base Widgets

Base widgets Widget() and MultiWidget() are extended by all the built-in widgets and may serve as a foundation for custom widgets.

Widget()

This abstract widget cannot be rendered, but provides the basic attribute Widget.attrs. You may also implement or override the render() method on custom widgets.

widget.attrs

An object containing HTML attributes to be set on the rendered widget:

var name = forms.TextInput({attrs: {size:10, title: 'Your name'}})
print(reactHTML(name.render('name', 'A name')))
// => <input size="10" title="Your name" type="text" name="name" value="A name">"

Key Widget methods are:

Widget#render()

Returns a ReactElement representation of the widget. This method must be implemented by extending widgets, or an Error will be thrown.

The ‘value’ given is not guaranteed to be valid input, therefore extending widgets should program defensively.

Widget#valueFromData()
Given an object containing input data and this widget’s name, returns the value of this widget. Returns null if a value wasn’t provided.

MultiWidget()

A widget that is composed of multiple widgets. MultiWidget() works hand in hand with the MultiValueField().

MultiWidget has one required argument:

MultiWidget.widgets
A list containing the widgets needed.

And one required method:

MultiWidget#decompress()

This method takes a single “compressed” value from the field and returns a list of “decompressed” values. The input value can be assumed valid, but not necessarily non-empty.

This method must be implemented by the widgets extending MultiWidget, and since the value may be empty, the implementation must be defensive.

The rationale behind “decompression” is that it is necessary to “split” the combined value of the form field into the values for each widget.

An example of this is how SplitDateTimeWidget() turns a Date value into a list with date and time split into two separate values.

Tip

Note that MultiValueField() has a complementary method MultiValueField#compress() with the opposite responsibility - to combine cleaned values of all member fields into one.

Other methods that may be useful to implement include:

MultiWidget#render()

The value argument must be handled differently in this method then in Widget#render() because it has to figure out how to split a single value for display in multiple widgets.

The value argument used when rendering can be one of two things:

  • A list.
  • A single value (e.g., a string) that is the “compressed” representation of a list of values.

If value is a list, the output of MultiWidget#render() will be a concatenation of rendered child widgets. If value is not a list, it will first be processed by the method MultiWidget#decompress() to create the list and then rendered.

When render() runs, each value in the list is rendered with the corresponding widget – the first value is rendered in the first widget, the second value is rendered in the second widget, etc.

Unlike in the single value widgets, render() doesn’t have to be implemented by extending widgets.

MultiWidget#formatOutput()
Given a list of rendered widgets (as ReactElement objects), returns the list or a ReactElement object containing the widgets. This hook allows you to lay out the widgets any way you’d like.

Here’s an example widget which extends MultiWidget() to display a date with the day, month, and year in different select boxes. This widget is intended to be used with a DateField() rather than a MultiValueField(), so we’ve implemented Widget#valueFromData():

var DateSelectorWidget = forms.MultiWidget.extend({
  constructor: function(kwargs) {
    kwargs = extend({attrs: {}}, kwargs)
    widgets = [
      forms.Select({choices: range(1, 32), attrs: kwargs.attrs}),
      forms.Select({choices: range(1, 13), attrs: kwargs.attrs}),
      forms.Select({choices: range(2012, 2017), attrs: kwargs.attrs})
    ]
    forms.MultiWidget.call(this, widgets, kwargs)
  },

  decompress: function(value) {
    if (value instanceof Date) {
      return [value.getDate(),
              value.getMonth() + 1, // Make month 1-based for display
              value.getFullYear()]
    }
    return [null, null, null]
  },

  formatOutput: function(renderedWidgets) {
    return React.createElement('div', null, renderedWidgets)
  },

  valueFromData: function(data, files, name) {
    var parts = this.widgets.map(function(widget, i) {
      return widget.valueFromData(data, files, name + '_' + i)
    })
    parts.reverse() // [d, m, y] => [y, m, d]
    return parts.join('-')
  }
})

The constructor creates several Select() widgets in a list. The “super” constructor uses this list to setup the widget.

The MultiWidget#formatOutput() method is fairly vanilla here (in fact, it’s the same as what’s been implemented as the default for MultiWidget), but the idea is that you could add custom HTML between the widgets should you wish.

The required method MultiWidget#decompress() breaks up a Date value into the day, month, and year values corresponding to each widget. Note how the method handles the case where value is null.

The default implementation of Widget#valueFromData() returns a list of values corresponding to each Widget. This is appropriate when using a MultiWidget with a MultiValueField(), but since we want to use this widget with a DateField() which takes a single value, we have overridden this method to combine the data of all the subwidgets into a 'yyyy-mm-dd' formatted date string and returns it for validation by the DateField().

Built-in widgets

Newforms provides a representation of all the basic HTML widgets, plus some commonly used groups of widgets, including the input of text, various checkboxes and selectors, uploading files, and handling of multi-valued input.

Widgets handling input of text

These widgets make use of the HTML elements <input> and <textarea>.

TextInput()

Text input: <input type="text" ...>

NumberInput()

Text input: <input type="number" ...>

EmailInput()

Text input: <input type="email" ...>

URLInput()

Text input: <input type="url" ...>

PasswordInput()

Password input: <input type='password' ...>

Takes one optional argument:

  • PasswordInput.renderValue

    Determines whether the widget will have a value filled in when the form is re-displayed after a validation error (default is false).

Textarea()

Text area: <textarea>...</textarea>

HiddenInput()

Hidden input: <input type='hidden' ...>

Note that there also is a MultipleHiddenInput() widget that encapsulates a set of hidden input elements.

DateInput()

Date input as a simple text box: <input type='text' ...>

Takes same arguments as TextInput(), with one more optional argument:

  • DateInput.format

    The format in which this field’s initial value will be displayed.

If no format argument is provided, the default format is the first format found in the current locale’s DATE_INPUT_FORMATS.

DateTimeInput()

Date/time input as a simple text box: <input type='text' ...>

Takes same arguments as TextInput(), with one more optional argument:

  • DateTimeInput.format

    The format in which this field’s initial value will be displayed.

If no format argument is provided, the default format is the first format found in the current locale’s DATETIME_INPUT_FORMATS.

TimeInput()

Time input as a simple text box: <input type='text' ...>

Takes same arguments as TextInput(), with one more optional argument:

  • TimeInput.format

    The format in which this field’s initial value will be displayed.

If no format argument is provided, the default format is the first format found in the current locale’s TIME_INPUT_FORMATS.

Selector and checkbox widgets

CheckboxInput()

Checkbox: <input type='checkbox' ...>

Takes one optional argument:

  • CheckboxInput.checkTest

    A function that takes the value of the CheckBoxInput and returns true if the checkbox should be checked for that value.

Select()

Select widget: <select><option ...>...</select>

  • Select.choices

    This attribute is optional when the form field does not have a choices attribute. If it does, it will override anything you set here when the attribute is updated on the Field().

NullBooleanSelect()

Select widget with options ‘Unknown’, ‘Yes’ and ‘No’

SelectMultiple()

Similar to Select, but allows multiple selection: <select multiple='multiple'>...</select>

RadioSelect()

Similar to Select, but rendered as a list of radio buttons within <li> tags:

<ul>
  <li><input type='radio' ...></li>
  ...
</ul>

For more granular control over the generated markup, you can loop over the radio buttons. Assuming a form myform with a field beatles that uses a RadioSelect as its widget:

myForm.boundField('beatles').subWidgets().map(function(radio) {
  return <div className="myRadio">{radio.render()}</div>
})

This would generate the following HTML:

<div class="myRadio">
  <label for="id_beatles_0"><input id="id_beatles_0" type="radio" name="beatles" value="john"> John</label>
</div>
<div class="myRadio">
  <label for="id_beatles_1"><input id="id_beatles_1" type="radio" name="beatles" value="paul"> Paul</label>
</div>
<div class="myRadio">
  <label for="id_beatles_2"><input id="id_beatles_2" type="radio" name="beatles" value="george"> George</label>
</div>
<div class="myRadio">
  <label for="id_beatles_3"><input id="id_beatles_3" type="radio" name="beatles" value="ringo"> Ringo</label>
</div>

That included the <label> tags. To get more granular, you can use each radio button’s tag(), choiceLabel and idForLabel(). For example, this code...:

myForm.boundField('beatles').subWidgets().map(function(radio) {
  return <label htmlFor={radio.idForLabel()}>
    {radio.choiceLabel}
    <span className="radio">{radio.tag()}</span>
  </label>
})

...will result in the following HTML:

<label for="id_beatles_0">
  John
  <span class="radio"><input id="id_beatles_0" type="radio" name="beatles" value="john"></span>
</label>
<label for="id_beatles_1">
  Paul
  <span class="radio"><input id="id_beatles_1" type="radio" name="beatles" value="paul"></span>
</label>
<label for="id_beatles_2">
  George
  <span class="radio"><input id="id_beatles_2" type="radio" name="beatles" value="george"></span>
</label>
<label for="id_beatles_3">
  Ringo
  <span class="radio"><input id="id_beatles_3" type="radio" name="beatles" value="ringo"></span>
</label>

If you decide not to loop over the radio buttons – e.g., if your layout simply renders the beatles BoundField – they’ll be output in a <ul> with <li> tags, as above.

CheckboxSelectMultiple()

Similar to SelectMultiple(), but rendered as a list of check buttons:

<ul>
  <li><input type='checkbox' ...></li>
  ...
</ul>

Like RadioSelect(), you can loop over the individual checkboxes making up the lists.

File upload widgets

FileInput()

File upload input: <input type='file' ...>

ClearableFileInput()

File upload input: <input type='file' ...>, with an additional checkbox input to clear the field’s value, if the field is not required and has initial data.

Composite widgets

MultipleHiddenInput()

Multiple <input type='hidden' ...> widgets.

A widget that handles multiple hidden widgets for fields that have a list of values.

  • MultipleHiddenInput.choices

    This attribute is optional when the form field does not have a choices attribute. If it does, it will override anything you set here when the attribute is updated on the Field().

SplitDateTimeWidget()

Wrapper (using MultiWidget()) around two widgets: DateInput() for the date, and TimeInput() for the time.

SplitDateTimeWidget has two optional attributes:

  • SplitDateTimeWidget.dateFormat

    Similar to DateInput.format

  • SplitDateTimeWidget.timeFormat

    Similar to TimeInput.format

SplitHiddenDateTimeWidget()

Similar to SplitDateTimeWidget(), but uses HiddenInput() for both date and time.