dimanche 28 juin 2015

How to display error messages in a multi-model form with transaction?

Two models, Organization and User, have a 1:many relationship. I have a combined signup form where an organization plus a user for that organization get signed up.

The problem I'm experiencing is: When submitting invalid information for the user, it renders the form again, as it should, but the error messages (such as "username can't be blank") are not displayed. The form does work when valid information is submitted and it does display error messages for organization, just not for user.

How should I adjust the code below so that also the error messages for user get displayed?

def new
  @organization = Organization.new
  @user = @organization.users.build
end

def create
  @organization = Organization.new(new_params.except(:users_attributes))
  #Validations require the organization to be saved before user, as user requires an organization_id. That's why users_attributs are above excluded and why below it's managed in a transaction that rollbacks if either organization or user is invalid. This works as desired.

  @organization.transaction do
    if @organization.valid?
        @organization.save
        begin
          # Execute next line in debugger (with invalid user info) correctly responds with: ActiveRecord::RecordInvalid Exception: Validation failed: Email can't be blank, Email is invalid, Username can't be blank, etc.
          @organization.users.create!(users_attributes)
        rescue
          # Should I perhaps add some line here that adds the users errors to the memory?
          raise ActiveRecord::Rollback
        end
     end
  end

  if @organization.persisted?
    flash[:success] = "Yeah!"
    redirect_to root_url
  else
    @user = @organization.users.build(users_attributes) # Otherwise the filled in information for user is gone (fields for user are then empty)
    render :new
  end

end

The form view includes:

<%= form_for @organization, url: next_url do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <%= f.text_field :name %>
        # Other fields

    <%= f.fields_for :users do |p| %>
        <%= p.email_field :email %>
            # Other fields
    <% end %>

    <%= f.submit "Submit" %>
<% end %>

The error messages partial is as follows:

<% object.errors.full_messages.each do |msg| %>
  <li><%= msg.html_safe %></li>
<% end %>

Update: Following the steps from Rob below I arrived at an errors partial below. This still does not display error messages for User. I added debugger responses inside the code below and for some reason nested_model.errors.any? returns false, while the debugger inside the controller (see above) does return error messages for user.

<% if object.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(object.errors.count, "error") %>.
    </div>

    <ul>
      <% object.errors.full_messages.each do |msg| %>
        <li><%= msg.html_safe %></li>
      <% end %>
    </ul>

  </div>
<% end %>

<% if defined?(nested_models) && nested_models.any? %>
  # Debugger: responds with "local-variable" for "defined?(nested_models)" and for "nested_models.any?" returns true.
  <div id="error_explanation">
    <ul>
      <% nested_models.each do |nested_model| %>
      # Debugger: "nested_model" has the same values as "nested_models.any?", as you would expect. But for "nested_model.errors.any?" it returns false, which it shouldn't.
        <% if nested_model.errors.any? %>    #Initially had "unless nested_model.valid?" but then errors for User are immediately displayed on loading the form page (new method).
          <ul>
            <% nested_model.errors.full_messages.each do |msg| %>
              <li><%= msg.html_safe %></li>
            <% end %>
          </ul>
        <% end %>
      <% end %>
    </ul>
  </div>
<% end %>

Aucun commentaire:

Enregistrer un commentaire