In the first few examples of Mojolicious::Lite we had our very simple HTML embedded in the Perl code. This was good just to get our feet wet, but soon we'd like to have some separation between Perl and HTML. Let's see how can we do this using Mojolicious::Lite.
We finished the echo example with the following script:
use Mojolicious::Lite;
get '/' => { text => 'Hello World' };
get '/echo' => { text => q{
<form method="POST"><input name="q"><input type="submit" value="Echo"></form>
}};
post '/echo' => sub {
my $self = shift;
$self->render( text => $self->param('q') );
};
app->secret('My extra secret passphrase.');
app->start;
Let's save it in a file called hello_world.pl
and run it
as morbo hello_world.pl
Then visit the http://localhost:3000/echo to see it is working before we make changes.

The echo template
We replace the get '/echo'
route with the following code:
get '/echo' => sub {
my $self = shift;
$self->render('echo');
};
It is a bit similar now to the post
method of the /echo
route
in that here too we use an anonymous subroutine, but the call to the render
method is a bit different. Instead of passing the text
key with the actual
HTML we want to send back, we pass in the name of our template.
The template itself can be added to the end of the file, after the __DATA__
tag.
This __DATA__
tag is a general Perl syntax that allows us to include pieces
of data within our script and then access it using the <DATA>
file-handle.
Mojolicious::Lite uses this feature of Perl to allow us to include the templates
in the script but separate from our actual Perl code.
The @@
syntax is used by the templating system of Mojolicious as a way to mark
where each template starts. echo.html.ep
is the mark of the template that we can
render as the echo template.
__DATA__
@@ echo.html.ep
What are you looking for?
<form method="POST"><input name="q"><input type="submit" value="Echo"></form>
The actual HTML code was changed so when we reload the page we can see that it has indeed changed and it already comes from the template:

The code should look like this now:
use Mojolicious::Lite;
get '/' => { text => 'Hello World' };
get '/echo' => sub {
my $self = shift;
$self->render('echo');
};
post '/echo' => sub {
my $self = shift;
$self->render( text => $self->param('q') );
};
app->secret('My extra secret passphrase.');
app->start;
__DATA__
@@ echo.html.ep
What are you looking for?
<form method="POST"><input name="q"><input type="submit" value="Echo"></form>
Server the response from a template too
This time we change the code of the POST
method handling the /echo
route. The render
method receives the name of the template as the first
parameter and then we can pass key-value pairs. They values will be accessible in
the template using the keys as scalar variables.
post '/echo' => sub {
my $self = shift;
$self->render( 'response', msg => $self->param('q') );
};
The template itself is added to the end of the code, after the previous template:
@@ response.html.ep
You typed: <%= $msg %>
Mojolicious::Lite allow us to the keys we passed as if they were scalar variable
and the following syntax embeds the value in our template: <%= $msg %>
We can now type something in the form, click on Echo and see the new response built from the template.
Connect the POST method to the same template
There are cases when the request page and the response page are really very different, and we need two separate templates, but in other cases we might want them to be served from the same template. For example, we might want the form to stay on the responses page. So we merge the two templates together to get:
__DATA__
@@ echo.html.ep
What are you looking for?
<form method="POST"><input name="q"><input type="submit" value="Echo"></form>
You typed: <%= $msg %>
Of course we also change the POST
handler to pass the template name
'echo' instead of 'response'.
As morbo has already restarted our server, we can just reload the page now. Because this is a POST-request, the browser will ask us if we want to re-submit the form. In Google Chrome it will look like this:

Once we click the Continue button we will see the new page that includes both the form and the text we typed earlier:

So let's try to reload the original page now by clicking on the link to http://localhost:3000/echo.
We will get a huge error message:

This is the well known Perl error Global symbol requires explicit package name, but displayed in a nice way.
The problem is that in our shared template we use the $msg
variable, but we pass it only in the POST-handler
as only there has it any value.
I am not sure if this is the best solution, but we can change the call to render
in the GET-handler
and replace $self->render('echo');
by $self->render('echo', msg => undef);
.
That way we'll have the $msg
variable, but it will be undef.
The resulting page is still not perfect, but at least it is not crashing:

The problem is that we still display the text You typed: even though we have not typed anything.
Conditionals in the template
The templating system that comes with Mojolicious is very powerful and allows us to embed Perl code
using %
to mark lines with Perl code:
% if (defined $msg) {
You typed: <%= $msg %>
% }
You can even indent the HTML between the Perl snippets as in HTML white-space has no impact.
% if (defined $msg) {
You typed: <%= $msg %>
% }
Exercise
Now it would be a good time to do a little exercise. The current version of our code looks like this:
use Mojolicious::Lite;
get '/' => { text => 'Hello World' };
get '/echo' => sub {
my $self = shift;
$self->render('echo', msg => undef);
};
post '/echo' => sub {
my $self = shift;
$self->render( 'echo', msg => $self->param('q') );
};
app->secret('My extra secret passphrase.');
app->start;
__DATA__
@@ echo.html.ep
What are you looking for?
<form method="POST"><input name="q"><input type="submit" value="Echo"></form>
% if (defined $msg) {
You typed: <%= $msg %>
% }
Two of the 3 routes are already using templates. Please, change the script so
the /
route will be served using a template called 'index'.
If you'd like to show your solution, please fork this GitHub repository make the changes and send a pull request. I won't merge it but we all can comment on it.