neekfenwick::blog

Stuff that seemed important at the time.

dijit.form.Form Validation

HTML5 has introduced a new attribute for plain DOM nodes: required. This, unfortunately, clashes with the long standing dijit attribute required.

Today I found that putting required: true on a dijit.form.RadioButton appears to validate and block form submission, but in a rather confusing way that I thought I’d share.

Long story short: RadioButton does not support required: true .. but if you provide required: true in data-dojo-props or specify required="required" on the DOM node, you will get built-in browser based HTML5 friendly form validation jumping in the way of your existing dijit.form.Form validation. This means you’re using two entirely separate means of form validation, leading to a (probably) unsatisfactory user experience.

This code and advice applies to Dojo 1.7.2, especially the parts that use the AMD loader syntax (require()) and dojo/on event handling.

A quick grep of the code shows me that required is not a base attribute shared by many dijits. Rather, it’s an attribute that exists on a few (FilteringSelect, ValidationTextBox, etc.) and is used in their own internal validate() functions. So, it is not the case that the Form they may be contained in checks for their required attribute and validates them accordingly. Form will call validate() on every dijit that has a validate attribute.

_FormMixin.js
1
    var valid = widget.disabled || !widget.validate || widget.validate();

Those dijits that happen to have a validate member will have it invoked as a function and it had better return a sensible truthy value.

What not to do

Don’t slap required: true on your RadioButton and hope that dijit.form.Form will play nicely with it.

mixing_dijit_and_html5.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<form data-dojo-type="dijit.form.Form" id="filterReminderForm" action="" method="post">

  <div class="row">
    <label class="generic">Were filters purchased?</label>
    <label for="filters_yes">Yes</label><input data-dojo-type="dijit.form.RadioButton"
                                              id="filters_yes" name="filters_purchased"
                                              value="yes"
                                              data-dojo-props="required: true">
    <label for="filters_no">No</label><input data-dojo-type="dijit.form.RadioButton"
                                              id="filters_no" name="filters_purchased"
                                              value="no"
                                              data-dojo-props="required: true">
  </div>

  <div class="row">
    <label class="generic">Customers Name</label>
    <input data-dojo-type="dijit.form.ValidationTextBox" name="customers_name"
           data-dojo-props="required: true">
  </div>


  <button data-dojo-type="dijit.form.Button" type="submit">Save order details</button>
</form>

This form opens with neither radio button checked (which in itself is regarded a sin by some but that’s a different argument). You will find that when you hit the Submit button when neither radio button is checked, the form does not submit, the dijit.form.Form onSubmit() framework is not even entered, yet the first radio button is focussed. This feels a lot like traditional dijit.form.Form validation gone wrong, where the Form focusses, and usually highlights with an error message, the first invalid field found.

What’s actually happening is the browser is handling the form submit action and noticing that the <input>s for the RadioButtons have required set on them, it sees that neither is checked, and it prevents form submission before the dijit.form.Form even knows about it.

What to do

Passing required: true to the RadioButtons in the previous example was wrong. It applies the required attribute to the <input> DOM node and the browser takes over. Rather, handle the validation of your radio buttons yourself in an onSubmit handler.

doing_it_the_dijit_way
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<script type="text/javascript">
require([
  'dojo/parser',
  'dijit/registry',
  'dojo/on',
  'mydijits/AdminDeps',
  'dijit/form/Textarea',
  'dijit/form/FilteringSelect',
  'dojo/data/ItemFileReadStore',
  'dijit/form/Form',
  'dijit/form/Button',
  'dijit/form/RadioButton',
  'dojo/domReady!'
], function(parser, registry, on) {
  parser.parse();
  
  on(registry.byId('filterReminderForm'), 'submit', function(e) {
    console.log("submit! this: ", this);
    if (!this.validate()) return;
    // RadioButton provides no dijit validation
    if (registry.byId('filters_yes').get('value') != 'on'
      && registry.byId('filters_no').get('value') != 'on') {
      registry.byId('filters_yes').focus();
      alert("Please specify whether filters were purchased or not.");
      e.preventDefault();
      return;
    }
    // .. any other code here.  Remember to prevent the event 'e' if you want to stop form submission.
  })
})
</script>

<form data-dojo-type="dijit.form.Form" id="filterReminderForm" action="" method="post">
  
  <div class="row">
    <label class="generic">Were filters purchased?</label>
    <label for="filters_yes">Yes</label><input data-dojo-type="dijit.form.RadioButton"
                                              id="filters_yes" name="filters_purchased"
                                              value="yes">
    <label for="filters_no">No</label><input data-dojo-type="dijit.form.RadioButton"
                                              id="filters_no" name="filters_purchased"
                                              value="no">
  </div>

  <div class="row">
    <label class="generic">Customers Name</label>
    <input data-dojo-type="dijit.form.ValidationTextBox" name="customers_name"
           data-dojo-props="required: true">
  </div>


  <button data-dojo-type="dijit.form.Button" type="submit">Save order details</button>
</form>

I’ve removed the required: true from the RadioButtons, and included some of my onSubmit handler javascript to show where I call validate() on my form. I have to check the RadioButton values myself in addition to calling the built-in validate() function. What this does it return control to me about form validation and user notification, for example popping up a Tooltip.