6 Feb
duccio

duccio il 6 February 2009 parla di Rails Snippet

Rails: form per creare/aggiornare più modelli

Probabilmente avrete già usato il metodo fields_for dei form per consentire la gestione di modelli correlati sia nelle relazione one-to-one sia nelle one-to-many.

    1 <% form_for @person, :url => { :action => "update" } do |person_form| %>
    2  ...
    3  <% person_form.fields_for :address do |address_fields| %>
    4    Street  : <%= address_fields.text_field :street %>
    5    Zip code: <%= address_fields.text_field :zip_code %>
    6  <% end %>
    7 <% end %>

E’ comodo perchè come al solito fa tutto da sé!!

Nei nostri progetti usiamo un builder per i form, per una migliore gestione degli errori, degli hint, e delle label; per semplificare la sintassi nelle viste, senza usare il fields_for, ho fatto una piccola modifica al builder specificando l’object per il quale deve essere creato l’input field. In questo esempio c’è uno user con un associazione one-to-one al billing (che contiene le informazioni per la fatturazione), spero che il codice chiarisca il concetto:

    1 class LabelledFormBuilder < ActionView::Helpers::FormBuilder
    2     include ActionView::Helpers::ActiveRecordHelper
    3     include ActionView::Helpers::TagHelper
    4     @src = ''
    5     (field_helpers).each do |selector|
    6       @src = @src + <<-END_SRC
    7       def #{selector}(field, options = {})
    8         ....
    9         
   10         derived = options[:object]
   11         options.delete(:object)
   12         if derived
   13           @object_name = derived.class.name.downcase
   14           @object = derived 
   15         end
   16         
   17         ... Altri controlli sui parametri passati al builder
   18         ...
   19         ...
   20 
   21         (content_tag :div, (label_name ? 
   22         @template.content_tag("label" , label_name, :for => (object.class.to_s.downcase + '_' + field.to_s)) : '') + 
   23         super + other +
   24         error_message_on(:object, field, (label_error) + " ") + hint, :class => "fl") + (help ? content_tag(:div, content_tag("p", help), :class => 'fr') : '')
   25         
   26       end
   27       END_SRC
   28    #   class_eval src, __FILE__, __LINE__
   29     end
   30 end

La parte che c’interessa è questa:

    1 derived = options[:object]
    2 options.delete(:object)
    3 if derived
    4   @object_name = derived.class.name.downcase
    5   @object = derived 
    6 end

Ridefinendo l’object_name e l’object creo un input per il modello correlato, che si comporterà come se aveste usato il fields_for:

    1 <% labelled_form_for ["admin", form], :method => :post, :html => {:id => "form-insert"} do |f| 
    2   billing = form.billing %>
    3   <h1>Dati personali</h1>
    4   <%= f.text_field :email, :label_name => 'Email' %>
    5   <%= f.text_field :firstname, :label_name => 'Nome' %>
    6   <%= f.text_field :lastname, :label_name => 'Cognome' %>
    7   <% if billing %>
    8   <h1>Dati fiscali</h1>
    9   <%= f.text_field :firstname, :object => billing, :label_name => 'Nome' %>
   10   <%= f.text_field :lastname, :object => billing, :label_name => 'Cognome' %>
   11   <%= f.text_field :address, :object => billing, :label_name => 'Indirizzo' %>
   12   <% end %>
   13 <% end %>

Otterrete un html di questo genere:

    1 <div class="form_labelled"><form action="/admin/users/1" class="edit_user" id="form-insert" method="post">
    2   <h1>Dati personali</h1>
    3   <div class="fl"><label for="user_email">Email</label><input class=" text" id="user_email" name="user[email]" size="30" type="text" value="dante@extendi.it" /></div>
    4   <div class="fl"><label for="user_firstname">Nome</label><input class=" text" id="user_firstname" name="user[firstname]" size="30" type="text" value="Dante" /></div>
    5   <div class="fl"><label for="user_lastname">Cognome</label><input class=" text" id="user_lastname" name="user[lastname]" size="30" type="text" value="Alighieri" /></div>
    6   <h1>Dati fiscali</h1>
    7   <div class="fl"><label for="billing_firstname">Nome</label><input class=" text" id="billing_firstname" name="billing[firstname]" size="30" type="text" value="Dante" /></div>
    8   <div class="fl"><label for="billing_lastname">Cognome</label><input class=" text" id="billing_lastname" name="billing[lastname]" size="30" type="text" value="Alighieri" /></div>
    9   <div class="fl"><label for="billing_address">Indirizzo</label><input class=" text" id="billing_address" name="billing[address]" size="30" type="text" value="P.za Santa Croce, 33" /></div>
   10 </form>
   11 </div>

Quindi nel method post avrete i params dello user e del suo relativo billing.

3 Commenti a “Rails: form per creare/aggiornare più modelli”

  1. Tex il 9 February 2009 alle 08:35 dice:

    Grande Duccio !

    Complimenti per il magnifico post !!!

  2. duccio il 9 February 2009 alle 11:46 dice:

    Grazie!!!

  3. SouthWind92 il 23 October 2009 alle 01:07 dice:

    A related ambiguity is questions which have the form of yes-no questions, but which are intended not to be. ,

Scrivi un commento