EchoX custom plugins can be included individually (though some have required dependencies), or all at once. Both echox-bootstrap.js and echox-bootstrap.min.js contain all plugins in a single file.

Background

Be sure to checkout the Javascript Overview for details on how to include and initialize plugins.

You can use all EchoX plugins purely through the markup API without writing a single line of JavaScript. This is the recommended approach.

About

A custom time picker component that permits incrementing and decrementing of the hours and minutes via the keyboard.

Changelog

Forked from: https://github.com/jdewit/bootstrap-timepicker with modifications.

  • Renamed class from 'Timepicker' to 'XTimepicker'
  • Renamed plugin function from 'timepicker' to 'xtimepicker'
  • Renamed all events from 'event_name.timepicker' to 'event_name.xtimepicker'
  • Removed options for using a 12 hour clock
  • Added a call to the inputmask at the end of the constructor
  • Added event bindings to modify the cursor position and selection
  • Fixed tabbing behavior and IE support
  • Added optional lazy initialization

Plugin dependency

Examples

Default timepicker. (24 hour mode, 1 minute increments)

<div class="form-group">
    <div class="input-group col-lg-2 col-md-3 col-sm-3 col-xs-5">
        <input id="timepicker1" class="form-control" type="text"/>
        <span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span>
    </div>
</div>
$('#timepicker1').xtimepicker();

Lazy init via data attributes

Note the data-provide="xtimepicker" - It is the attribute that lets you bind without JavaScript.

<div class="form-group">
    <div class="input-group col-lg-2 col-md-3 col-sm-3 col-xs-5">
        <input data-provide="xtimepicker" class="form-control" type="text"/>
        <span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span>
    </div>
</div>
<div class="form-group">
    <div class="input-group  col-lg-2 col-md-3 col-sm-3 col-xs-5">
        <input value="11:11" data-provide="xtimepicker" class="form-control" type="text"/>
        <span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span>
    </div>
</div>

Current time with a 15 minute step

$('#timepicker2').xtimepicker({
    minuteStep: 15,
    defaultTime: 'current'
});

Initialize via data elements on the input parent

If you use an input-group-addon you should put the data attributes on a parent element so that mousing over anything withing the parent will init the timepicker

<div class="form-group">
    <div class="input-group col-lg-2 col-md-3 col-sm-3 col-xs-5" data-provide="xtimepicker">
        <input class="form-control" type="text"/>
        <span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span>
    </div>
</div>

Data Attributes

Configuration options can also be set with the use of data attributes.

<div class="col-lg-2 col-md-3 col-sm-3 col-xs-5">
    <input id="timepicker6" class="form-control" type="text" data-minute-step="1" data-default-time="11:11"/>
</div>
//you still have to call the plugin to wire up the widget, unless you include the data-provide='true' attribute.
//In this case, we didnt include that attribute, so we must call the constructor, but we do not need to provide a config object.
$('#timepicker6').xtimepicker();

Usage

Name Options / Defaults Description
minuteStep 15 Specify a step for the minute field.
defaultTime 'current' (default)
'13:45'
false
Set to the current time.
Set to a specific time.
Do not set a default time

Using the defaultTime="current" together with lazy initialization will not work (as the user expects), since the value will not be set to the current time until the user mouses-over the input.

Methods

setTime

Set the time manually

$('#timepicker').xtimepicker('setTime', '15:45');

Update

Triggered when the date is updated

$('#timepicker').xtimepicker().on('changeTime.xtimepicker', function(e) {
    console.log('The time is ' + e.time.value);
    console.log('The hour is ' + e.time.hour);
    console.log('The minute is ' + e.time.minute);
});

About

A custom date picker component that permits changing the date via the keyboard. You can restrict the date picker component to only allow selections of date, month, or year.

Changelog

  • Created a wrapper plugin $.fn.xdatepicker which delegates to the bootstrap-datepicker and inputMask plugins

Plugin dependency

Examples

Month, Day, and Year

Attached to a field with the date format specified via options. The initial date value must match the date format.

<input type="text" value="02/16/2013" id="dp1" class="form-control">
$('#dp1').xdatepicker({
    format: 'mm/dd/yyyy'
});

Attached to a field with the format specified via data attributes. You can use the provide data attribute to make the datepicker initialize on focus or click.

