Report hidden field error on visible field?

1326 views
Skip to first unread message

mcbsys

unread,
Oct 24, 2012, 10:05:09 PM10/24/12
to plataformate...@googlegroups.com
Hi,

I'm using Rails 3.2.8, simple_form 2.0.2, and bootstrap-sass 2.0.3.1. I'm having some trouble implementing a typeahead field with rails3-jquery-autocomplete 1.0.9.

"All" I want to do is replace a <select> with a typeahead.

In my model I have

  validates :to_contact_id,   :presence => true

The old, working simple_form select looks like this:

    <%= f.association :to_contact, :collection => @to_contacts, :required => true,
                  :value_method => :id, :label_method => :display_name %>

If the user does not select a value, the field is nicely wrapped in red and an error message appears below the field.

It seems that the type-ahead needs a hidden field to store the id that it returns. Which apparently means I can't use the simple_form integration; I'm down to coding the bootstrap wrapper divs, autocomplete_field_tag, and field values manually, e.g.

  <div class="control-group string required">
    <label class="string required control-label" for="message_to_contact">
      <abbr title="required">*</abbr>
      To contact
    </label>
    <div class="controls">
      <% to_contact_name = @message.to_contact ?
            @message.to_contact.display_name : '' %>
      <%= autocomplete_field_tag "to_contact_name", '',
                     autocomplete_contact_display_name_messages_path,
                     :required => true,
                     :id_element => "#to_contact_id",
                     :value => to_contact_name %>
      <%= f.hidden_field :to_contact_id, :id => "to_contact_id" %>
      <%= f.full_error :to_contact_id %>
    </div>
  </div>

The main issue I'm facing is reporting validation errors. Using simple_form's full_error after the hidden field kind of works, but it is not consistent with other errors:

- The error text appears next to the field rather than below it
- The error text is black instead of red
- The field label is black instead of red
- The field in error (to_contact_name) does not have a red border
- The name of the failing field is included

I can see from the HTML that the problem is that Bootstrap expects an "error" class on the outer control-group div.

What I really need is a way to detect (validate) an error on to_contact_id, but report the error on to_contact_name. Can this be done? Or is there a better way to implement autocomplete?

Thanks as always for your help,

Mark

mcbsys

unread,
Oct 25, 2012, 7:08:54 PM10/25/12
to plataformate...@googlegroups.com
I found a workaround by manually handling the errors array. It's a lot of code for one form field:

  <% to_contact_error_class =
       @message.errors.messages[:to_contact_id] ? "error" : "" %>
  <div class="control-group string required <%= to_contact_error_class %>">
    <label class="string required control-label" for="message_to_constact">

      <abbr title="required">*</abbr>
      To contact
    </label>
    <div class="controls">
      <% to_contact_name = @message.to_contact ?
            @message.to_contact.display_name : '' %>
      <%= autocomplete_field_tag "to_contact_name", '',
            autocomplete_contact_display_name_messages_path,
            :required => true,
            :id_element => "#to_contact_id",
            :class => "input-large",

            :value => to_contact_name %>
      <%= f.hidden_field :to_contact_id, :id => "to_contact_id" %>
      <span class="help-inline">
        <%= @message.errors.messages[:to_contact_id].join(", ") %>
      </span>
    </div>
  </div>

mcbsys

unread,
Oct 25, 2012, 7:21:56 PM10/25/12
to plataformate...@googlegroups.com
Oops, to avoid nil object errors on .join, the help-inline <span> should only appear if there is an error:

      <% if @message.errors.messages[:to_contact_id] %>

        <span class="help-inline">
          <%= @message.errors.messages[:to_contact_id].join(", ") %>
        </span>
      <% end %>

Carlos Antonio da Silva

unread,
Oct 28, 2012, 11:48:17 AM10/28/12
to plataformate...@googlegroups.com
I think you can still use SimpleForm "niceties" to generate all the wrapping output for you, using the block input form:

    <%= f.input :to_contact, :required => true do %>
      <% to_contact_name = @message.to_contact ? 
            @message.to_contact.display_name : '' %>
      <%= autocomplete_field_tag "to_contact_name", '',
                     autocomplete_contact_display_name_messages_path,
                     :required => true,
                     :id_element => "#to_contact_id",
                     :value => to_contact_name %>
      <%= f.hidden_field :to_contact_id, :id => "to_contact_id" %>
    <% end %>

