###*
Forms
=====
Unpoly comes with functionality to [submit](/form-up-target) and [validate](/up-validate)
forms without leaving the current page. This means you can replace page fragments,
open dialogs with sub-forms, etc. all without losing form state.
@class up.form
###
up.form = (($) ->
u = up.util
###*
Sets default options for form submission and validation.
@property up.form.config
@param {Number} [config.observeDelay=0]
The number of miliseconds to wait before [`up.observe`](/up.observe) runs the callback
after the input value changes. Use this to limit how often the callback
will be invoked for a fast typist.
@param {Array} [config.validateTargets=['[up-fieldset]:has(&)', 'fieldset:has(&)', 'label:has(&)', 'form:has(&)']]
An array of CSS selectors that are searched around a form field
that wants to [validate](/up.validate). The first matching selector
will be updated with the validation messages from the server.
By default this looks for a `
`, `` or `
The server response is searched for the selector given in `up-target`.
The selector content is then [replaced](/up.replace) in the current page.
The programmatic variant of this is the [`up.submit`](/up.submit) function.
\#\#\# Failed submission
When the server was unable to save the form due to invalid data,
it will usually re-render an updated copy of the form with
validation messages.
For Unpoly to be able to detect a failed form submission,,
the form must be re-rendered with a non-200 HTTP status code.
We recommend to use either 400 (bad request) or
422 (unprocessable entity).
In Ruby on Rails, you can pass a
[`:status` option to `render`](http://guides.rubyonrails.org/layouts_and_rendering.html#the-status-option)
for this:
class UsersController < ApplicationController
def create
user_params = params[:user].permit(:email, :password)
@user = User.new(user_params)
if @user.save?
sign_in @user
else
render 'form', status: :bad_request
end
end
end
Note that you can also use the
[`up-validate`](/up-validate) attribute to perform server-side
validations while the user is completing fields.
\#\#\# Redirects
Unpoly requires two additional response headers to detect redirects,
which are otherwise undetectable for an AJAX client.
When the form's action performs a redirect, the server should echo
the new request's URL as a response header `X-Up-Location`
and the request's HTTP method as `X-Up-Method: GET`.
If you are using Unpoly via the `unpoly-rails` gem, these headers
are set automatically for every request.
\#\#\# Giving feedback while the form is processing
The `
When the user changes the `email` field, we want to validate that
the e-mail address is valid and still available. Also we want to
change the `password` field for the minimum required password length.
We can do this by giving both fields an `up-validate` attribute:
Whenever a field with `up-validate` changes, the form is POSTed to
`/users` with an additional `X-Up-Validate` HTTP header.
Upon seeing this header, the server is expected to validate (but not save)
the form submission and render a new copy of the form with validation errors.
In Ruby on Rails the processing action should behave like this:
class UsersController < ApplicationController
# This action handles POST /users
def create
user_params = params[:user].permit(:email, :password)
@user = User.new(user_params)
if request.headers['X-Up-Validate']
@user.valid? # run validations, but don't save to the database
render 'form' # render form with error messages
elsif @user.save?
sign_in @user
else
render 'form', status: :bad_request
end
end
end
Note that if you're using the `unpoly-rails` gem you can simply say `up.validate?`
instead of manually checking for `request.headers['X-Up-Validate']`.
The server now renders an updated copy of the form with eventual validation errors:
The `` around the e-mail field is now updated to have the `has-error`
class and display the validation message.
\#\#\# How validation results are displayed
Although the server will usually respond to a validation with a complete,
fresh copy of the form, Unpoly will by default not update the entire form.
This is done in order to preserve volatile state such as the scroll position
of `