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