<input type="text" value="02/16/2013" data-date-format="mm/dd/yyyy" data-provide="xdatepicker" class="form-control">

As a component.

You can use an input add-on to open the datepicker widget. In order to do that you must add your data-date-* attributes on the parent div. You must also add the class date to the parent div.

<form class="form-inline" role="form">
    <div class="form-group">
        <div class="date input-group col-lg-12 col-md-12 col-sm-12" data-date-format="mm-dd-yyyy" data-provide="xdatepicker">
            <input class="form-control" size="16" type="text" value="12-02-2013">
            <span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span></span>
        </div>
    </div>
</form>

Attached to non-field element, using events to work with the date values.

<button class="btn btn-small" id="dp100">Click Me</button>
$('#dp100').xdatepicker().on('changeDate', function(e){
    var y = e.date.getFullYear(),
    _m = e.date.getMonth() + 1,
    m = (_m > 9 ? _m : '0'+_m),
    _d = e.date.getDate(),
    d = (_d > 9 ? _d : '0'+_d);
    $(this).text(y + '-' + m + '-' + d);
});

Inline/embedded picker

<div id="dp101" data-date="12-02-2013" data-date-format="dd-mm-yyyy"  data-provide="xdatepicker"></div>

Note

The datepicker is created inline because it is attached to a DIV element

Year and Month

Start with years viewMode.

<input type="text" value="02/2013" readonly  data-provide="xdatepicker" data-date="01/2013" data-date-format="mm/yyyy" data-date-view-mode="years" data-date-min-view-mode="1" data-date-disable-mask="true" class="form-control">

Years Only

Limit the view mode to years.

<input type="text" value="02/2013" readonly data-provide="xdatepicker" data-date="02/2013" data-date-format="mm/yyyy" data-date-view-mode="months" data-date-min-view-mode="1" data-date-disable-mask="true" class="form-control">

Date Ranges

Attached to two elements that use events to validate the date range.

<form class="form-inline" role="form">
    <div class="form-group col-lg-2 col-md-2 col-sm-2">
        <label for="dp4">Start date:</label>
        <input id="dp4" type="text" class="form-control" value="2013-02-20" data-date-format="yyyy-mm-dd">
    </div>
    <div class="form-group col-lg-2 col-md-2 col-sm-2">
        <label for="dp5">End date:</label>
        <input id="dp5" type="text" class="form-control" value="2013-02-25" data-date-format="yyyy-mm-dd">
    </div>
</form>
var startDate = new Date(2013, 2, 20);
var endDate = new Date(2013, 2, 25);
$('#dp4').xdatepicker()
    .on('changeDate', function (ev) {
        if (ev.date.valueOf() > endDate.valueOf()) {
            alert('The start date can not be greater then the end date');
        } else {
            startDate = new Date(ev.date);
        }
        $('#dp4').xdatepicker('hide');
    });
$('#dp5').xdatepicker()
    .on('changeDate', function (ev) {
        if (ev.date.valueOf() < startDate.valueOf()) {
            alert('The end date can not be less then the start date');
        } else {
            endDate = new Date(ev.date);
            $('#endDate').text($('#dp5').data('date'));
        }
        $('#dp5').xdatepicker('hide');
    });

As a linked date range.

Both inputs must live under a single parent who's class is input-daterange. Initialize the datepicker this element to create a linked range.

to
<form class="form-inline" role="form">
    <div class="form-group">
        <div class="input-group input-daterange" id="daterange-example">
            <input class="form-control" size="16" type="text">
            <span class="input-group-addon">to</span>
            <input class="form-control" size="16" type="text">
        </div>
    </div>
</form>
$('#daterange-example.input-daterange').xdatepicker();

Disabling dates in the past and dependent disabling.

In order to enable and disable dates you can use setStartDate and setEndDate methods

//set a start date limit:
$('.datepicker').datepicker('setStartDate', '2012-01-01');
//remove a start date limit:
$('.datepicker').datepicker('setStartDate', null);

Datepicker Sandbox αlphα

Use the form below to tweak the options; results and code appear in realtime below.

As you change options, your address bar will update to reflect the current configuration (requires a browser with history.replaceState support). This url can be used to link to the sandbox with the given configuration pre-loaded.