That'd give you the wrapping, label, and error logic that SimpleForm already handles, and would allow you to customize the way the input is generated, by creating the autocomplete you need.

Please give it a try and tweak as necessary. Hope that helps.
--
At.
Carlos Antonio

mcbsys

unread,
Oct 30, 2012, 3:38:03 PM10/30/12
to plataformate...@googlegroups.com
Thanks very much, I think that will work. Let me just confirm one thing. After our discussion here:

https://groups.google.com/d/topic/plataformatec-simpleform/ASEVn--NBZw/discussion

I had set up the model to validate :to_contact_id, :presence => true, and I was using f.association in the form.

It seems that f.association does not fully handle block input (no label, for example). So I am back to using f.input as per your example.

Is it sufficient, then, to simply change the model to validate :to_contact, :presence => true? It seems to work; I just want to confirm that it's okay to drop the explicit validation on to_contact_id.

Mark

Carlos Antonio da Silva

unread,
Oct 30, 2012, 5:53:48 PM10/30/12
to plataformate...@googlegroups.com
Yes, f.association with a block works just like f.simple_fields_for, so you can't use the input block version. You can either change to validate :to_contact, the only issue is that this will always load your object to validate if it exists, sometimes causing an extra database query.

Another possibility is to manually add the error lookup inside the block, using something like "f.error :to_contact_id".
--
At.
Carlos Antonio

mcbsys

unread,
Oct 30, 2012, 6:45:29 PM10/30/12
to plataformate...@googlegroups.com
Thanks. I tried f.error but it seems I'm back to my original issue:  the message is correct, but because it applies to to_contact_id, the outer div for to_contact doesn't get the "error" class, so nothing is red.

Carlos Antonio da Silva

unread,
Oct 30, 2012, 8:08:45 PM10/30/12
to plataformate...@googlegroups.com
Ah, that's correct. In this case you have to validate the same attribute I guess :).
--
At.
Carlos Antonio

mcbsys

unread,
Oct 31, 2012, 3:39:54 PM10/31/12
to plataformate...@googlegroups.com
Carlos, thanks as always for your help.

Blogged the entire process here:

Convert a Select Drop-Down Box to an Autocomplete in Rails

mcbsys

unread,
Oct 31, 2012, 11:12:15 PM10/31/12
to plataformate...@googlegroups.com
Wow, testing the autocomplete with rspec and capybara is turning out to be quite a challenge. Any tips appreciated. Capybara thread:

https://groups.google.com/d/topic/ruby-capybara/wX03JWbW01c/discussion

Mark

mcbsys

unread,
Nov 3, 2012, 4:42:00 PM11/3/12
to plataformate...@googlegroups.com
I got autocomplete testing to work with the Selenium driver, but not webkit. Details:

http://stackoverflow.com/a/13213185/550712

Mark
Message has been deleted
Message has been deleted

Vojtěch Kusý

unread,
Feb 3, 2013, 6:48:14 AM2/3/13
to plataformate...@googlegroups.com
I've similar issue and found better solution IMHO.

In your model:

validates :to_contact_id,   :presence => true
after_validation :validate_contact

protected

def validate_contact
  if errors[:to_contact_id].present?
    errors[:to_contact_id].each { |message| errors.add :to_contact, message }
  end
end

This way errors on to_contact_id are just copied to the right form element, to_contact in your case, so you can define the simple form in a usual way without any blocks magic and as a benefit there is no additional DB query.

mcbsys

unread,
Feb 5, 2013, 1:08:55 PM2/5/13
to plataformate...@googlegroups.com
Thanks Vojtěch. I've also updated your reply on my blog to match this reply.

mcbsys

unread,
Sep 17, 2013, 9:22:02 PM9/17/13
to plataformate...@googlegroups.com
I had another situation where I needed to display a message on the to_contact field when another field (to_email) is blank.

I could validate the presence of to_email, but for some reason, adding to_email as a hidden field in the to_contact block didn't cause the message to display on to_contact.

Finally solved it by adding this to the model:

  validate :to_email_required
 
  def to_email_required
    if to_email.blank?
      # Add error to to_contact so it will display next to that field
      errors.add(:to_contact, "To contact must have Email address")
    end
  end
Reply all
Reply to author
Forward
0 new messages