Validating server side forms
This guide covers the usage of validator to validate the forms rendered on the server using Edge templates. We will use using session flash messages to access the validator errors.
Creating the form
The final example is hosted on codesandbox. Click here to preview the outcome or edit the project directly on codesandbox.
AdonisJS does not interfere with your HTML and you define the forms using the standard HTML syntax. In other words, AdonisJS doesn't have any form builders doing magic behind the scenes, and hence you have the complete freedom to structure the HTML the way you want.
The following is the HTML form to create a new blog post by accepting the post title and body.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Create a new blog post</title>
</head>
<body>
<form action="/posts" method="POST">
<div>
<p>
<label for="title"> Post title </label>
</p>
<input type="text" name="title" />
</div>
<div>
<p>
<label for="body"> Post body </label>
</p>
<textarea name="body" cols="30" rows="10"></textarea>
</div>
<div>
<button type="submit">Create Post</button>
</div>
</form>
</body>
</html>
As you can notice, the entire document is vanilla HTML with no special syntax inside it. As a small improvement, you can replace the hard-coded form action /posts
with the route helper
method.
Assuming the following route declarations.
import Route from '@ioc:Adonis/Core/Route'
Route.get('posts/create', 'PostsController.create')
Route.post('posts', 'PostsController.store')
You can get the route URL for storing a new post using its controller.action
name.
<form action="/posts" method="POST">
<form action="{{ route('PostsController.store') }}" method="POST">
<!-- Rest of the form -->
</form>
Validating the form
Let's continue with the same form we created above and implement the PostsController.store
method to validate the incoming request.
import { schema, rules } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export class PostsController {
public async store({ request }: HttpContextContract) {
const postSchema = schema.create({
title: schema.string({ trim: true }, [
rules.minLength(10)
]),
body: schema.string(),
})
const payload = await request.validate({ schema: postSchema })
console.log(payload)
return 'Post created'
}
}
schema.create
The schema.create
method creates a new schema definition. The schema defines the shape of the data you expect.
schema.string
The schema.string
method enforces the value to be a valid string. There are other similar methods to enforce different data types like a boolean, a number and so on.
Optionally, you can define additional validations on a property using the rules
object.
request.validate
The request.validate
method accepts the pre-defined schema and validates the request body against it.
If the validation fails, the validator will redirect the client back to the form along with the error messages and the form values.
If the validation succeeds, you can access the validated properties as the method return value.
Displaying validation errors
The validation errors are shared with the form using session flash messages
. Inside your edge templates, you can access them using the flashMessages
global property.
Errors structure inside flash messages
{
errors: {
title: ['required validation failed'],
body: ['required validation failed'],
}
}
Access error messages
{{ flashMessages.get('errors.title') }}
{{ flashMessages.get('errors.body') }}
Retaining input values
Post redirect, the browser re-renders the page and the form values are reset to their initial state. However, you can access the submitted values using flash messages.
You can access the form input values using the field name. For example:
<div>
<p>
<label for="title"> Post title </label>
</p>
<input
type="text"
name="title"
value="{{ flashMessages.get('title', '') }}"
/>
</div>
You can also nested values using the field name
<input
type="text"
name="user[email]"
value="{{ flashMessages.get('user[email]', '') }}"
/>
Displaying success message
The usage of flash messages is not only limited to the validation errors. You can also use them to display the success message post form submission. For example:
import { schema, rules } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class PostsController {
// Create method
public async store({ request }: HttpContextContract) {
public async store({ request, session, response }: HttpContextContract) {
// ... Collapsed existing code
console.log(payload)
return 'Post created'
session.flash('success', 'Post created successfully')
response.redirect().back()
}
}
Access the success message inside edge template
@if(flashMessages.has('success'))
<p>{{ flashMessages.get('success') }}</p>
@endif
Form method spoofing
Standard HTML forms cannot make use of all the HTTP verbs beyond GET
and POST
. It means you cannot create a form with the method PUT
.
However, AdonisJS allows you to spoof the HTTP method using the _method
query string. In the following example, the request will be routed to the route listening for the PUT
request.
<form method="POST" action="/posts/1?_method=PUT"></form>
Form method spoofing only works:
- When the value of
http.allowMethodSpoofing
is set to true inside theconfig/app.ts
file. - And the original request method is
POST
.