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.