Important Note

Not all date formats are supported by the input mask. Using an unsupported date format without disabling the input mask will have dire consequences for you and your family.

The date formats supported by the input mask are:

dd/mm/yyyy mm/dd/yyyy yyyy/mm/dd dd.mm.yyyy dd-mm-yyyy mm.dd.yyyy mm-dd-yyyy yyyy.mm.dd yyyy-mm-dd

Type:


Options:

Days of week disabled:


Sandbox HTML


    

Sandbox Javascript


    

Usage

Call the date picker via JavaScript:

$('.datepicker').xdatepicker();

Options

Name Type Default Description
format string 'mm/dd/yyyy' The date format, combination of dd, mm, and yyyy. Not all date formats are supported by the input mask. Only formats containing a combination of all 3 components mm dd yyyy.
disableMask boolean false Disables the date input mask.
weekStart number 0 Day of the week start. 0 (Sunday) to 6 (Saturday)
calendarWeeks boolean false Whether or not to show week numbers to the left of week rows.
startDate date -Infinity The earliest date that may be selected; all earlier dates will be disabled.
endDate date Infinity The latest date that may be selected; all later dates will be disabled.
daysOfWeekDisabled string, array [] Days of the week that should be disabled. Values are 0 (Sunday) to 6 (Saturday). Multiple values should be comma-separated. Example: disable weekends: '0,6' or [0,6].
autoclose boolean true Whether or not to close the datepicker immediately when a date is selected.
startView number, string 0, 'month' The view that the datepicker should show when it is opened. Accepts values of 0 or 'month' for month view (the default), 1 or 'year' for the 12-month overview, and 2 or 'decade' for the 10-year overview. Useful for date-of-birth datepickers.
minViewMode number, string 0, 'days' Set a limit for the view mode. Accepts: 'days' or 0, 'months' or 1, and 'years' or 2. Gives the ability to pick only a month or an year. The day is set to the 1st for 'months', and the month is set to January for 'years'.
todayBtn boolean, "linked" false If true or "linked", displays a "Today" button at the bottom of the datepicker to select the current date. If true, the "Today" button will only move the current date into view; if "linked", the current date will also be selected.
todayHighlight boolean true If true, highlights the current date.
clearBtn boolean false If true, displays a "Clear" button at the bottom of the datepicker to clear the input value. If "autoclose" is also set to true, this button will also close the datepicker.
keyboardNavigation boolean true Whether or not to allow date navigation by arrow keys.
forceParse boolean true Whether or not to force parsing of the input value when the picker is closed. That is, when an invalid date is left in the input field by the user, the picker will forcibly parse that value, and set the input's value to the new, valid date, conforming to the given format.
inputs array none A list of inputs to be used in a range picker, which will be attached to the selected element. Allows for explicitly creating a range picker on a non-standard element.
beforeShowDay Function(Date) $.noop A function that takes a date as a parameter and returns one of the following values:
  • undefined to have no effect
  • A Boolean, indicating whether or not this date is selectable
  • A String representing additional CSS classes to apply to the date's cell
  • An object with the following properties:
    • `enabled`: same as the Boolean value above
    • `classes`: same as the String value above
    • `tooltip`: a tooltip to apply to this date, via the `title` HTML attribute
orientation string "auto"

A space-separated string consisting of one or two of "left" or "right", "top" or "bottom", and "auto" (may be omitted); for example, "top left", "bottom" (horizontal orientation will default to "auto"), "right" (vertical orientation will default to "auto"), "auto top". Allows for fixed placement of the picker popup.

"orientation" refers to the location of the picker popup's "anchor"; you can also think of it as the location of the trigger element (input, component, etc) relative to the picker.

"auto" triggers "smart orientation" of the picker. Horizontal orientation will default to "left" and left offset will be tweaked to keep the picker inside the browser viewport; vertical orientation will simply choose "top" or "bottom", whichever will show more of the picker in the viewport.

Methods

