FAQ
Hey all!

I am trying to figure out the most elegant way to build/include multiple
forms in one page using Catalyst::Controller::FormBuilder.

Right now I am doing it as follows...

In my controller:

sub foo : Local Form
{
my ( $self, $c ) = @_;

# Form that actually belongs to "foo"
my $form = $self->formbuilder;

# Form for the search bar, belongs to "foo_search"
my $search_form = $c->forward('foo_search');
}

sub foo_search : Local Form
{
my ( $self, $c ) = @_;

# Form that belongs to "foo_search"
my $form = $self->formbuilder;

# Return the search form
return $form;
}


In the "foo.tt" template:

<!-- Should be the "foo_search" form -->
[% INCLUDE foo_search.tt %]

<!-- Should render the "foo" form -->
[% FormBuilder.render %]


In the foo_search.tt template:

<!-- Should render the "foo_search" form -->
[% FormBuilder.render %]


What I end up with when viewing the "foo" action page is two
"foo_search" forms. I understand why this happening, from an internals
standpoint, but I am not sure how to get around it without ugly hacking.
I would like to be able to keep the niceness of the "Form" pragma in
the controller and all the cool things that go along with it.

Has anyone found a good way to do this? What about if I wanted to build
two different forms from within one page directly in the action (not
using forwards)?

Is the general rule of thumb to only use the "Form" pragma if there will
only be one form, and use FormBuilder directly (so I can choose a unique
variable namespace for each) if there will be multiple?

Thanks!
Danny

