Stateful Forms
Small library to deal with forms. Inspired by AngularJS + React. 0 dependencies.
Name Please fill in a name <!-- Support for server side responses --> Oops, something went wrong on with the request. Try again later. Submit Form successfully submitted
Features
- 0 dependencies
- Tiny footprint ~6kb minified, ±10 times smaller than AngularJS
- Good documentation
- Works on IE9+
- Excellent performance
- Forms will be serialized and send with AJAX
- Uses HTML5 validation attributes
- No JavaScript configuration required
- Powerful expressions based on AngularJS expressions
When to use Stateful Forms?
- Contact forms
- Landing page signup forms
- Order forms
- Questionnaires
- When you deal with forms in general
Quick Start
- Install
stateful-forms
- Add the script to your page
- Call
window.createStatefulForms()
when the DOM is loaded - Add the
stateful
attribute to your forms - Scroll down for examples
Installation
Not yet written...
How does it work?
Stateful Forms creates a state object based on your form and form elements. That state object could look something like this before you touched the form:
form: submitted: false valid: false invalid: true pristine: true dirty: false touched: false untouched: true email: valid: false invalid: true pristine: true dirty: false touched: false untouched: true name: // ... same as email password: // ... same as email
It then exposes that state object to elements inside of your form we call directives
.
sf-show
in this example is such a directive:
Please fill in a name.
Inside of your directive
you put expressions
.
In the previous example that was: form.submitted && name.invalid
.
Whenever you change something in the form a state update is triggered. It checks if there are any directives listening to this state. If there are, it updates those directives with that state.
What happens on a form submit
When a form is submitted the form sets form.submitted
to true.
If the form is valid, it is it serializes the form and makes
an AJAX request to the endpoint in your action
attribute.
In the mean while it sets request.pending
to true.
So you can go all wild on your loading indicators.
When the request is successful, request.success
is set to true.
Also a response
object is added to the state.
This response
object contains whatever you send back from the server on two properties.
response.text
contains the raw response text.response.json
contains the response text parsed byJSON.parse
.
See documentation and examples below.
Directives: attributes you can put on elements
stateful
, put on aform
to create a stateful form. Supports multiple forms.sf-show="<Expression>"
, toggles the element if the expression is Truthy.sf-text="<Expression>"
, sets the text of the element.sf-class="<ObjectExpression>"
, sets classnames based on expressions.sf-attribute="<ObjectExpression>"
, sets attributes based on expressions.
sf-show
directive
sf-show="<Expression>"
, toggles the element if the expression is Truthy.
Show when name is invalidShow when inquiry is questionShow when inquiry is not question
Note that the Truthy value of the Expression
is used.
sf-text
directive
sf-text="<Expression>"
, sets the text of the element.
Will show the value of the expression hereThis text will be replaced with email.value
sf-class
directive
sf-class="<ObjectExpression>"
, sets classnames based on expressions.
Gets has-error class when email is invalid Gets has-error or is-valid class based on
sf-attributes
directive
sf-attribute="<ObjectExpression>"
, sets attributes based on expressions.
Create account
Expressions
Expressions in Stateful Forms are a slimmed down version of AngularJS expressions. The actual code is a remix of angular/expressionist.js, slimmed down and converted to es5. Don’t worry if you don’t know what that means.
Additionally Stateful Forms uses something called ObjectExpression
.
Valid expressions
Not all of these are directly applicable in real world usage, but it is nice to know there is power when you need it.
!false || true
(true && false) || true
!(11 == 10)
!true
2<3
2>3
2<=2
2>=2
2==3
2!=3
true&&true
true||false
7==3+4?10:20
true&&false?10:20
'str ' + 4
a.b
a.b.c.d
taxRate / 100 * subTotal
Please see test/parser.js
for more examples.
ObjectExpression
An ObjectExpression
is an object holding expressions:
key1: <Expression>; key2: <Expression>
They are used in sf-class
and sf-attributes
.
It uses the ;
character to split expressions.
And the first :
character to split the key from the expression.
The example would return:
has-error: 'name.invalid' is-valid: 'name.valid'
Which is in turn parsed by the sf-class
or sf-attributes
directive
to become usable.
In AngularJS but not in Stateful Forms
These would’t make a lot of sense in Stateful Forms either.
- Variable assignment
someVar = 'hello'
not supported - Function calls
someFunc()
not supported - Array access
a[0]
not supported - Object literals
{ a: 'prop' }
not supported - Object key access
obj['someKey']
not supported - ... more obscure expressions omitted
Form elements
The following list shows all the supported form elements. You can ignore the right hand side of the arrow.
-
input[type="text"]
=>StatefulTextInput
-
input[type="hidden"]
=>StatefulTextInput
-
input[type="phone|tel"]
=>StatefulTextInput
, doesn’t validate -
input[type="email"]
=>StatefulTextInput
, validates email -
textarea
=>StatefulTextInput
-
input[type="checkbox"]
=>StatefulCheckbox
-
input[type="radio"]
=>StatefulRadio
grouped byStatefulRadioGroup
-
select
=>StatefulSelect
-
No
type="file"
support -
No
type="date"
support`
input[type="phone"]
validate?
Why doesn’t Since there are so many different types of phone numbers. It’s impossible to have a one size fits all.
The best way to include phone validation is through a regular expression
on the pattern
attribute.
The following StackOverflow post might give you more insight: A comprehensive regex for phone number validation
type="date"
or datepicker support?
Why no The date type is horribly supported among browser with inconsistent UI and API’s. If you want to use a custom datepicker you are better of reflecting the value to a hidden input field.
Proxy a form element into an other type
Advanced usage, you better now what you are doing.
You can potentially use the sf-element
attribute to set what type
a form element should use. This might be useful for unsupported
form elements. Usage: sf-element="text"
for example.
Form elements states
Form element states are the same as in AngularJS.
form.pristine = has the form element been interacted with
form.dirty = !pristine
form.valid = is the form element valid
form.invalid = !valid
form.touched = did the form element blur (or interacted with for some elements)
form.untouched = !touched
Form element state is reflected as classnames
Form elements reflect their state as classnames (just like AngularJS).
The state is prefixed with is-{state}
.
<input type="text" class="is-valid is-untouched is-pristine">
Validation
Stateful Form implements pretty much everything you can in HTML5.
required
min
max
maxlength
minlength
pattern
Note that required
is needed for a form element to not be valid
by default.
For now there is no async
validator support out of the box.
This is often needed for checking if usernames are taken.
The form itself
Stateful Forms only fully work when you:
- Add the
stateful
attribute to aform
element. - Add the
novalidate
attribute to aform
element
Only inside of that form do directives work.
$form
object in the state object
The $form
is always available and refers to the form itself.
It has the same properties as a form element with a couple extra properties.
$form.submitted = has the submit button been clicked
$form.pristine = have any of the form elements inside been interacted with
$form.dirty = !pristine
$form.valid = are all form elements valid
$form.invalid = !valid
$form.touched = did any form element blur
$form.untouched = !touched
$request
object in the state object
The The $request
object is always available
// state object $request: pending: false success: true failed: false status: requeststatus // ... state omitted
$response
object in the state object
The The $response
object is only available when the form is submitted.
// state object $response: json: JSON text: requestresponseText // ... state omitted
You can use the response
object to show messages from the server.
If you decide to implement something like that.
Examples
Check out the examples
directory for more elaborate use cases.
If you clone this repo you can run them by running npm run dev
.
working example:
<!-- working example --> <!-- Note the value of the sf-show attribute --> Please enter a correct name Submit
Show validation message when form submitted and field is valid
- Useful for validation messages
Please enter a correct name Submit <!-- script omitted -->
Additionally it is a good practice to show the required *
asterisk only
when the form is submitted:
Email * Please enter a valid email <!-- script omitted -->
Give an element a class based on validation
- Useful to give a class to a surrounding element
Please enter a correct name Submit <!-- script omitted -->
Disable submit button based on checkbox
- Useful for terms and conditions checkbox
I agree to the terms and conditions Create account <!-- script omitted -->
Show value of form element when valid
- Useful to show a confirmation message with the users email
<!-- form and script omitted --> Email * Please enter a valid email We will send a confirmation email to .
select
element
Show messages based on selected with a <!-- form and script omitted --> Inquiry Question Feedback Billing Other Ask us any question. We are always open for feedback. Let’s figure out this billing inquiry together. Tell us what’s on your mind.
Acknowledgements
This project could not be made if it is wasn’t for the excellent work of the AngularJS team. Especially on the angular/expressionist.js project which I happen to stumble upon.
TODO
- Build support for
input[type="radio"]
- Reflect classnames to form elements
- Test out successful responses
- Build support for
input[type="hidden"]
- Figure out what to do with
input[type="date"]
- Rename form, response and request to $form, $response, $request
- Create a solid build step with minification
- Publish to
npm
- Publish to
bower
- Publish to
cdnjs
- Write installation guide
- Put in ESLint
Author & License
Created by Jeroen Ransijn under the MIT license.