X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FTutorial%2FAdvancedCRUD.pod;h=96fb811de10ef52b81a7463ab5b0cb0765db2924;hb=936a5dd5c424d816dd470690473c1e38127a0c3c;hp=c68d6c846b26058f6039bbabb261dbe3e2339886;hpb=be16bacd7d5dcea0165355cb7bbd8a14c1af184e;p=catagits%2FCatalyst-Runtime.git diff --git a/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod b/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod index c68d6c8..96fb811 100644 --- a/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod +++ b/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod @@ -58,7 +58,7 @@ validation of user-entered data, and automated transfer of data between forms and model objects. In keeping with the Catalyst (and Perl) spirit of flexibility, there are -many different ways approach advanced CRUD operations in a Catalyst +many different ways to approach advanced CRUD operations in a Catalyst environment. One alternative is to use L to instantly construct a set of Controller methods and templates for @@ -70,7 +70,7 @@ L. Here, we will make use of the L to not only ease form creation, but to also provide validation of the submitted -data. The approached used by the part of the tutorial is to slowly +data. The approached used by this part of the tutorial is to slowly incorporate additional L functionality in a step-wise fashion (we start with fairly simple form creation and then move on to more complex and "magical" features such as validation and @@ -79,12 +79,7 @@ auto-population/auto-saving). B Part 8 of the tutorial is optional. Users who do not wish to use L may skip this part. -B: Note that all of the code for this part of the tutorial can be -pulled from the Catalyst Subversion repository in one step with the -following command: - - svn checkout http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial@### - IMPORTANT: Does not work yet. Will be completed for final version. +You can checkout the source code for this example from the catalyst subversion repository as per the instructions in L =head1 C FORM CREATION @@ -131,12 +126,12 @@ following method: return $w; } -This method provides a central location (so it can be called by multiple -actions, such as C and C) that builds an HTML::Widget-based -form with the appropriate fields. The "Get Authors" code uses DBIC to -retrieve a list of model objects and then uses C to create a hash -where the hash keys are the database primary keys from the authors table -and the associated values are the last names of the authors. +This method provides a central location that builds an +HTML::Widget-based form with the appropriate fields. The "Get authors" +code uses DBIC to retrieve a list of model objects and then uses C +to create a hash where the hash keys are the database primary keys from +the authors table and the associated values are the last names of the +authors. =head2 Add Actions to Display and Save the Form @@ -192,9 +187,9 @@ following methods: # Set a status message for the user $c->stash->{status_msg} = 'Book created'; - # Use 'hw_create' to redisplay the form + # Use 'hw_create' to redisplay the form. As discussed in + # Part 3, 'detach' is like 'forward', but it does not return $c->detach('hw_create'); - } Note how we use C to build the core parts of the form @@ -430,6 +425,8 @@ now has a C<$c-Estash-E{template}> line). Note that if we process the form in C I forward/detach back to , we would end up with C being called twice, resulting in a duplicate set of elements being added to the form. +(There are other ways to address the "duplicate form rendering" issue -- +just be aware that it exists.) =item * @@ -449,7 +446,8 @@ similar to the prior version of the C method. =head2 Try Out the Form -Press C to kill the previous server instance (if it's still running) and restart it: +Press C to kill the previous server instance (if it's still +running) and restart it: $ script/myapp_server.pl @@ -459,6 +457,7 @@ two, and zero authors. When you click Submit, the HTML::Widget C items will validate the logic and insert feedback as appropriate. + =head1 Enable C Support In this section we will take advantage of some of the "auto-population" @@ -542,6 +541,9 @@ match the following code: # Set a status message for the user $c->stash->{status_msg} = 'Book created'; + + # Redisplay an empty form for another + $c->stash->{widget_result} = $w->result; } # Set the template @@ -554,7 +556,190 @@ C<$c-Emodel('MyAppDB::Book')-Ecreate> and replaced it with a single call to C<$book-Epopulate_from_widget>. Note that we still have to call C<$book-Eadd_to_book_authors> once per author because C does not currently handle the relationships -between tables. +between tables. Also, we reset the form to an empty fields by adding +another call to C<$w-Eresult> and storing the output in the stash +(if we don't override the output from C<$w-Eprocess($c-Ereq)>, +the form values already entered will be retained on redisplay -- +although this could be desirable for some applications, we avoid it +here to help avoid the creation of duplicate records). + + +=head2 Try Out the Form + +Press C to kill the previous server instance (if it's still +running) and restart it: + + $ script/myapp_server.pl + +Try adding a book that validate. Return to the book list and the book +you added should be visible. + + + +=head1 Rendering C Forms in a Table + +Some developers my wish to use the "old-fashioned" table style of +rendering a form in lieu of the default C rendering that +assumes you will use CSS for formatting. This section demonstrates +some techniques that can override the default rendering with a +custom class. + + +=head2 Add a New "Element Container" + +Open C in your editor and enter: + + package FormElementContainer; + + use base 'HTML::Widget::Container'; + + sub _build_element { + my ($self, $element) = @_; + + return () unless $element; + if (ref $element eq 'ARRAY') { + return map { $self->_build_element($_) } @{$element}; + } + my $e = $element->clone; + $e = new HTML::Element('span', class => 'fields_with_errors')->push_content($e) + if $self->error && $e->tag eq 'input'; + + return $e ? ($e) : (); + } + + 1; + +This simply dumps the HTML code for a given form element, followed by a +C that can contain validation error message. + + +=head2 Enable the New Element Container When Building the Form + +Open C in your editor. First add a +C for your element container class: + + use FormElementContainer; + +B If you forget to C your container class in your +controller, then your form will not be displayed and no error messages +will be generated. Don't forget this important step! + +Then tell C to use that class during rendering by updating +C to match the following: + + sub make_book_widget { + my ($self, $c) = @_; + + # Create an HTML::Widget to build the form + my $w = $c->widget('book_form')->method('post'); + + # ***New: Use custom class to render each element in the form + $w->element_container_class('FormElementContainer'); + + # Get authors + my @authorObjs = $c->model("MyAppDB::Author")->all(); + my @authors = map {$_->id => $_->last_name } + sort {$a->last_name cmp $b->last_name} @authorObjs; + + # Create the form feilds + $w->element('Textfield', 'title' )->label('Title')->size(60); + $w->element('Textfield', 'rating' )->label('Rating')->size(1); + # Convert to multi-select list + $w->element('Select', 'authors')->label('Authors') + ->options(@authors)->multiple(1)->size(3); + $w->element('Submit', 'submit' )->value('submit'); + + # Set constraints + $w->constraint(All => qw/title rating authors/) + ->message('Required. '); + $w->constraint(Integer => qw/rating/) + ->message('Must be an integer. '); + $w->constraint(Range => qw/rating/)->min(1)->max(5) + ->message('Must be a number between 1 and 5. '); + $w->constraint(Length => qw/title/)->min(5)->max(50) + ->message('Must be between 5 and 50 characters. '); + + # Set filters + for my $column (qw/title rating authors/) { + $w->filter( HTMLEscape => $column ); + $w->filter( TrimEdges => $column ); + } + + # Return the widget + return $w; + } + +The two new lines are marked with C<***New:>. + + +=head2 Update the TT Template + +Open C and edit it to match: + + [% META title = 'Create/Update Book' %] + + [%# Comment out the auto-rendered form %] + [%# widget_result.as_xml %] + + + [%# Iterate over the form elements and display each -%] +
+ + [% FOREACH element = widget_result.elements %] + + + + + [% END %] +
+ [% element.label.as_text %] + + [% element.element_xml %] + + [% element.error_xml %] + +
+
+ + +

Return to book list

+ + + [%# A little JavaScript to move the cursor to the first field %] + + +This represents three changes: + +=over 4 + +=item * + +The existing C has been commented out. + +=item * + +It loops through each form element, displaying the field name in the +first table cell along with the form element and validation errors in +the second field. + +=item * + +JavaScript to position the user's cursor in the first field of the form. + +=back + + +=head2 Try Out the Form + +Press C to kill the previous server instance (if it's still +running) and restart it: + + $ script/myapp_server.pl + +Try adding a book that validate. Return to the book list and the book +you added should be visible. =head1 AUTHOR @@ -562,7 +747,7 @@ between tables. Kennedy Clark, C Please report any errors, issues or suggestions to the author. The -most recent version of the Catlayst Tutorial can be found at +most recent version of the Catalyst Tutorial can be found at L. Copyright 2006, Kennedy Clark, under Creative Commons License