Search Discussions

  • Robert 'phaylon' Sedlacek at Feb 12, 2007 at 11:31 am

    Danny Warren wrote:
    What I end up with when viewing the "foo" action page is two
    "foo_search" forms. I understand why this happening, from an internals
    standpoint, but I am not sure how to get around it without ugly hacking.
    I would like to be able to keep the niceness of the "Form" pragma in
    the controller and all the cool things that go along with it.
    I haven't done it, but I guess you could do one form per controller by
    changing the stashed variable names in the controller configuration.

    --
    # Robert 'phaylon' Sedlacek
    # Perl 5/Catalyst Developer in Hamburg, Germany
    { EMail => ' rs@474.at ', Web => ' http://474.at ' }
  • Danny Warren at Feb 12, 2007 at 11:56 am
    Yea, I was thinking of something along those lines. One idea I was
    playing with was creating a new base controller that inherits from
    C::C::FormBuilder, and sets the stashed variables based on the form name
    (from the *.fb config files). That is the least-hacky thing I can think
    of that lets me keep all the niceness without a bunch of extra code in
    each action.

    One bonus to that is that it removes visual ambiguity in the templates
    as to which form is being rendered. Will post back if that works
    decently, but I am still open to ideas if others have solved this one
    already.

    Thanks!
    Danny

    Robert 'phaylon' Sedlacek wrote:
    Danny Warren wrote:
    What I end up with when viewing the "foo" action page is two
    "foo_search" forms. I understand why this happening, from an internals
    standpoint, but I am not sure how to get around it without ugly hacking.
    I would like to be able to keep the niceness of the "Form" pragma in
    the controller and all the cool things that go along with it.
    I haven't done it, but I guess you could do one form per controller by
    changing the stashed variable names in the controller configuration.
  • Matt S Trout at Feb 12, 2007 at 12:08 pm

    On 12 Feb 2007, at 11:56, Danny Warren wrote:

    Yea, I was thinking of something along those lines. One idea I was
    playing with was creating a new base controller that inherits from
    C::C::FormBuilder, and sets the stashed variables based on the form
    name (from the *.fb config files). That is the least-hacky thing I
    can think of that lets me keep all the niceness without a bunch of
    extra code in each action.

    One bonus to that is that it removes visual ambiguity in the
    templates as to which form is being rendered. Will post back if
    that works decently, but I am still open to ideas if others have
    solved this one already.
    RFC additional syntax; the C::C::FormBuilder author is on this list
    and responsive so you could probably get some feedback and be able to
    work with him to get the functionality you need merged into mainline.

    --
    Matt S Trout, Technical Director, Shadowcat Systems Ltd.
    Offering custom development, consultancy and support contracts for
    Catalyst,
    DBIx::Class and BAST. Contact mst (at) shadowcatsystems.co.uk for
    details.
    + Help us build a better perl ORM: http://dbix-
    class.shadowcatsystems.co.uk/ +
  • Juan Camacho at Feb 12, 2007 at 1:19 pm

    On 2/11/07, Danny Warren wrote:
    Hey all!

    I am trying to figure out the most elegant way to build/include multiple
    forms in one page using Catalyst::Controller::FormBuilder.
    I'm in a bit of rush right now and won't have time to respond today,
    so I'm pasting a response I had previously sent to someone else.

    Try setting up your own Action class. This is code I had provided
    someone else trying to do something similar. You probably don't want
    the extra _add_form_to method -- I was simply parroting back some code
    I was provided. The general idea is to provide a top level hash key
    for each form. I don't necessarily recommend the key name used in
    this example -- a better one would be the $formbuilder->name. It
    would be a good idea to provide a more generalized approach that can
    be released to CPAN as C-C-FormBuilder-MultiForm module. Volunteers
    welcome :) Let me know if this helps.

    package MyApp::Controller::Base;

    use strict;
    use base 'Catalyst::Controller::FormBuilder';

    __PACKAGE__->config(
    'Controller::FormBuilder' => {
    action => 'MyApp::Action::FormBuilder',
    }
    );

    package MyApp::Controller::Company;

    use base qw/MyApp::Controller::Base/;

    sub edit : Local Form {
    my ($self, $c) = @_;

    $c->stash->{template} = 'books/edit.tt2';

    my $form = $self->formbuilder;

    if ($form->submitted && $form->validate) {
    return $c->forward('/company/save');
    }

    $c->forward('/company/searchform');
    }

    sub searchform : Local Form('/searchform') {
    my ($self, $c) = @_;
    my $searchform = $self->formbuilder;
    my $fields = $searchform->fields;
    $c->stash->{template} = 'searchresults.tt2';
    $c->stash->{search_results} = $c->model('Listings')->search($fields);
    }

    package MyApp::Action::FormBuilder;

    use strict;
    use base 'Catalyst::Controller::FormBuilder::Action::TT';

    sub setup_template_vars {
    my $self = shift;
    my ( $controller, $c ) = @_;

    $self->SUPER::setup_template_vars(@_);

    my $forms = {};
    my $name = $self->_attr_params->[0] || $self->reverse;
    _add_form_to( $forms, split('/', $name), $c->stash->{FormBuilder} );

    $c->stash->{forms} = $forms;
    }

    sub _add_form_to {
    my $href = shift;
    my $node_count = scalar @_;
    return if !$node_count;
    my $node_count = scalar @_;
    return if !$node_count;
    return shift if $node_count == 1;
    my $node = shift;
    $href->{$node} = _add_form_to( { }, @_ );
    return $href;
    }
  • Juan Camacho at Feb 12, 2007 at 1:22 pm

    On 2/12/07, Juan Camacho wrote:
    On 2/11/07, Danny Warren wrote:
    Hey all!

    I am trying to figure out the most elegant way to build/include multiple
    forms in one page using Catalyst::Controller::FormBuilder.
    I'm in a bit of rush right now and won't have time to respond today,
    so I'm pasting a response I had previously sent to someone else.

    Try setting up your own Action class. This is code I had provided
    someone else trying to do something similar. You probably don't want
    the extra _add_form_to method -- I was simply parroting back some code
    I was provided. The general idea is to provide a top level hash key
    for each form. I don't necessarily recommend the key name used in
    this example -- a better one would be the $formbuilder->name. It
    would be a good idea to provide a more generalized approach that can
    be released to CPAN as C-C-FormBuilder-MultiForm module. Volunteers
    welcome :) Let me know if this helps.

    package MyApp::Controller::Base;

    use strict;
    use base 'Catalyst::Controller::FormBuilder';

    __PACKAGE__->config(
    'Controller::FormBuilder' => {
    action => 'MyApp::Action::FormBuilder',
    }
    );

    package MyApp::Controller::Company;

    use base qw/MyApp::Controller::Base/;

    sub edit : Local Form {
    my ($self, $c) = @_;

    $c->stash->{template} = 'books/edit.tt2';

    my $form = $self->formbuilder;

    if ($form->submitted && $form->validate) {
    return $c->forward('/company/save');
    }

    $c->forward('/company/searchform');
    }

    sub searchform : Local Form('/searchform') {
    my ($self, $c) = @_;
    my $searchform = $self->formbuilder;
    my $fields = $searchform->fields;
    $c->stash->{template} = 'searchresults.tt2';
    $c->stash->{search_results} = $c->model('Listings')->search($fields);
    }

    package MyApp::Action::FormBuilder;

    use strict;
    use base 'Catalyst::Controller::FormBuilder::Action::TT';

    sub setup_template_vars {
    my $self = shift;
    my ( $controller, $c ) = @_;

    $self->SUPER::setup_template_vars(@_);

    my $forms = {};
    my $name = $self->_attr_params->[0] || $self->reverse;
    _add_form_to( $forms, split('/', $name), $c->stash->{FormBuilder} );

    $c->stash->{forms} = $forms;
    }

    sub _add_form_to {
    my $href = shift;
    my $node_count = scalar @_;
    return if !$node_count;
    my $node_count = scalar @_;
    return if !$node_count;
    return shift if $node_count == 1;
    my $node = shift;
    $href->{$node} = _add_form_to( { }, @_ );
    return $href;
    }
    btw, this only work off C-C-FormBuilder that's up on
    http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Controller-FormBuilder/

    It will eventually be released.
  • Danny Warren at Feb 12, 2007 at 9:49 pm
    Thank you Juan! This is along the lines of what I am looking for, and
    gives me a good place to start from. I will play around with this today
    and get back to you (unfortunately dev.catalyst.perl.org is unresponsive
    at the moment, but I did get a chance to browse the repo and see the
    current C::C::FormBuilder source before it went down).

    Once I get something working I will post it here, and I would love to
    help come up with something reusable for CPAN to solve this if the code
    is general enough.

    Thanks again for your help and being so active on the list. Reading
    through you other posts in the archive has been quite helpful.

    Danny

    Juan Camacho wrote:
    On 2/11/07, Danny Warren wrote:
    Hey all!

    I am trying to figure out the most elegant way to build/include multiple
    forms in one page using Catalyst::Controller::FormBuilder.
    I'm in a bit of rush right now and won't have time to respond today,
    so I'm pasting a response I had previously sent to someone else.

    Try setting up your own Action class. This is code I had provided
    someone else trying to do something similar. You probably don't want
    the extra _add_form_to method -- I was simply parroting back some code
    I was provided. The general idea is to provide a top level hash key
    for each form. I don't necessarily recommend the key name used in
    this example -- a better one would be the $formbuilder->name. It
    would be a good idea to provide a more generalized approach that can
    be released to CPAN as C-C-FormBuilder-MultiForm module. Volunteers
    welcome :) Let me know if this helps.

    package MyApp::Controller::Base;

    use strict;
    use base 'Catalyst::Controller::FormBuilder';

    __PACKAGE__->config(
    'Controller::FormBuilder' => {
    action => 'MyApp::Action::FormBuilder',
    }
    );

    package MyApp::Controller::Company;

    use base qw/MyApp::Controller::Base/;

    sub edit : Local Form {
    my ($self, $c) = @_;

    $c->stash->{template} = 'books/edit.tt2';

    my $form = $self->formbuilder;

    if ($form->submitted && $form->validate) {
    return $c->forward('/company/save');
    }

    $c->forward('/company/searchform');
    }

    sub searchform : Local Form('/searchform') {
    my ($self, $c) = @_;
    my $searchform = $self->formbuilder;
    my $fields = $searchform->fields;
    $c->stash->{template} = 'searchresults.tt2';
    $c->stash->{search_results} = $c->model('Listings')->search($fields);
    }

    package MyApp::Action::FormBuilder;

    use strict;
    use base 'Catalyst::Controller::FormBuilder::Action::TT';

    sub setup_template_vars {
    my $self = shift;
    my ( $controller, $c ) = @_;

    $self->SUPER::setup_template_vars(@_);

    my $forms = {};
    my $name = $self->_attr_params->[0] || $self->reverse;
    _add_form_to( $forms, split('/', $name), $c->stash->{FormBuilder} );

    $c->stash->{forms} = $forms;
    }

    sub _add_form_to {
    my $href = shift;
    my $node_count = scalar @_;
    return if !$node_count;
    my $node_count = scalar @_;
    return if !$node_count;
    return shift if $node_count == 1;
    my $node = shift;
    $href->{$node} = _add_form_to( { }, @_ );
    return $href;
    }

    _______________________________________________
    List: Catalyst@lists.rawmode.org
    Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
    Dev site: http://dev.catalyst.perl.org/
  • Matt S Trout at Feb 14, 2007 at 9:28 am

    On 12 Feb 2007, at 21:49, Danny Warren wrote:

    Thank you Juan! This is along the lines of what I am looking for,
    and gives me a good place to start from. I will play around with
    this today and get back to you (unfortunately dev.catalyst.perl.org
    is unresponsive at the moment, but I did get a chance to browse the
    repo and see the current C::C::FormBuilder source before it went
    down).
    Odd, we had some trouble over the weekend but the system was fine on
    monday.

    --
    Matt S Trout, Technical Director, Shadowcat Systems Ltd.
    Offering custom development, consultancy and support contracts for
    Catalyst,
    DBIx::Class and BAST. Contact mst (at) shadowcatsystems.co.uk for
    details.
    + Help us build a better perl ORM: http://dbix-
    class.shadowcatsystems.co.uk/ +
  • Danny Warren at Feb 14, 2007 at 11:10 am
    By the time the message arrived on the list it was back up... ;)

    Matt S Trout wrote:
    On 12 Feb 2007, at 21:49, Danny Warren wrote:

    Thank you Juan! This is along the lines of what I am looking for, and
    gives me a good place to start from. I will play around with this
    today and get back to you (unfortunately dev.catalyst.perl.org is
    unresponsive at the moment, but I did get a chance to browse the repo
    and see the current C::C::FormBuilder source before it went down).
    Odd, we had some trouble over the weekend but the system was fine on
    monday.

    --Matt S Trout, Technical Director, Shadowcat Systems Ltd.
    Offering custom development, consultancy and support contracts for
    Catalyst,
    DBIx::Class and BAST. Contact mst (at) shadowcatsystems.co.uk for details.
    + Help us build a better perl ORM:
    http://dbix-class.shadowcatsystems.co.uk/ +



    _______________________________________________
    List: Catalyst@lists.rawmode.org
    Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
    Dev site: http://dev.catalyst.perl.org/
  • Danny Warren at Feb 13, 2007 at 3:11 am
    (Started a new thread, since your previous response answered my basic
    questions, but I think my problem is something else entirely)

    I used your below sample code to cook up something that supports a
    naming scheme for multiple form variables in TT, but I am still having
    issues. I tracked it down to something in C::C::FormBuilder itself that
    I can't seem to get around, and I was wondering if you had any ideas.

    It looks to me that forwarding from one "Form" action to another "Form"
    action causes the FormBuilder data from the first action to be
    overwritten.

    I added the following lines to Action::TT to confirm this...

    $c->log->debug( 'ACTION: ' . $c->action );
    $c->log->debug( 'REAL ACTION: ' . ( $self->_attr_params->[0] ||
    $self->reverse ) );
    $c->log->debug( 'FORM NAME: ' . $controller->_formbuilder->name );


    (I also dumped out the entire FormBuilder object, but I won't post that
    here since it is huge)

    Here is a snippet from the debug log that illustrates this (in my sample
    code "foo" forwards to "foo_search"):

    [debug] Form (foo): Looking for config file foo.fb
    [debug] Form (foo): Found form config
    /home/dwarren/MyApp/root/forms/foo.fb

    [debug] Form (foo_search): Looking for config file foo_search.fb
    [debug] Form (foo_search): Found form config
    /home/dwarren/MyApp/foo_search.fb

    [debug] ACTION: foo
    [debug] REAL ACTION: foo_search
    [debug] FORM NAME: foo_search

    [debug] ACTION: foo
    [debug] REAL ACTION: foo
    [debug] FORM NAME: foo_search

    Can you confirm this, or am I doing something wrong here? I am still
    somewhat green with Catalyst, so I might be misunderstanding exactly how
    forwards work. From reading the docs, I was under the impression that
    two actions should not clobber each other upon forwarding (since
    forwarded actions are processed inside an eval).

    Thanks again!
    Danny

    Juan Camacho wrote:
    On 2/11/07, Danny Warren wrote:
    Hey all!

    I am trying to figure out the most elegant way to build/include multiple
    forms in one page using Catalyst::Controller::FormBuilder.
    I'm in a bit of rush right now and won't have time to respond today,
    so I'm pasting a response I had previously sent to someone else.

    Try setting up your own Action class. This is code I had provided
    someone else trying to do something similar. You probably don't want
    the extra _add_form_to method -- I was simply parroting back some code
    I was provided. The general idea is to provide a top level hash key
    for each form. I don't necessarily recommend the key name used in
    this example -- a better one would be the $formbuilder->name. It
    would be a good idea to provide a more generalized approach that can
    be released to CPAN as C-C-FormBuilder-MultiForm module. Volunteers
    welcome :) Let me know if this helps.

    package MyApp::Controller::Base;

    use strict;
    use base 'Catalyst::Controller::FormBuilder';

    __PACKAGE__->config(
    'Controller::FormBuilder' => {
    action => 'MyApp::Action::FormBuilder',
    }
    );

    package MyApp::Controller::Company;

    use base qw/MyApp::Controller::Base/;

    sub edit : Local Form {
    my ($self, $c) = @_;

    $c->stash->{template} = 'books/edit.tt2';

    my $form = $self->formbuilder;

    if ($form->submitted && $form->validate) {
    return $c->forward('/company/save');
    }

    $c->forward('/company/searchform');
    }

    sub searchform : Local Form('/searchform') {
    my ($self, $c) = @_;
    my $searchform = $self->formbuilder;
    my $fields = $searchform->fields;
    $c->stash->{template} = 'searchresults.tt2';
    $c->stash->{search_results} = $c->model('Listings')->search($fields);
    }

    package MyApp::Action::FormBuilder;

    use strict;
    use base 'Catalyst::Controller::FormBuilder::Action::TT';

    sub setup_template_vars {
    my $self = shift;
    my ( $controller, $c ) = @_;

    $self->SUPER::setup_template_vars(@_);

    my $forms = {};
    my $name = $self->_attr_params->[0] || $self->reverse;
    _add_form_to( $forms, split('/', $name), $c->stash->{FormBuilder} );

    $c->stash->{forms} = $forms;
    }

    sub _add_form_to {
    my $href = shift;
    my $node_count = scalar @_;
    return if !$node_count;
    my $node_count = scalar @_;
    return if !$node_count;
    return shift if $node_count == 1;
    my $node = shift;
    $href->{$node} = _add_form_to( { }, @_ );
    return $href;
    }

    _______________________________________________
    List: Catalyst@lists.rawmode.org
    Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
    Dev site: http://dev.catalyst.perl.org/
  • Juan Camacho at Feb 14, 2007 at 1:27 am

    On 2/12/07, Danny Warren wrote:
    It looks to me that forwarding from one "Form" action to another "Form"
    action causes the FormBuilder data from the first action to be
    overwritten.
    Can you confirm this, or am I doing something wrong here? I am still
    somewhat green with Catalyst, so I might be misunderstanding exactly how
    forwards work. From reading the docs, I was under the impression that
    two actions should not clobber each other upon forwarding (since
    forwarded actions are processed inside an eval).
    Each action will have it's own formbuilder object instance. After you
    return from the forward to another form action, $self->formbuilder
    will be a brand new FormBuilder object.

    sub foo : Form('foo') {
    my ($self, $c) = @_;
    # $self->formbuilder is for foo form
    $self->forward('bar');
    # now $self->formbuilder is for bar form
    }

    sub bar : Form('bar') { ... }

    Consequently, each action should do it's own thing with it's
    FormBuilder object -- e.g. validate, add or change form fields). Not
    sure what kind of problem you are having as a result of this since you
    didn't provide sample Controller code.
  • Danny Warren at Feb 14, 2007 at 11:50 am
    Apologies, I should have mentioned that I was using the controller code
    from my first mail. I have attached my test controller as Test.pm.

    Thank you for your explanation about how $c->formbuilder works with
    forwards, that makes some of what I am seeing make more sense (and that
    I had some incorrect assumptions about how forwards worked, will brush
    up on the catalyst docs).

    I should clarify my question from the previous mail, reading it again it
    wasn't very clear.

    Based on the code you provided, I created a new Action::TT that attempts
    to place a copy of the fb objects in a hash so it can be later accessed
    by form name in the template (ex: forms.foo.FormBuilder). My base
    controller is attached as Multiple.pm and my modified Action::TT is
    attached as TT.pm.

    The problem is that, even though the C::C::FormBuilder code is called
    twice (once for each action), only the last form to be processed is kept
    in $controller->_formbuilder. Makes sense, given what you explained
    below about forwards, but that still leaves me wondering how to:

    a) grab the name of the form so I can put multiple forms in a stash hash
    (since _formbuilder->name is always that of the last form forwarded to)

    b) put the correct form data in the stash for each form, since the data
    for the last form processed will always be in _formbuilder.

    To see this in action, here is the log snippet from my last e-mail (you
    can see that C::C::FB runs against both actions, foo and foo_search, but
    that foo_search's data is the only thing that ends up in _formbuilder
    for both). See attached TT.pm for how I am grabbing the form data,
    maybe I am doing it wrong...

    [debug] Form (foo): Looking for config file foo.fb
    [debug] Form (foo): Found form config
    /home/dwarren/MyApp/root/forms/foo.fb

    [debug] Form (foo_search): Looking for config file foo_search.fb
    [debug] Form (foo_search): Found form config
    /home/dwarren/MyApp/foo_search.fb

    [debug] ACTION: foo
    [debug] REAL ACTION: foo_search
    [debug] FORM NAME: foo_search

    [debug] ACTION: foo
    [debug] REAL ACTION: foo
    [debug] FORM NAME: foo_search


    I also ran the same code you provided in your last e-mail, and it
    happens there as well - the data is for the last form that was forwarded
    to. I would expect that C::C::FormBuilder is called as it comes across
    the action (which it is) but it looks like "setup_template_vars" happens
    later, once $_formbuilder has been populated with the forwarded data.

    If I am just not grasping something, let me know. Maybe there is
    something funky with my Catalyst environment as well (if your sample
    code works for you and it does not for me)?

    Sorry again if this confusing or long-winded. I have just been pulling
    my hair out on this one, and I don't think I know the guts of catalyst
    well enough yet to properly debug and explain what I am seeing.

    Thank you again for your time!
    Danny


    Juan Camacho wrote:
    On 2/12/07, Danny Warren wrote:
    It looks to me that forwarding from one "Form" action to another "Form"
    action causes the FormBuilder data from the first action to be
    overwritten.
    Can you confirm this, or am I doing something wrong here? I am still
    somewhat green with Catalyst, so I might be misunderstanding exactly how
    forwards work. From reading the docs, I was under the impression that
    two actions should not clobber each other upon forwarding (since
    forwarded actions are processed inside an eval).
    Each action will have it's own formbuilder object instance. After you
    return from the forward to another form action, $self->formbuilder
    will be a brand new FormBuilder object.

    sub foo : Form('foo') {
    my ($self, $c) = @_;
    # $self->formbuilder is for foo form
    $self->forward('bar');
    # now $self->formbuilder is for bar form
    }

    sub bar : Form('bar') { ... }

    Consequently, each action should do it's own thing with it's
    FormBuilder object -- e.g. validate, add or change form fields). Not
    sure what kind of problem you are having as a result of this since you
    didn't provide sample Controller code.

    _______________________________________________
    List: Catalyst@lists.rawmode.org
    Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
    Dev site: http://dev.catalyst.perl.org/
    -------------- next part --------------
    package Catalyst::Controller::FormBuilder::Multiple;

    use strict;
    use base qw| Catalyst::Controller::FormBuilder |;

    sub __setup
    {
    my $self = shift;
    my $class = ref $self;

    # Get a copy of the template setting or use default
    # NOTE: Duplicated from parent, unsure of best way to do this cleanly
    my $config = $self->config->{'Controller::FormBuilder'} || {};
    my $tmpl_type = $config->{template_type} || "TT";

    # Override the action value from the config to point to our template
    # actions, which will be passed on to the parent's setup method
    $config->{action} ||= "Catalyst::Controller::FormBuilder::Multiple::Action::$tmpl_type";

    $self->SUPER::__setup();
    }

    1;
    -------------- next part --------------
    package MyApp::Controller::Test;

    use strict;
    use warnings;
    use base 'Catalyst::Controller::FormBuilder::Multiple';

    sub foo : Local Form
    {
    my ( $self, $c ) = @_;
    my $foo_form = $self->formbuilder;
    my $foo_search_form = $c->forward('foo_search');
    }

    sub foo_search : Local Form
    {
    my ( $self, $c ) = @_;
    my $form = $self->formbuilder;
    return $form;
    }

    1;
    -------------- next part --------------
    package Catalyst::Controller::FormBuilder::Multiple::Action::TT;

    use strict;
    use base qw| Catalyst::Controller::FormBuilder::Action::TT |;

    sub setup_template_vars
    {
    my $self = shift;
    my ( $controller, $c ) = @_;

    $self->SUPER::setup_template_vars(@_);

    # Get the name of the current form, and the stash and obj names that have already
    # been created
    my $form_name = $controller->_formbuilder->name;
    my $stash_name = $controller->_fb_setup->{stash_name};
    my $obj_name = $controller->_fb_setup->{obj_name};

    # If a form name is defined, create aliases to this form by name
    # NOTE: Will make multiple form hash key configurable later
    if ( defined $form_name )
    {
    $c->stash->{forms}{$form_name}{$stash_name} = $c->stash->{$stash_name};
    $c->stash->{forms}{$form_name}{$obj_name} = $c->stash->{$obj_name};
    }

    # DEBUG, will remove later
    $c->log->debug("REAL ACTION: " . ( $self->_attr_params->[0] || $self->reverse ) );
    $c->log->debug("ACTION: " . $c->action );
    $c->log->debug("FORM NAME: " . $form_name);
    }

    1;
  • Juan Camacho at Feb 14, 2007 at 12:53 pm
    Doubt it has anything to do with your environment. I thought I had
    tested it a while back when helping someone else, but maybe something
    was overlooked since I never got a final word back from the guy I was
    helping. I think I have an idea of what might be wrong -- it has to
    do with the following sequence of events:

    Action foo
    ========
    1) setup new FormBuilder before action
    2) Run foo action which then $c->forwards to foo_search
    Action foo_search
    ==============
    1a) setup new FormBuilder before action
    1b) run foo_search action
    1c) setup_template_vars for foo_search after action
    3) setup_template_vars for foo after action

    Problem in #3 is that $self->_formbuilder is clobbered from the inner
    forward action.

    I will try to look into this today and get back to you, but it may not
    be until quite late tonight and I won't be able to spend too much time
    on it until this weekend. In the mean time, send *me* directly a
    zip/gz of a minimal MyApp catalyst application (including forms and tt
    files) so I can quickly get going on it.

    Hopefully others will chime in to provide other ideas and
    clarification of the action sequence.

    On 2/14/07, Danny Warren wrote:
    Apologies, I should have mentioned that I was using the controller code
    from my first mail. I have attached my test controller as Test.pm.

    Thank you for your explanation about how $c->formbuilder works with
    forwards, that makes some of what I am seeing make more sense (and that
    I had some incorrect assumptions about how forwards worked, will brush
    up on the catalyst docs).

    I should clarify my question from the previous mail, reading it again it
    wasn't very clear.

    Based on the code you provided, I created a new Action::TT that attempts
    to place a copy of the fb objects in a hash so it can be later accessed
    by form name in the template (ex: forms.foo.FormBuilder). My base
    controller is attached as Multiple.pm and my modified Action::TT is
    attached as TT.pm.

    The problem is that, even though the C::C::FormBuilder code is called
    twice (once for each action), only the last form to be processed is kept
    in $controller->_formbuilder. Makes sense, given what you explained
    below about forwards, but that still leaves me wondering how to:

    a) grab the name of the form so I can put multiple forms in a stash hash
    (since _formbuilder->name is always that of the last form forwarded to)

    b) put the correct form data in the stash for each form, since the data
    for the last form processed will always be in _formbuilder.

    To see this in action, here is the log snippet from my last e-mail (you
    can see that C::C::FB runs against both actions, foo and foo_search, but
    that foo_search's data is the only thing that ends up in _formbuilder
    for both). See attached TT.pm for how I am grabbing the form data,
    maybe I am doing it wrong...

    [debug] Form (foo): Looking for config file foo.fb
    [debug] Form (foo): Found form config
    /home/dwarren/MyApp/root/forms/foo.fb

    [debug] Form (foo_search): Looking for config file foo_search.fb
    [debug] Form (foo_search): Found form config
    /home/dwarren/MyApp/foo_search.fb

    [debug] ACTION: foo
    [debug] REAL ACTION: foo_search
    [debug] FORM NAME: foo_search

    [debug] ACTION: foo
    [debug] REAL ACTION: foo
    [debug] FORM NAME: foo_search


    I also ran the same code you provided in your last e-mail, and it
    happens there as well - the data is for the last form that was forwarded
    to. I would expect that C::C::FormBuilder is called as it comes across
    the action (which it is) but it looks like "setup_template_vars" happens
    later, once $_formbuilder has been populated with the forwarded data.

    If I am just not grasping something, let me know. Maybe there is
    something funky with my Catalyst environment as well (if your sample
    code works for you and it does not for me)?

    Sorry again if this confusing or long-winded. I have just been pulling
    my hair out on this one, and I don't think I know the guts of catalyst
    well enough yet to properly debug and explain what I am seeing.

    Thank you again for your time!
    Danny


    Juan Camacho wrote:
    On 2/12/07, Danny Warren wrote:
    It looks to me that forwarding from one "Form" action to another "Form"
    action causes the FormBuilder data from the first action to be
    overwritten.
    Can you confirm this, or am I doing something wrong here? I am still
    somewhat green with Catalyst, so I might be misunderstanding exactly how
    forwards work. From reading the docs, I was under the impression that
    two actions should not clobber each other upon forwarding (since
    forwarded actions are processed inside an eval).
    Each action will have it's own formbuilder object instance. After you
    return from the forward to another form action, $self->formbuilder
    will be a brand new FormBuilder object.

    sub foo : Form('foo') {
    my ($self, $c) = @_;
    # $self->formbuilder is for foo form
    $self->forward('bar');
    # now $self->formbuilder is for bar form
    }

    sub bar : Form('bar') { ... }

    Consequently, each action should do it's own thing with it's
    FormBuilder object -- e.g. validate, add or change form fields). Not
    sure what kind of problem you are having as a result of this since you
    didn't provide sample Controller code.

    _______________________________________________
    List: Catalyst@lists.rawmode.org
    Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
    Dev site: http://dev.catalyst.perl.org/
    package Catalyst::Controller::FormBuilder::Multiple;

    use strict;
    use base qw| Catalyst::Controller::FormBuilder |;

    sub __setup
    {
    my $self = shift;
    my $class = ref $self;

    # Get a copy of the template setting or use default
    # NOTE: Duplicated from parent, unsure of best way to do this cleanly
    my $config = $self->config->{'Controller::FormBuilder'} || {};
    my $tmpl_type = $config->{template_type} || "TT";

    # Override the action value from the config to point to our template
    # actions, which will be passed on to the parent's setup method
    $config->{action} ||= "Catalyst::Controller::FormBuilder::Multiple::Action::$tmpl_type";

    $self->SUPER::__setup();
    }

    1;

    package MyApp::Controller::Test;

    use strict;
    use warnings;
    use base 'Catalyst::Controller::FormBuilder::Multiple';

    sub foo : Local Form
    {
    my ( $self, $c ) = @_;
    my $foo_form = $self->formbuilder;
    my $foo_search_form = $c->forward('foo_search');
    }

    sub foo_search : Local Form
    {
    my ( $self, $c ) = @_;
    my $form = $self->formbuilder;
    return $form;
    }

    1;
    package Catalyst::Controller::FormBuilder::Multiple::Action::TT;

    use strict;
    use base qw| Catalyst::Controller::FormBuilder::Action::TT |;

    sub setup_template_vars
    {
    my $self = shift;
    my ( $controller, $c ) = @_;

    $self->SUPER::setup_template_vars(@_);

    # Get the name of the current form, and the stash and obj names that have already
    # been created
    my $form_name = $controller->_formbuilder->name;
    my $stash_name = $controller->_fb_setup->{stash_name};
    my $obj_name = $controller->_fb_setup->{obj_name};

    # If a form name is defined, create aliases to this form by name
    # NOTE: Will make multiple form hash key configurable later
    if ( defined $form_name )
    {
    $c->stash->{forms}{$form_name}{$stash_name} = $c->stash->{$stash_name};
    $c->stash->{forms}{$form_name}{$obj_name} = $c->stash->{$obj_name};
    }

    # DEBUG, will remove later
    $c->log->debug("REAL ACTION: " . ( $self->_attr_params->[0] || $self->reverse ) );
    $c->log->debug("ACTION: " . $c->action );
    $c->log->debug("FORM NAME: " . $form_name);
    }

    1;
    _______________________________________________
    List: Catalyst@lists.rawmode.org
    Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
    Dev site: http://dev.catalyst.perl.org/
  • Danny Warren at Feb 14, 2007 at 7:46 pm
    Yep, that sequence of events is exactly what I am seeing. Hooray, I am
    not crazy! (well, on this one thing...)

    One way I was thinking of getting around this was to do the same thing
    you suggested with the template vars for the $controller->_formbuilder
    data - create named aliases (at step 1 and 1a below) so that the CGI::FB
    object can be grabbed even if clobbered by the forward. I will try this
    out while you are checking in to things yourself (I haven't done it yet
    because it doesn't look clean to implement, and I thought I was doing
    something really wrong).

    What I guess we would end up with is an FB::Multiple module that:

    1) Still provide the normal ways of getting at form data, so that your
    actions with no forwards still work like normal (ex: [%
    FormBuilder.render %] and $self->formbuilder)

    2) Provide an alternate way of getting at form stuff, so that you can
    get data even if you are using forwards (ex: [%
    forms.foo.FormBuilder.render %] and $self->forms{foo} or
    $self->forms_foo - still haven't decided on a naming scheme)

    Will send you my test app in a moment.

    Thanks again!

    Danny

    Juan Camacho wrote:
    Doubt it has anything to do with your environment. I thought I had
    tested it a while back when helping someone else, but maybe something
    was overlooked since I never got a final word back from the guy I was
    helping. I think I have an idea of what might be wrong -- it has to
    do with the following sequence of events:

    Action foo
    ========
    1) setup new FormBuilder before action
    2) Run foo action which then $c->forwards to foo_search
    Action foo_search
    ==============
    1a) setup new FormBuilder before action
    1b) run foo_search action
    1c) setup_template_vars for foo_search after action
    3) setup_template_vars for foo after action

    Problem in #3 is that $self->_formbuilder is clobbered from the inner
    forward action.

    I will try to look into this today and get back to you, but it may not
    be until quite late tonight and I won't be able to spend too much time
    on it until this weekend. In the mean time, send *me* directly a
    zip/gz of a minimal MyApp catalyst application (including forms and tt
    files) so I can quickly get going on it.

    Hopefully others will chime in to provide other ideas and
    clarification of the action sequence.
  • Juan Camacho at Feb 15, 2007 at 12:43 am
    OK, I've made a small change to the C-C-FormBuilder-Action class that
    should now help get you on your way. Instead of [% FormBuilder.render
    %], you'll need to use [% forms.foo.FormBuilder.render %]. BTW, try
    to make the the stash name 'forms' configurable. I look forward to
    seeing a final version of MultiForm.

    Index: lib/Catalyst/Controller/FormBuilder/Action.pm
    ===================================================================
    --- lib/Catalyst/Controller/FormBuilder/Action.pm (revision 6038)
    +++ lib/Catalyst/Controller/FormBuilder/Action.pm (working copy)
    @@ -133,6 +133,7 @@
    my $form = $self->_setup_form(@_);
    $controller->_formbuilder($form);
    $self->NEXT::execute(@_);
    + $controller->_formbuilder($form); # keep the same form in case
    of forwards

    $self->setup_template_vars( @_ );
    }

    Once I'm finished with an additional unit test, I'll release to CPAN.
    On 2/14/07, Danny Warren wrote:
    Attached is a copy of the MyApp I am using to test this.

    Quick explanation of what is what:

    lib/MyApp/Controller/Company.pm + Base.pm:

    The sample code you provided, with action names renamed for laziness
    (so I could reuse the templates, etc)

    lib/MyApp/Controller/Test.pm:

    My test app, pointing at the FB::Multiple module (which is not really
    working yet, so I am mainly just using it so I can spit out log msgs)

    lib/Catalyst:

    The FB::Multiple lib I am using (with debug log msgs)

    So you should be able to hit /company/foo or /test/foo and look in the
    logs for the "ACTION" / "REAL ACTION" / "FORM NAME" debug messages.

    Also note, I am aware now that my TT files are kinda wrong - I don't
    know why I expected that including a second TT with another [%
    FormBuilder.render %] in it would magically work (I know now that it is
    doing exactly what it should be - rendering two copies of whatever is in
    $self->formbuilder). I just kept them there, since I am looking at the
    logs and not at the page for testing. I planned on adjusting the
    templates once I got the "access by form name" thing working.

    Let me know if there is anything else I can do to help!

    Thanks,
    Danny

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupcatalyst @
categoriescatalyst, perl
postedFeb 12, '07 at 1:50a
activeFeb 15, '07 at 12:43a
posts15
users4
websitecatalystframework.org
irc#catalyst

People

Translate

site design / logo © 2022 Grokbase