Method Args Type ? Description
.datepicker(options) Initializes an datepicker.
remove Remove the datepicker. Removes attached events, internal attached objects, and added HTML elements.
show Show the datepicker.
hide Hide the datepicker.
update date string Update the datepicker with the current input value or given date as argument. In second case input will be updated as well.
setDate date date Sets the internal date. date is assumed to be a "local" date object, and will be converted to UTC for internal use.
setUTCDate date date Sets the internal date. date is assumed to be a UTC date object, and will not be converted.
getDate Returns a localized date object representing the internal date object of the first datepicker in the selection.
getUTCDate Returns the internal UTC date object, as-is and unconverted to local time, of the first datepicker in the selection.
setStartDate date string Sets a new lower date limit on the datepicker. Omit date (or provide an otherwise falsey value) to unset the limit.
setEndDate date string Sets a new upper date limit on the datepicker. Omit date (or provide an otherwise falsey value) to unset the limit.
setDaysOfWeekDisabled dates string, array Sets the days of week that should be disabled. Omit dates (or provide an otherwise falsey value) to unset the disabled days.

Events

Datepicker triggers a number of events in certain circumstances. All events have extra data attached to the event object that is passed to any event handlers:

$('.datepicker').datepicker()
    .on(picker_event, function(e){
        # `e` here contains the extra attributes
    });
  • `date`: the relevant Date object, in local timezone.
  • `format([format])`: a function to make formatting `date` easier. `format` can be any format string that datepicker supports. If `format` is not given, the format set on the datepicker will be used.
Event Description
show Fired immediately when the date picker is displayed.
hide Fired immediately when the date picker is hidden.
clearDate Fired when the date is cleared, normally when the "clear" button (enabled with the `clearBtn` option) is pressed.
changeDate Fired when the date is changed.
changeYear Fired when the view year is changed from decade view.
changeMonth Fired when the view month is changed from year view.

Keyboard support

The datepicker includes some keyboard navigation:

up, down, left, right arrow keys

By themselves, left/right will move backward/forward one day, up/down will move back/forward one week.

With the shift key, up/left will move backward one month, down/right will move forward one month.

With the ctrl key, up/left will move backward one year, down/right will move forward oone year.

Shift+ctrl behaves the same as ctrl -- that is, it does not change both month and year simultaneously, only the year.

escape

The escape key can be used to hide and re-show the datepicker; this is necessary if the user wants to manually edit the value.

enter

When the picker is visible, enter will simply hide it. When the picker is not visible, enter will have normal effects -- submitting the current form, etc.

About

Allow only valid numeric values to be typed into an input, and format them

Changelog

Uses the autoNumeric plugin under the hood: http://www.decorplanit.com/plugin/

  • Exposed 3 options from autoNumeric (see usage)
  • Added ability to use data elements
  • Added optional lazy initialization

Plugin dependency

Currency Examples

Default options (no min, no max, allow decimals) with a default value (512.8) from the DOM

$
$('#currency0').numberInput();

Min: -10,000.00, max: 10,000.00, allow decimals

$
$('#currency1').numberInput({
    min: -10000,
    max: 10000,
    decimals: true
});
<form class="form-horizontal" role="form">
    <div class="form-group">
        <div class="input-group col-lg-3">
            <span class="input-group-addon">$</span>
            <input id="currency1" value="" type="text" class="form-control">
        </div>
    </div>
</form>

Min: 0, max: 10,000, don't allow decimals

$
$('#currency2').numberInput({
    min: 0,
    max: 10000,
    decimals: false
});

Percentage Examples

Default options (no min, no max, allow decimals) with a default value (42.8) from the DOM

%
<input id="percentage0" value="42.8" type="text" class="form-control">
$('#percentage0').numberInput();

Min: -10,000.00, max: 10,000.00, allow decimals

%
$('#percentage1').numberInput({
    min: -10000,
    max: 10000,
    decimals: true
});

Min: 0, max: 100, don't allow decimals

%
$('#percentage2').numberInput({
    min: 0,
    max: 100,
    decimals: false
});

Decimal Examples

Default options (no min, no max, precision of 2 decimal places) with a default value (3.14159) from the DOM

<input id="decimal0" value="3.14159" type="text" class="form-control">
$('#decimal0').numberInput();

Min: -10,000.00000, max: 10,000.00000, precision of 5 decimal places

$('#decimal1').numberInput({
    min: -10000,
    max: 10000,
    decimals: 5
});

Min: 0, max: 100, don't allow decimals

$('#decimal2').numberInput({
    min: 0,
    max: 100,
    decimals: false
});

