Interesting question (possibly)

Hello,

I have a form that displays information about a piece of equipment.
Among it’s attributes is its site and building. The trick is that a
site can have many buildings. Instead of storing both the site and the
building in the model, I just store the building because I can always
get the site from the building. If I store both of them in the model,
I would have to validate on each save if the combination is valid. And
I don’t want to go there.

So, a piece of equipment belongs to a building (through building_id)
and a building belongs to a site (through site_id).

Now, on the form I have two dependent selects. One for the building,
which depends on the selected value of the site, and one for the site
itself.

When the form is submitted, I need to remove site_id from the params
hash because otherwise I would get an error from ActiveRecord that
equipment.site_id is not a valid method (which would be correct) So,
that’s easy.

Here’s the problem. When I select the site and don’t select a building
my model validation sees that building_id is missing and reloads the
form with an error. Great! BUT, I lose the previous selection for the
site because it’s not in the model.

What do I do to maintain the “view state” of something that is not in
the model?

Might this solve your answer?

<%= select_tag :foo, options_for_select([‘A’, ‘B’], params[:foo]) %>

or something like this

<% fields_for :equipment do |f| %>
<%= f.select :foo, [‘A’, ‘B’] %>
<% end %>

Could you post your code?

Thanks for the reply! Hmm, I don’t see how that would help. See, I’m
losing site_id after the equipment model has determined that it can’t
save the data from the form. It returns the equipment object and by
that time the site_id is gone. It seems like I need to store it in a
session variable or something. Or did I misunderstand your solution?

I would just set a cookie :-/

cookies[:site_id] = {:value => params[:site_id], :expires =>
5.minutes.from_now}

Have that in whatever action receives the post data. Then set the form
field to set itself to that data in that cookie if the cookie exists.
After setting the cookie, remove the site_id from the params hash.

But I’m imagining, you shouldn’t -have- to remove site_id from the
params hash. If you’re doing something like, equipment =
Equipment.new(params[:equipment]) in your controller, then just make
sure in your form that site_id uses <%= select_tag(‘site_id’) %> instead
of <%= select(‘equipment’, ‘site_id’) %> and is not part of the
params[:equipment] hash.

I like the cookie idea. The reason why I’m removing site_id from the
params hash is because, you’re right, I’m generating the select using
the equipment model like so: <%= select(:equipment, :site_id,
Site.list) %>

But since site_id is not part of the equipment model, I defined a
method in that model that returns site_id through the relationship of
“equipment” with “building”. And the side effect is of course that
site_id is now part of the params. I went with this approach because
it was so easy. But if I go with the cookie solution, I’m going to
have to use select_tag and then, for sure, there’s no need for site_id
to be part of the params for the equipment model.

The way I understand the view state is maintained is as follows. When
a form representing a model is submitted and the validation fails, the
model object is returned to the view. It would be nice to be able to
modify that object before it’s passed back to the view.

Hope this makes sense.

Thanks for your suggestion!

On Jun 23, 11:59 pm, “R. Elliott M.” <rails-mailing-l…@andreas-

If it was me, I’d just have:

attr_accessor :site_id

in your equipment model, it’ll save you having to remove site_id from
the equipment params hash, and it should have the site still selected
when the form is reloaded on error. Its a bit hacky but its simple and
should work.

Hi David,

I just tried it. Unfortunately, It didn’t work. If site_id is not part
of the equipment model, how would “attr_accessor :site_id” help? I am
very curious about what you had in mind. Maybe I can use it after all.
Let me know when you have a minute.

Thanks,
Sergei

On Jun 24, 4:15 pm, David F. <rails-mailing-l…@andreas-

Ringo, thanks for your help. I think the solution is to use either a
session variable or a cookie. Because site_id is not part of the
model, I don’t think there’s a way around sessions or cookies.

surge wrote:

Hi David,

I just tried it. Unfortunately, It didn’t work. If site_id is not part
of the equipment model, how would “attr_accessor :site_id” help? I am
very curious about what you had in mind. Maybe I can use it after all.
Let me know when you have a minute.

Thanks,
Sergei

On Jun 24, 4:15 pm, David F. <rails-mailing-l…@andreas-

Did you still include the site_id field in the params hash you passed to
the model? Or do you still have the code that removes it?

When you add “attr_accessor :site_id” it should add a “site_id=” and a
“site_id” method, so therefore, when you pass site_id in the params, it
will assign the value to an instance variable, which is returned by the
“site_id” method, so also when you display the form, it should show the
value that you selected in the site_id select box.

