Day 19 - HTML::Widget tricks
Custom markup with HTML::Widget
Introduction
Today we'll show you how to generate custom markup using a custom container, and how to add custom elements to your forms.
Let's add a simple feedback form to our Cheer project. First thing, add the Catalyst::Plugin::HTML::Widget (with documentation in HTML::Widget) in the plugin list, in Cheer.pm. Then open Root.pm and type your form:
sub feedback : Local {
my ($self, $c) = @_;
my $w = $c->widget('feedback')->method('post')->action($c->req->uri);
$w->legend('Tell us what you think!');
$w->element('Textfield', 'name')->label('Your name:');
$w->element('Textfield', 'email')->label('Your email:');
$w->element('Textarea', 'message')->cols(40)->rows(5)->label('Your feedback');
$w->element('Hidden','feedback_submitted')->value('1');
$w->element('Submit', 'send')->value('Send it');
$w->constraint('Email','email')->message($c->localize('Invalid email address'));
$w->constraint('All','message')->message('You have to write something');
my $result;
if ( $c->req->params->{feedback_submitted} ) {
$result = $w->process ( $c->request );
unless ($result->have_errors) {
$c->log->debug("Something should happen here..");
}
} else {
$result = $w->process;
}
$c->stash->{feedback_form} = $result->as_xml;
$c->stash->{template} = 'feedback.tt';
}
Don't like the markup? Build a custom container
The generated markup is as follows (whitespace added for easy reading):
<form action="http://localhost:3000/feedback" id="feedback" method="post"> <fieldset class="widget_fieldset"> <legend id="feedback_legend">Tell us what you think!</legend> <label for="feedback_name" id="feedback_name_label">Your name: <input class="textfield" id="feedback_name" name="name" type="text" /> </label> <label for="feedback_email" id="feedback_email_label">Your email: <input class="textfield" id="feedback_email" name="email" type="text" /> </label> <label for="feedback_message" id="feedback_message_label">Your feedback <textarea class="textarea" cols="40" id="feedback_message" name="message" rows="5"> </textarea> </label> <input class="hidden" id="feedback_feedback_submitted" name="feedback_submitted" type="hidden" value="1" /> <input class="submit" id="feedback_send" name="send" type="submit" value="Send it" /> </fieldset> </form>
This may not be exactly what you seek , and if you don't want to build the form manually in the template, you'll have to build a custom container.
Let's just make a container that does nothing, but will allow us to continue further. Put this in lib/Cheer/Container.pm . The code is just copied from HTML::Widget::Container, ready for our changes:
package Cheer::Container;
use warnings;
use strict;
use base 'HTML::Widget::Container';
sub build_element_label {
my ( $self, $element, $class ) = @_;
return $element unless defined $self->label;
my $l = $self->label->clone;
my $radiogroup;
if ( $class eq 'radiogroup_fieldset' ) {
$element->unshift_content($l);
$radiogroup = 1;
}
elsif ( $self->error && $element->tag eq 'span' ) {
# it might still be a radiogroup wrapped in an error span
for my $elem ( $element->content_refs_list ) {
next unless ref $$elem;
if ( $$elem->attr('class') eq 'radiogroup_fieldset' ) {
$$elem->unshift_content($l);
$radiogroup = 1;
}
}
}
if ( !$radiogroup ) {
# Do we prepend or append input to label?
$element =
( $class eq 'checkbox' or $class eq 'radio' )
? $l->unshift_content($element)
: $l->push_content($element);
}
return $element;
}
1;
Now set it as your container class:
--- Root.pm
+++ Root.pm
@@ -47,6 +47,7 @@
sub feedback : Local {
my ($self, $c) = @_;
my $w = $c->widget('feedback')->method('post')->action($c->req->uri);
+ $w->element_container_class('Cheer::Container');
$w->legend('Tell us what you think!');
Let's say you want your elements to render like this:
<div> <label for="foo">Foo:</label> <input ... /> </div>
Just edit the container:
--- Container.pm
+++ Container.pm
@@ -34,7 +34,7 @@
$element =
( $class eq 'checkbox' or $class eq 'radio' )
? $l->unshift_content($element)
- : $l->push_content($element);
+ : HTML::Element->new('div')->push_content($l,$element);
}
That's it! You can check the new output immediately. For more funky edits, you'll have to read the manual for HTML::Element.
Add custom tags in your labels and forms
AUTHOR
Bogdan Lucaciu bogdan@sns.ro
COPYRIGHT
Copyright 2006 System & Network Solutions - http://www.sns.ro
This document can be freely redistributed and can be modified and re-distributed under the same conditions as Perl itself.