Data Attributes

Configuration options can also be set with the use of data attributes.

$
%
    <form class="form-horizontal" role="form">
        <div class="form-group">
            <div class="input-group col-lg-3">
                <span class="input-group-addon">$</span>
                <input value="500" type="text" data-number-input-min='-100' data-number-input-max='1000' data-number-input-decimals='false' class="form-control currencyInput">
            </div>
        </div>
        <div class="form-group">
            <div class="input-group col-lg-3">
                <input type="text" value="101.001" type="text" data-number-input-min='0' data-number-input-max='10000' data-number-input-decimals='3' class="form-control currencyInput">
                <span class="input-group-addon">%</span>
            </div>
        </div>
        <div class="form-group">
            <div class="input-group col-lg-3">
                <input type="text" value="42" type="text" data-number-input-min='42' data-number-input-max='42' data-number-input-decimals='true' class="form-control currencyInput">
                <span class="input-group-addon"></span>
            </div>
        </div>
    </form>
    
    $('.currencyInput').numberInput();
    

Lazy Initialization

The number input can be initialized on focus via data attributes and without using any JavaScript, to do this include the following attribute data-require='xnumberinput'.

    <input value="1100.500" type="text" data-provide='xnumberinput' data-number-input-decimals='3' data-number-input-max='10000' class="form-control text-right">
    

Usage

Name Data Attribute Type Default Description
min data-number-input-min number Number.MIN_VALUE Minimum value allowed in the input
max data-number-input-max number Number.MAX_VALUE Maximum value allowed in the input
decimals data-number-input-decimals boolean | number 2 Number of decimal places the value must have. If passed in as boolean, true=2 and false=0

Styling

Its up to the developer to make sure the text is aligned right, do this by adding the class text-right, and to format the input value with the correct number format. Failing to do this will cause the formatting/aligning to happen on focus, which may not be desired.

About

A custom auto-complete component that can replace either a single input or a multi-select.

Changelog

Uses this Twitter typeahead.js fork: https://github.com/pmonson711/typeahead.js and select2, combines the functionality with a custom plugin.

  • typeahead.js was forked and modified to support ajax sources, message the user about empty results, ajax indicator and Bootstrap 3 styling
  • select2 css was modified to support Bootstrap 3

Plugin dependency

Examples

Simple Typeahead

This autocomplete uses a list of countries which it uses for the suggestions. Suggestions start appearing after you type 3 characters. Try typing a name of the best country, for example Russia.

Note the data-provide="autocomplete" attribute - this makes the widget initialize itself on focus without any javascript

<input type="text" name="simple-typeahead" id="simple-typeahead" value="" class="form-control"
       data-provide="autocomplete"
       data-fetch="../assets/data/countries.json" />

Simple Typeahead Ajax

This autocomplete uses a list of NHL teams, so type a name of a good team, for example "Bruins" which is the best team ever (not just this year).

Account Typeahead

This is an example of an autocomplete that uses a POST to fetch its data. Currently there is no server to provide its data, so look, but don't touch.


    <input type="text" name="account-typeahead.AccountName" id="simple-typeahead2" value="" class="form-control"
           data-provide="autocomplete"
           data-fetch="/NUTCTest/Search/FetchAccount"
           data-method="POST"
           data-fetch-as="searchString"
           data-template=" <small>()</small>"
           data-value-prop="AccountName"
           data-update-siblings-by-name="true"
           data-pluck="Accounts" />
    <hr />
    <input type="text"  class="form-control" name="account-typeahead.ENumber" value="" tabindex="-1" />
    

Multi Typeahead

This autocomplete lets you select multiple values. It uses the NHL teams data-source.

The only thing you need to do in order to make an autocomplete accept multiple values is to add the multiple attribute.

    <input type="text" name="multi-typeahead" id="multi-typeahead" value="" class="form-control"
           multiple
           data-provide="autocomplete"
           data-fetch="../assets/data/nhl.json" />

Usage

