Ruby on Rails - edit/new and show in same partial - ruby-on-rails

I have a relatively simple address partial that I use for 'new' and 'edit' forms for when an address is needed on an object. It works great. The problem is now that I want the same fields to show up on my 'show' views. The only problem is that my address partial requires a form which isn't present in a 'show' view. I would duplicate it, but that doesn't seem very DRY to have one partial for new/edit and another for show.
I was wondering if anyone has solved this problem. I have thought about using .new? and .persisted? but that doesn't really help because an object for editing will pass both of those in the same way a show object would.
Any help is greatly appreciated.
EDIT: Here is my address partial for new and edit. I would like to use the same partial as in the new,edit and show view, as recreating an entirely new partial doesn't seem very DRY. The issue I see is that this partial requires a form :f, which of course isn't available in a 'show' view.
<p>
<%= f.label :address_line_1, 'Address 1' %><br />
<%= f.text_field :address_line_1 %>
</p>
<p>
<%= f.label :address_line_2, 'Address 2' %><br />
<%= f.text_field :address_line_2 %>
</p>
<p>
<%= f.label :city %><br />
<%= f.text_field :city %>
</p>
<p class="address_state">
<%= f.label :state , "State" %><br />
<%= f.select :state, us_states, :include_blank => true %>
</p>
<p>
<%= f.label :zip_code, 'Zip Code' %><br />
<%= f.text_field :zip_code %>
</p>

