Mojolicious::Lite with embedded templates
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 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.
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:
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.
The template itself is added to the end of the code, after the previous template:
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.
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:
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.
The templating system that comes with Mojolicious is very powerful and allows us to embed Perl code
using % to mark lines with Perl code:
You can even indent the HTML between the Perl snippets as in HTML white-space has no impact.
Now it would be a good time to do a little exercise. The current version of our code
looks like this:
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.
__DATA__
@@ echo.html.ep
What are you looking for?
<form method="POST"><input name="q"><input type="submit" value="Echo"></form>
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
post '/echo' => sub {
my $self = shift;
$self->render( 'response', msg => $self->param('q') );
};
@@ response.html.ep
You typed: <%= $msg %>
Connect the POST method to the same template
__DATA__
@@ echo.html.ep
What are you looking for?
<form method="POST"><input name="q"><input type="submit" value="Echo"></form>
You typed: <%= $msg %>
Conditionals in the template
% if (defined $msg) {
You typed: <%= $msg %>
% }
% if (defined $msg) {
You typed: <%= $msg %>
% }
Exercise
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 %>
% }
Published on 2013-08-28