Perl(Amon2)で、Formに自動でエラーメッセージを埋め込みたい。

FormValidator::Liteとかでバリデートした結果でエラーがあった場合に、自動でエラーを埋め込むような感じにしたいなーと。

で、とりあえず書いてみたプラグインが下記。

package MyApp::Web::Plugin::HTML::FillInFormError;
use strict;
use warnings;
use utf8;

use Amon2::Util;
use HTML::Filter::Callbacks;

sub init {
     my ( $self, $c ) = @_;

     Amon2::Util::add_method($c, 'fillin_formerror', \&_fillin_formerror);
}

sub _fillin_formerror {
    my ( $c, $error ) = @_;

    $c->add_trigger(
        'HTML_FILTER' => sub {
            my ($c, $html) = @_;

            my $f = HTML::Filter::Callbacks->new();
            $f->add_callbacks(
                '*' => {
                    start => sub {
                        my $self = shift;
                        if ( $self->attr('name') ) {
                            my $name = $self->attr('name');
                            if ( exists $error->{ $name } ) {
                                $self->prepend('<p class="error">'. $error->{ $name } .'</p>');
                            }
                        }
                    },
                },
            );
            $html = $f->process($html);

            return $html;
        },
    );
    return;
}

1;

HTML::Filter::Callbacksっていう、タグを置換するようなモジュールがあったんでこれを使って、
nameが一致したinputタグの前にprependでpタグ(エラーメッセージ)を追加。

で、上記のプラグインをロード。

__PACKAGE__->load_plugins(
    '+MyApp::Web::Plugin::HTML::FillInFormError',
);

これをエラー発生時に呼び出す。

sub index {
    my ( $class, $c ) = @_;

    # FormValidator::Liteの結果
    my $error = {
      age => 'error message!!'
    };

    $c->fillin_formerror( $error );

    $c->render('index.tx');
}

で、上で読み込んでるindex.txは下記のような感じ。

<form>
  <input type="text" name="age">
</form>

これを実行してみると、

<form>
  <p class="error">error message!!</p><input type="text" name="age">
</form>

って感じなhtmlが出力されるはずです。

と、ここまで書いてみて、
実際はFillinFormと一緒に使う事になるんですが、このまま使うと生成されたhtmlを2回パーズする事になるんでパフォーマンス的にイケてない気が・・。
ここはまだまだ検討の余地がありそう。。