I have the same concern, wanting to DRY out not just new/edit but show as well. With a lot of attributes on a model, it becomes a pain to maintain them in parallel across multiple views.
Poked around, could not find anything, except this (unresolved) post. And #Valery, isn't fields_for really meant for editing other models inside your form_for?
#FlexFiend, I ended up solving your problem this way (and admittedly, it's clunky, but it gets the job done): DO use a form also for the show view, and create a partial (shared by all actions) that calls helpers that test the controller action, and if "editable", then invoke the right control type. Otherwise else just render the attribute if it's a show action.
For example, the starting template (show, new, edit) simply invokes a form partial:
<%= render :partial => 'form' %>
...where _form is generic:
<%= form_for(#myObject) do |f| %>
<div>
<%= render :partial => "fields_for_all_actions", :locals => { :f => f } %>
</div
<% end %>
...and inside the partial, to either show or edit a 'name' attribute use a helper 'render_field':
<div><%= f.label :name %><span><%= render_field("#facility",f,"name", 30)%></span></div>
Inside the helper, render_field looks like this:
def render_field(object_name, f, attribute, txt_size=20)
if %w[new edit create update].include? controller.action_name
f.text_field(attribute.to_sym, :size => txt_size)
else
eval(object_name + "." + attribute)
end
end
I have created other helpers for other controls (text areas, radios, checkboxes, etc). These helpers are not model-specific, they live in application_helper.rb and can be used by multiple models.
It works, but I would be curious to know of other solutions to this problem, as the maintenance challenge of larger models is not insignificant. Granted, that argues perhaps for refactoring the model into component models, but that's another post.

Include a readonly attribute in your fields, something like:
<%= f.text_field :name, readonly: #readonly %>
then place #readonly = true in your show method.

<%= fields_for #object do |f| %>
<%= render 'your/fields_parial', :f => f %>
<% end %>

Related

Nested fields not being added on form submit

I am using the cocoon gem to try and achieve adding an object which belongs to another with nested fields. I have a 'user_resolution' which has many 'milestones'. I have set up the associations accordingly in both of these models. For some reason, milestones are failing to be created, however if I add one manually in the database I can successfully update it. I am able to dynamically add the fields and remove them using the cocoon gem but that is all. When I click 'add milestone' it redirects me to the show view of the user resolution and throws the success message saying user resolution has been updated, no errors are thrown but the milestone(s) is/are not created.
user_resolution.rb
has_many :milestones
accepts_nested_attributes_for :milestones, reject_if: :all_blank, allow_destroy: true
milestone.rb
belongs_to :user_resolution
I have set up the nested form within the edit view as for now I only want users to add a milestone to a resolution in the edit view.
user_resolutions/edit.html.erb
<%= form_for(#user_resolution) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<%= f.fields_for :milestones do |milestone| %>
<%= render 'milestone_fields', f: milestone %>
<% end %>
<%= link_to_add_association 'Add Milestone', f, :milestones %>
<%= f.submit "Add Milestone" %>
<% end %>
_milestone_fields.html.erb
<div class="nested-fields">
<div class="field-row">
<%= f.label :name, 'Name' %>
<%= f.text_field :name %>
</div>
<div class="field-row">
<%= f.label :description, 'Name' %>
<%= f.text_area :description %>
</div>
<div class="field-row">
<%= f.label :severity, 'severity' %>
<%= f.check_box :severity %>
</div>
<div class="field-row">
<%= f.label :target_date, 'target_date' %>
<%= f.date_select :target_date %>
</div>
<%= link_to_remove_association 'Remove', f %>
</div>
The permitted parameters within the user resolutions controller also contain the following
milestones_attributes: [:id, :user_resolution_id, :name, :description, :target_date, :severity, :complete, :_destroy]
The milestones themselves have no views, they only have a model and a controller. The controller create action (which i'm unsure is required for nested forms) contains the standard following code
def create
#milestone = Milestone.new(milestone_params)
if #milestone.save
redirect_to user_resolutions_path,
:flash => { :success => "You successfully created a milestone" }
else
redirect_to new_milestone_path,
:flash => { :error => "Oops something went wrong. Try again." }
end
end
I've been as informative as I can but if you need anything else let me know. Thanks guys.
which i'm unsure is required for nested forms
You don't need a create action for milestones - they'll be populated from the user_resolutions#create controller action.
There are several things to look at with this. I'll detail some here. This won't be a specific answer, but may help point you in the right direction.
Firstly, you need to make sure you're receiving the correct params.
Cocoon does a great job building the nested form - you need to make sure it's obliging Rails' nested attribute structure.
To do this, you should right-click > view source.
In the f.fields_for section (it won't be called that in the HTML), you'll be looking for the equivalent to the following:
<input type="text" name="milestones_attributes[0][name]" value="">
The important thing to note is the name...
Each time you use a form, or any Rails view helper for that matter, you're really just building standard HTML. form_for just creates an HTML form, and thus any params contained within it need to adhere to a certain structure for Rails to recognize the params.
The f.fields_for elements will typically be called x_attributes[:id][:param] - this is passed to Rails, which cycles through each [:id] to determine the number of nested params to add.
You need to check the source for the above naming structure. If you see it, that's good. If not, it means you haven't built your form properly.
Secondly, you need to make sure your objects are being built in the controller.
I'm not sure how Cocoon does this, but essentially, each time you use f.fields_for, you have to build the associated object before:
def new
#user_reservation = UserReservation.new
#user_reservation.milestones.build #-> this is what makes f.fields_for work
end
If the first step shows incorrect element naming, it means your associative objects are not being built (which is why they're not being recognized).
To test it, you should build the associative objects in the new method, before sending.
Finally, you'll want to post your params.
These tell you in explicit detail what Rails is doing with the nested attributes, allowing you to determine what's happening with them.
Sorry for the long-winded answer. You'll not have received any answers anyway, so I felt it prudent to give you something.

Is it possible to access a controller's instance variable in a partial if the partial is being rendered by a different controller's view?

Hi guys thanks in advance for the help. I am building a sort of online game using rails. The user interface is all supposed to be centered around one page: users/... I am running into an issue in creating and interacting with objects outside of users. For example, within the user page users are supposed to have the ability to create a fortress. The fortress of course is its own object with its own model and controller. If I were to try to put a form to create a fort using the form_for tag in the users page, the form wouldn't be able to access the fort controller's #fort instance variable and would throw an error. In my attempt to solve the problem I have created a partial view for forts called _new.html.erb containing the fort form
Here it is:
<h1>Create a New Fort</h1>
<%= form_for(#fort) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :ownership %>
<%= f.text_field :ownership %>
<div style="display:none;">
<%= f.label :xco %>
<%= f.text_field :xco %>
<%= f.label :yco %>
<%= f.text_field :yco %>
<%= f.label :territory %>
<%= f.password_field :territory %>
</div>
<%= f.submit "Create a Fort" %>
<% end %>
I then render that partial in my user show.html.erb here:
<%= render "forts/new" %>
Of course moving the form to a partial does nothing at all, but what I was hoping to do was something along the lines of this:
<%= render partial: "new", object: #fort %>
I would like to pass the fort instance variable into the partial then render the partial in the users view. I recognize however that the variable passed to the partial as described above comes directly from the view the partial is rendered in, and at that realization I am at a loss for ideas.
I have considered the key might be somewhere in associations and I have been crawling the internet for info on that. My models are set up so a user has_many forts and a fort belongs_to a user. I don't know whether or not that will help.
All help is appreciated,
Thanks loads,
Alex P
From your outer view, assuming #fort has been instantiated in your controller, you can pass #fort directly to your partial like this:
<%= render 'forts/new', :fort => #fort %>
I think what you are looking for is accepts_nested_attributes_for. This RailsCast explains how it is used.
In your case you would add accepts_nested_attributes_for :forts to your user model. I'm not sure what your user/show view looks like, but you should just be able to add a <%= f.fields_for :forts do |f| %> which would contain the fields currently in forts/new.
Yes, you can pass Locals with render statements wherever you call partial.
<%= render 'shared/error_messages', :locals => {:info => first, :img_style => "original"} %>

Ruby on Rails 3: How to pass a parameter to a select box in a form?

I have a Client model that can have many Projects.
These are my view files:
edit.html.erb
<%= form_for(#project) do |f| %>
<%= render 'fields', :f => f %>
<%= f.submit Create %>
<% end %>
_fields.html.erb
<div>
<%= f.label :name %><br/>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :client_id %><br/>
<%= f.select(:client_id, current_user.client_names) %>
</div>
This is easy and works great.
But is there a way to create a Project from within a Client view as well?
For example on the client edit page it would be nice to have a link New Project for this Client that leads to the above New Project form, but with the respective client preselected in the select box.
Can this be done somehow?
I can't seem to find a way to pass the Client ID to the New Project form.
Thanks for any help!
This is certainly possible, but requires passing the client id to the new_projects_path. This can be done by nesting the routes to projects inside of clients, or just appending the client id. The difference would be URLs that look like the following:
/clients/1/projects/new
- or -
/projects/new?client_id=1
In your projects controller, you should be able to instantiate the #project variable with a preselected client:
#project = Project.new :client_id => params[:client_id]
Yes, you can do this. You're going to use accepts_nested_attributes_for :project in your Client model. Nesting this will allow you to do something like
<%= f.fields_for :project do |p| %>
<fieldset>
<%= p.text_area :content %>
</fieldset>
<% end %>
Check out the railscasts episode #196 on Nested Model Form.

How to use one partial in different contexts

i am new to rails and try to do the following:
I would like, because i use Bootstrap, to have a partial for a input field, with it's label and a little icon i called symbol in this case.
Here is my view:
<%= form_for(#user, :class => "form-horizontal" ) do |f| %>
<fieldset>
<%= render 'shared/text_field', function: f, tag: :name, symbol: '<i class="icon-user"></i>' %>
<%= render 'shared/text_field', function: f, tag: :email, symbol: "#" %>
<%= render 'shared/password_field', function: f, tag: :password, symbol: '<i class="icon-log"></i>' %>
<%= render 'shared/password_field', function: f, tag: :password_confirmation, alt: "Passwort wiederholen", symbol: '<i class="icon-log"></i>' %>
<!-- SUBMIT -->
<%= f.submit "Anmeldung", :class => "btn btn-primary" %>
</fieldset>
<% end %>
Here a subpartial for normal input fields:
<%= render 'shared/bootstrap/input_field' %>
<% content_for :label do %>
<%= function.label tag, :class => "control-label", :for => "prependedInput" %>
<% end %>
<%content_for :symbol do %>
<%= raw(symbol) %>
<% end %>
<% content_for :field do %>
<%= function.text_field tag, :class => "input-xlarge", :id => "prependedInput", :size => "6" %>
<% end %>
(there is also a subpartial for password fields, basicly exchanging function.text_field with function.input_field)
And here is the input_field which is rendered:
<div class="control-group">
<%= yield :label %>
<div class="controls">
<div class="input-prepend">
<span class="add-on"><%= yield :symbol %> </span>
<%= yield :field %>
</div>
</div>
</div>
So my question is: how can i solve this Problem in a nice and easy way (this things happend while refactoring and it got even worse then better) and how can i make it work, because by now something like this happens: http://pastebin.com/bNsgT9AR so yield puts with each content_for the content and the content before into the place (except the last one)
It would be great to hear nice solutions from you, there has to be a so much nicer way as almost always in Rails.
Greetings form Germany ;)
As the author of The Rails View, I agree with Ben Taitelbaum's post on this and say that Formtastic is totally a great option for this kind of complexity.
With regards to the code you posted, as developers we want to avoid that kind of view writing across the board, as it ends up an unmanageable mess. There's too many files involved in editing a form, and at some point, some other dev will come in and overwrite our shared partial, not knowing where else it was used, to change the way a different form behaves. Any benefit of code reuse that we can get out of this kind of abstraction is completely obliterated by the potential for it to go sour very quickly with mutliple developers.
While Formtastic is great, I've also been using SimpleForm a lot in my recent apps, especially when I don't need the full power of Formtastic.
SimpleForm will handle your label, and it also supports i18n as well. With this it would be:
<%= simple_form_for #user do |f| %>
<%= f.input :name, :input_html => { :class => 'user-icon' } %>
<%= f.input :email %>
<%= f.input :password, :as => :password, :input_html => { :class => 'log-icon' } %>
<%= f.input :password_confirmation, :as => :password, :input_html => { :class => 'log-icon' } %>
<%= f.button :submit %>
<% end %>
Then in your I18n file, you can have:
en:
simple_form:
labels:
user:
username: 'User name'
password: 'Password'
password_confirmation: 'Password confirmation'
de:
simple_form:
labels:
user:
username: 'Benutzername'
password: 'Passwort'
password_confirmation: 'Passwort wiederholen'
And more as you need it. You can define your hints and placeholders in the i18n file as well.
Also, think about adding the icon as a CSS pseudo class :after, and do so for each class that you need to add in the code)
input.user-icon:after {
// image as background, positioned properly, etc etc
}
input.log-icon:after {
// image as background, positioned properly, etc etc
}
And that way, you can remove this kind of stuff from your ERB/HTML and put it where it belongs: In the CSS (presentation layer).
There's a great section on forms in The Rails View, where they recommend using FormBuilders for this type of thing (they also discuss formtastic, which is worth checking out).
The biggest lesson I got out of this book (and I can't recommend it enough!) is that it's important to keep views as simple as possible, since they tend to be the least fun part of a rails app to debug, especially as the app grows large. So from this perspective, I would want the view to be nice and simple, something like:
<%= semantic_form_for(#user) do |f| %>
<%= f.inputs do %>
<%= f.input :name, class: 'icon-user' %>
<%= f.input :email %>
<%= f.input :password %>
<%= f.input :password_confirmation %>
<% end %>
<%= f.buttons %>
<% end %>
Start with a nice readable view as the goal, and add the necessary helpers and formbuilders as needed for custom field types.
I'd recommend using I18n for setting the submit button text to be "Anmeldung" instead of hardcoding that as well.
I haven't used it, but you might want to check out formtastic-bootstrap for getting the integration you want..
I feel that's a bit too much generalization overhead for a simple input field.
Have you considered simply creating one shared input field partial and just passing in the field name (you named it tag) and the type of field (text_field or password_field) as parameters? I can see that your approach might be more general, but if you just need those two options for now, keep it as simple as possible.
Addition in reply to comment:
You can simply pass parameters to your partial like so:
<%= render 'shared/error_messages', object: f.object %>
And then use the the variables in your partial. in your case you could
<%= render 'shared/bootstrap/input_field', function: f, tag: :name, input_field_type: :text_field %>
and then in _input_field.html.erb
<div class="control-group">
<%= function.label tag, :class => "control-label", :for => "prependedInput" %>
...

Conditional form input field types

Rails 2.3.11
I would like to use a text_field to input data under one condition, otherwise using a selection box. Right now, my code looks like this:
views/posters/new.html.erb
<% form_for #poster, :html => {:multipart => true} do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :image %> - We're not going to enlarge it for you, so please upload the biggest copy you can!<br />
<%= f.file_field :image %><br />
</p>
<p>
<% if current_user.admin? && params[:event_id] && !current_user.events.find_by_id(params[:event_id]) && Event.find_by_id(params[:event_id]) %>
<%= f.label "Event ID" %><br />
<%= f.text_field :event_id, :value => params[:event_id] %>
<% else %>
<%= f.label :event_id %><br />
<%= f.select :event_id, #events, :selected => params[:event_id].to_i %>
<% end %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
controllers/posters_controller.rb
def new
#poster = Poster.new
current_user ||= User.find_by_id session[:user_id]
#events = [["Don't attach to an event", '']]
current_user.events.each {|event| #events << [event.title, event.id]}
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #poster }
end
end
Error message: http://cl1p.net/halp
How can I use two different types of form input fields for the same parameter (but each under a different condition, not simultaneously)?
Update: I think the problem stems from the issue of Rails putting the previously-submitted information back into their respective input fields. This explains why no tantrum is thrown when a file that passes all the validation tests (that is, a PNG less than 3 MB), but breaks down when nothing (or anything that doesn't meet that condition) is attached.
Firstly, I don't understand why you are manually setting the values on the text box and select box. Usually, Rails does this for you, but without the form definition, I can't tell if you really need to do this.
Even then, given the information, I think it's safe to say that what you need is this:
f.text_field :event_id
...
f.select :event_id, #events
This ought to work for what you intend to do. I'm not sure what it has to do with the submitting of a file, but yes you are right about the previously-submitted part. The unexpected nil stems from this:
params[:event_id].to_i
Unless you are setting this parameter entry manually inside your controller, you will not be able to cast it to an integer if it is nil. If you go with using the basic form helper calls this goes away.

Resources