Name Type Default Description
multiple boolean false Should the input allow multiple selections
fetch string none The url to use as a data source, the string '%QUERY' may be replaced for get requests
method string GET The http method used to fetch data.
dataType string json The expected type of data returned from the server.
requestdeplay number 400 MS before requesting data after keystrokes.
updateSiblingsByName boolean false Update other inputs by matching names of model bindings.
minLength number 3 The minimum number of characters before requesting data.
itemtemplate string none An html string which formats the selection in the dropdown.
limit number 10 The number of items to show on the screen.
allowFreeEntry boolean false Allow user entry which doesn't match selection
fetchAs string q parameter to GET or POST the query as.
valueProp string Result property to use as the value from a selection
pluck string Result property to use the collection.

About

This is an unobtrusive utility that lets you treat multiple forms as if they were one single form.

You would use this when html markup prevents you from wrapping a set of controls in a single form, so you have to create multiple forms, but you want these to submit and validate as if they were a single form.

Changelog

Plugin dependency

Examples

2 Forms function as a single form

In order to use this plugin you must choose one of the forms to be the parent form. This form will be the form that is submitted when a submit is triggered on any of its child forms.

The parent form needs no special markup, but you do need to be able to write a unique selector that selects just the parent form.

All child forms need to specify a data-parent attribute, its value must be the jquery selector which selects only the parent form.

The selector used by the child forms must be exactly the same. More than one selector could select the parent form, but you must use a single selector among all the child forms.
<div class="row">
<div class="col-xs-6">
<form class="form" role="form" id="parent" action="http://httpbin.org/post" method="post">
    <div class="panel panel-default">
        <div class="form-group col-xs-12">
            <label>Date of birth:
                <input value="February 11, 1847" name="birthDate" class="form-control" type="text">
            </label>
        </div>
        <div class="form-group col-xs-12">
            <label>Date of death:
                <input value="October 18, 1931" name="deathDate" class="form-control" type="text">
            </label>
        </div>
    </div>
</form>
</div>
<!--
there can be a bunch of markup here and other forms that
prevent wrapping both of these forms in a single form element
-->
<div class="col-xs-6">
<form class="form" role="form" data-parent="#parent">
    <div class="panel panel-default">
        <div class="form-group col-xs-12">
            <label>First name:
                <input value="Thomas" name="firstName" class="form-control" type="text">
            </label>
        </div>
        <div class="form-group col-xs-12">
            <label>Last name:
                <input value="Edison" name="lastName" class="form-control" type="text">
            </label>
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
</form>
</div>
</div>

Using a hidden parent, and buttons form

If the parent form is submitted, none of the child form's controls are serialized into it. For this reason you may want to create an empty parent form and have all your visible forms use it as a parent.

This example also shows creating a form with just the submit button in it.

Sometimes you may want to have the buttons in a another form!
<div class="panel panel-default">
    <form id="hiddenParent"  action="http://httpbin.org/post" method="post"></form>
    <div class="panel-heading">Sometimes you may want to have the buttons in a another form!</div>
    <div class="panel-body">
        <form data-parent="form#hiddenParent" class="form">
            <label>Hello:
                <input type="text" value="Hello world!" name="hello"  class="form-control"/>
            </label>
            <label>My cat says:
                <input type="text" value="MEOW!" name="cat" class="form-control"/>
            </label>
        </form>
    </div>
    <div class="panel-footer">
        <form data-parent="form#hiddenParent" class="form">
            <button type="submit" class="btn btn-primary">Save</button>
        </form>
    </div>
</div>
<form data-parent="form#hiddenParent" class="form">
    <!-- this also gets submitted -->
    <label>My dog says: <input type="text" value="Eat your own dog food." name="cat" class="form-control"/></label>
</form>

Usage

Add the attribute data-parent="[selector]" to any form, where [selector] is a valid jQuery selector that selects a single form element in the document (and not the child form itself).

Ex: <form data-parent='#mainForm form.parent'>...</form>

Submitting a form with a data-parent="[selector]" attribute has the following effect:

  • All other forms with the same data-parent attribute value are found
  • Each form is checked for validity using echox-validator.js plugin, if all child forms are valid,
  • the parent form is checked for validity, and if its valid:
  • The child form's submittable controls are serialized into a name=value dict (avoid name collisions)
  • The serialized form state is written out as hidden inputs on the parent form, where the hiddens are created or updated
  • The parent form is submitted
Submitting the parent form will NOT cause the child forms to be submitted!
For this reason you may want to create an empty parent form, like in the 2nd example