NAME
Catalyst::View::Valiant::HTMLBuilder - Per Request, strongly typed Views in code
SYNOPSIS
package Example::View::HTML::Home;
use Moose;
use Catalyst::View::Valiant::HTMLBuilder
qw(form_for link_to uri);
has 'page_title' => (is=>'ro', required=>1);
sub the_time ($self) { P {class=>'timestamp'}, scalar localtime}
sub render($self, $c, $content) {
return Html +{ lang=>'en' }, [
Head [
Title $self->page_title,
Meta +{ charset=>"utf-8" },
Meta +{ name=>"viewport", content=>"width=device-width, initial-scale=1, shrink-to-fit=no" },
Link +{ rel=>"stylesheet", type=>'text/css', href=>"/static/core.css" },
Link +{ rel=>"stylesheet",
href=>"https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css",
integrity=>'sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N',
crossorigin=>"anonymous" },
($self->caller->can('css') ? $self->caller->css : ''),
],
Body [
$content,
Div $self->the_time,
Script +{ src=>'https://code.jquery.com/jquery-3.7.0.min.js',
integrity=>'sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=',
crossorigin=>"anonymous" }, '',
Script +{ src=>'https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js',
integrity=>'sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct',
crossorigin=>"anonymous" }, '',
],
];
}
1;
DESCRIPTION
WARNINGS: Experimental code that I might need to break back compatibility in order to fix issues. I've rewritten this from scratch twice already so if I do so again and it breaks all your code, you've been warned.
This is a Catalyst::View subclass that provides a way to write views in code that are strongly typed and per request. It also integrates with several of Valiant's HTML form generation code modules to make it easier to create HTML forms that properly synchronize with your Valiant models for displaying errors and performing validation.
Unlike most Catalyst views, this view is 'per request' in that it is instantiated for each request. This allows you to store per request state in the view object as well as localize view specific logic to the view object. In particular it allows you to avoid or reduce using the stash in order to pass values from the controller to the view. I think this can make your views more robust and easier to support for the long term. It builds upons Catalyst::View::BasePerRequest which provides the per request behavior so you should take a look at the documentation and example controller integration in that module in order to get the idea.
As a quick example here's a possible controller that might invoke the view given in the SYNOPSIS:
package Example::Controller::Home;
use Moose;
use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub index($self, $c) {
my $view = $c->view('HTML::Home', page_title=>'The Home Page');
}
1;
This will then work with the commonly used Catalyst::Action::RenderView or my Catalyst::ActionRole::RenderView to produce a view response and set it as the response body.
This approach is experimental and I expect best practices around it to evolve so don't get stuck on any particular way of doing things.
ATTRIBUTES
This class inherits all of the attributes from Catalyst::View::BasePerRequest
METHODS
This class inherits all of the methods from Catalyst::View::BasePerRequest as well as:
tb
Instance of Valiant::HTML::Util::TagBuilder that is used to generate HTML tags.
Returns the current form
object.
view
Given the name of a view, returns the view object. Basically a wrapper around $c->view
.
data_template
Returns the data template for the current view. This is a template that is used to generate data attributes for the view. The template is read from a file named DATA
in the view class. The template is processed by the sf
method in Valiant::HTML::TagBuilder so you can use the same syntax as you would in a view. Example
package Example::View::HTML::Home;
use Moose;
use Catalyst::View::Valiant::HTMLBuilder;
has bar => (is=>'ro', required=>1);
sub render($self, $c, $content) {
return Div +{ $self->data_template }, $content;
}
1;
__DATA__
data-foo="{:bar}"
safe
Marks a string as safe to render by first escaping it and then wrapping it in a Valiant::HTML::SafeString object.
raw
Marks a string as safe to render by wrapping it in a Valiant::HTML::SafeString object.
safe_concat
Given one or more strings and / or Valiant::HTML::SafeString objects, returns a new Valiant::HTML::SafeString object that is the concatenation of all of the strings.
escape_html
Given a string, returns a new string that is the escaped version of the original string.
escape_javascript
Given a string, returns a new string that is the javascript escaped version of the original string.
uri_escape
Given a string, returns a new string that is the URI escaped version of the original string. Given an array, returns a string that is the URI escaped version of the key value pairs in the array.
read_attribute_for_html
Given an attribute name, returns the value of that attribute if it exists. If the attribute does not exist, it will die.
attribute_exists_for_html
Given an attribute name, returns true if the attribute exists and false if it does notu.
formbuilder_class
sub formbuilder_class { 'Example::FormBuilder' }
Provides an easy way to override the default formbuilder class. By default it will use Valiant::HTML::FormBuilder. You can override this method to return a different class via a subclass of this view.
EXPORTS
All HTML tags are exported as functions. For example:
package Example::View::HTML::Home;
use Moose;
use Catalyst::View::Valiant::HTMLBuilder;
sub render($self, $c, $content) {
return Div +{ class=>'foo' }, $content;
}
The export is the same as the HTML tag name but with the first letter capitalized.
In addition you can request to export any method from the form object, the pager object, the controller object or the context object. For example:
package Example::View::HTML::Home;
use Moose;
use Catalyst::View::Valiant::HTMLBuilder
qw(form_for link_to uri);
sub render($self, $c, $content) {
return Div +{ class=>'foo' }, $content;
}
This will export the form_for
, link_to
and uri
methods from the form object, the pager object, the controller object and the context object respectively.
See Valiant::HTML::Util::Form and Valiant::HTML::Util::FormTags for more information on the form methods.
See Valiant::HTML::Util::Tagbuilder for more information on the tag methods.
See Valiant::HTML::SafeString for more information on the safe string methods.
See Valiant::JSON::Util for more information on the JSON methods.
SUBCLASSING
Create a base class in your project:
package Example::View::HTML;
use Moose;
use Catalyst::View::Valiant::HTMLBuilder;
sub redirect_to_action ($self, $action, @args) {
return $self->redirect_to($self->uri_for_action($action, @args));
}
sub the_time ($self) {
return P {class=>'timestamp'}, scalar localtime;
}
__PACKAGE__->meta->make_immutable;
Then you can use it as in this example. The subclass will automatically inherit any local methods and attributes from the base class.
package Example::View::HTML::Page;
use Moose;
use Example::View::HTML;
has 'page_title' => (is=>'ro', required=>1);
sub render($self, $c, $content) {
return Html +{ lang=>'en' }, [
Head [
Title $self->page_title,
Meta +{ charset=>"utf-8" },
Meta +{ name=>"viewport", content=>"width=device-width, initial-scale=1, shrink-to-fit=no" },
Link +{ rel=>"stylesheet", type=>'text/css', href=>"/static/core.css" },
Link +{ rel=>"stylesheet",
href=>"https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css",
integrity=>'sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N',
crossorigin=>"anonymous" },
],
Body [
$content,
Div $self->the_time,
],
];
}
__PACKAGE__->meta->make_immutable;
SEE ALSO
Valiant, Valiant::HTML::Util::Form, Valiant::HTML::Util::FormTags, Valiant::HTML::Util::Tagbuilder, Valiant::HTML::SafeString.
AUTHOR
See Valiant
COPYRIGHT & LICENSE
See Valiant