Template-Lace
view release on metacpan or search on metacpan
README.mkdn view on Meta::CPAN
<view-footer copydate='$.copywrite' />
And another example:
<lace-form
method="post"
action="$.post_url">
<input type='text' name='user' />
<input type='text' name='age' />
<input type='text' name='motto' />
<input type='submit' />
</lace-form>
Canonically a component is a tag in the form of '$prefix-$name' (like <prefix-name ...>)
and typically will contain HTML attributes (like 'attr1="Literal"') and it may also
have content, as in "<lace-form><input id='content' type="submit"/></lace-form>". When you render
a template containing components, any HTML attributes will be converted to a real value
and passed to the component as initialization arguments. There are three different
ways your attributes will be processed:
- literal values
Example: <prefix-name attr='1984'/>
When a value is a simple literal that value is passed as is.
- a path from the model instance
Example: <prefix-name attr='$.foo'/>
This returns the value of "$self->foo", where $self is the model instance that the factory
created. You can follow a data path similarly to Template Toolkit, for example:
<prefix-name attr="$.foo.bar.baz" />
Would be the value of "$self->foo->bar->baz" (or possibly $self->foo->{bar}{baz} since we
follow either a method name or the key of a hash). Currently we do not follow arrayrefs.
You'll probably use this quite often to pass instance information to your component. If
the path does not exist that will return a run time error.
- a CSS match
Examples: <prefix-name title=\\'title:content' css=\\'@link' />
When the value of the attribute begins with a '\\' that means we want to get the value of
a CSS match to the current DOM. In Perl when a variable starts with a '\\' the means its
a reference; so you can think of this as a reference to a point in the current DOM.
In general the value here is just a normal CSS match specification (see [Mojo::DOM58](https://metacpan.org/pod/Mojo::DOM58) for
details on the match specifications supported). However we have added two minor bits to
how [Mojo::DOM58](https://metacpan.org/pod/Mojo::DOM58) works to make some types matching easier. First, if a match specification
ends in ':content' that means 'match the content, not the full node'. In the example case
"title=\\'title:content'" that would get the text value of the title tag. Second, in the case
where you want the match to return a collection of nodes all matching the specification, you
would prepend a '@' to the front of it (think in Perl @variable means an array variable). In
the given example "css=\\'@link'" we want the attribute 'css' to be a collection of all the
linked stylesheets in the current DOM.
You will use this type of value when you are making components that do complex layout
and overlays of the current DOM (such as when you are creating a master layout page for
your website).
In addition to any attributes you pass to a component via a declaration as described above
all components could get some of the following automatic atttributes:
- content
If the component has content as in the following example
<prefix-name
attr1=>'literal value'
attr2=>'$.foo'
attr3=>\'title:content'>
[some addtional content such as HTML markup and text]
</prefix-name>
That content will be sent to the component under the 'content' attribute
- container
If the component is a subcomponent it will receive the instance model of its
parent as the 'container' attribute.
- model
All components get the 'model' attribute, which is the model instance that contains
the template in which they appear. I would use this carefully since I think that
you would prefer to pass information from the model to the component via attributes.
Components can be an instance of any class that does `create` and `process_dom`
but generally you will make your components out of other [Template::Lace](https://metacpan.org/pod/Template::Lace) models
since that provides the most features and template reusability. Components are added to
the Factory at the time you construct it:
my $factory = Template::Lace::Factory->new(
model_class=>'Local::Template::List',
component_handlers=>+{
layout => sub {
my ($name, $args, %attrs) = @_;
$name = ucfirst $name;
return Template::Lace::Factory->new(model_class=>"Local::Template::$name");
},
lace => {
form => Template::Lace::Factory->new(model_class=>'Local::Template::Form'),
input => Template::Lace::Factory->new(model_class=>'Local::Template::Input'),
},
},
);
Components are added as a hashref of data associated with the 'component\_handlers'
initialization argument for the class [Template::Lace::Factory](https://metacpan.org/pod/Template::Lace::Factory). You can either
attach a component to a full 'prefix-name' pair, as in the examples for 'form' and
'input', or you can create a component 'generator' for an entire prefix by associating
the prefix with a coderef which is responsible for returning a component based on the
actual name.
If your components are trivial and/or you don't want to make a full model and Factory for
one, you can use the [Template::Lace::Utils](https://metacpan.org/pod/Template::Lace::Utils) subroutine `mk_component` to assist. This
creates an instance of [Template::Lace::ComponentCallback](https://metacpan.org/pod/Template::Lace::ComponentCallback) which is a very simple component
defined by a code reference. These type of components are easy to make and can run faster
README.mkdn view on Meta::CPAN
of Perl is available!
Finally, lets look at the 'view-master' component, which is a type of layout component
to add common header / footer information to your page (a standard enough thing to do
when building a website:
package MyApp::View::Master;
use Moo;
with 'Template::Lace::ModelRole';
has title => (is=>'ro', required=>1);
has css => (is=>'ro', required=>1);
has meta => (is=>'ro', required=>1);
has body => (is=>'ro', required=>1);
sub on_component_add {
my ($self, $dom) = @_;
$dom->title($self->title)
->head(sub { $_->append_content($self->css->join) })
->head(sub { $_->prepend_content($self->meta->join) })
->body(sub { $_->at('h1')->append($self->body) })
->at('#header')
->content($self->title);
}
sub template {
my $class = shift;
return q[
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<title>Page Title</title>
<link href="/static/base.css" rel="stylesheet" />
<link href="/static/index.css" rel="stylesheet"/ >
</head>
<body id="body">
<h1 id="header">Intro</h1>
</body>
</html>
];
}
1;
This is a type of component intended to perform layout work for you. In this case we are creating a common
header and footer and some internal market. The values for it attributes come not from the model class
but from the contained DOM itself. Lets look again at the top of the component declaration:
<view-master title=\'title:content'
css=\'@link'
meta=\'@meta'
body=\'body:content'>
So four attributes, all coming from the DOM associated with the 'content' area of this component. We
grab the content of the title tag and the content of the HTML body tag, as well as the collection (if
any) of the link takes (for css style sheets) and any template specific meta tags.
If you are looking carefully you have noticed instead of a 'process\_dom' method we have a 'on\_component\_add' method. We could do this with 'process\_dom' but that method runs for every request and since this overlay contains no dynamic request bo...
Here's a sample of the actual result, rendering all the components (you can peek at the repository which has all the code for these examples to see how it all works)
<html>
<head>
<meta startup="Fri Mar 31 08:43:24 2017">
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<title>
Things To Do
</title>
<link href="/static/base.css" rel="stylesheet" type="text/css">
<link href="/css/input.min.css" rel="stylesheet" type="text/css">
<script src="/js/input.min.js" type="text/javascript"></script>
</head>
<body id="body">
<h1>
Things To Do
</h1>
<form id="newtodo">
<div class="field">
<label for="item">Todo</label>
<input id="item" name="item" type="text" value="milk">
</div>
<div class="ui error message">
<ol class="errors">
<li>too short
</li>
<li>too similar it existing item
</li>
</ol>
</div>
</form>
<ol id="todos">
<li>Buy Milk
</li>
<li>Walk Dog
</li>
</ol>
<section id="footer">
<hr>
<p id="copy">
copyright 2017
</p>
</section>
</body>
</html>
So even though we have a page with a lot happening, we can write a model class that focuses
just on the primary task (display the list of Todos) and let components handle the other work.
A complex template can be logically divided into clear chunks, each dedicated to one function
and each with a clearly defined, strongly typed interface. I believe this leads to well
organized and concise templates that are maintainable over the long term.
You can review the documentation for each of the main classes in this distribution, and/or
review the test cases for more examples. Or if you want to use this for building web sites
immediately, see [Catalyst::View::Template::Lace](https://metacpan.org/pod/Catalyst::View::Template::Lace) as you quickest path.
# IMPORTANT NOTE REGARDING VALID HTML
( run in 0.457 second using v1.01-cache-2.11-cpan-ceb78f64989 )