Hi Dave,

I understand how your solution would work now. Let me play with it a
little bit longer to see if I forgot something.

Answering your question though, yes, I did remove the line of code
that removed site_id from the hash.

Thank you for the follow-up!

All right, I’ve done more testing with attr_accessor :site_id. My
latest finding is that site_id IS returned upon an error in the
validation, BUT the site select doesn’t restore the selection. I have
my site select defined as follows:

<%= select(:equipment, :site_id, Site.list(session[:customer_id]),
{ :include_blank => ‘true’ }) %>

I have no idea how the selection is not maintained if
equipment.site_id does contain the value that I submitted.

Also, I forgot to mention that I WOULD like to have site_id as a model
method for equipment. I have it defined as:

def site_id
if self.building
return self.building.site.id
else
nil
end
end

So, instead of attr_acessor :site_id, I decided to go with
attr_writer :site_id. But here’s the interesting part, if I do it that
way, I don’t get the site_id back upon an error. But I do get it back
when I use attr_accessor. I don’t understand it at all.

Any ideas? This is not such a big deal, but I’m very curious how to do
it elegantly.

On 6/28/07, surge [email protected] wrote:

I have no idea how the selection is not maintained if
end
end

So, instead of attr_acessor :site_id, I decided to go with
attr_writer :site_id. But here’s the interesting part, if I do it that
way, I don’t get the site_id back upon an error. But I do get it back
when I use attr_accessor. I don’t understand it at all.

Any ideas? This is not such a big deal, but I’m very curious how to do
it elegantly.

Do you mean that when your page is loaded the correct piece of equipment
is
not selected in your select box?

<%= select(:equipment, :site_id, Site.list(session[:customer_id]),
{ :include_blank => ‘true’, :selected => @equipment.site_id }) %>

Would this help?
Daniel

So, instead of attr_acessor :site_id, I decided to go with
attr_writer :site_id. But here’s the interesting part, if I do it that
way, I don’t get the site_id back upon an error. But I do get it back
when I use attr_accessor. I don’t understand it at all.

Any ideas? This is not such a big deal, but I’m very curious how to do
it elegantly.

Probably because your site_id method will only return a value if the
equipment has a building_id. Try this instead:

def site_id
if self.building
return self.building.site.id
else
return @site_id
end
end

Do you mean that when your page is loaded the correct piece of equipment is
not selected in your select box?

Yep. But it only happens when the page reloads after a validation
error.

<%= select(:equipment, :site_id, Site.list(session[:customer_id]),
{ :include_blank => ‘true’, :selected => @equipment.site_id }) %>

I so hoped it would! Unfortunately, it didn’t. Also, I don’t see
“:selected” as a valid option that can be passed to “select()”. At
least not in the API docs.

Thanks for the try anyway!

But the select control still doesn’t restore the selection after a
reload. I probably should look inside the code for select – unless
you can think of something really obvious.

Thanks for the post. I do appreciate it.

The only thing I can think of is that the function you are using to
produce the list values, Site.list(session[:customer_id]), is returning
the site.id as a string or something, so when the select list is
rendered, it does not match with the equipment.id?

Probably because your site_id method will only return a value if the
equipment has a building_id. Try this instead:

def site_id
if self.building
return self.building.site.id
else
return @site_id
end
end

You’re absolutely right and I like that change a lot :slight_smile:

But the select control still doesn’t restore the selection after a
reload. I probably should look inside the code for select – unless
you can think of something really obvious.

Thanks for the post. I do appreciate it.

David F. wrote:

But the select control still doesn’t restore the selection after a
reload. I probably should look inside the code for select – unless
you can think of something really obvious.

Thanks for the post. I do appreciate it.

The only thing I can think of is that the function you are using to
produce the list values, Site.list(session[:customer_id]), is returning
the site.id as a string or something, so when the select list is
rendered, it does not match with the equipment.id?

I meant to say equipment.site_id - which would be integer, and therefore
would only match another integer.

From the docs

http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelp

Oops… I stand corrected!

On 6/28/07, surge [email protected] wrote:

{ :include_blank => ‘true’, :selected => @equipment.site_id }) %>

I so hoped it would! Unfortunately, it didn’t. Also, I don’t see
“:selected” as a valid option that can be passed to “select()”. At
least not in the API docs.

Thanks for the try anyway!

From the docs

By default, post.person_id is the selected option. Specify :selected =>
value to use a different selection or :selected => nil to leave all
options
unselected.