Rails alternative to including active record call in view - ruby-on-rails

I have a form partial that looks like this:
<%= form_for(#pool) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :tournament %><br />
<%= f.collection_select :tournament_id, Tournament.active, :id, :name, :prompt => true %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This seems like a code smell because the view shouldn't be responsible for knowing how to get the data for the <select> tag. The alternative to have the controller assign an instance variable is problematic because I have to replicate that code in several actions depending on whether or not this form is rendered.
In ASP.NET MVC I'd just pull that field out into a partial view and display it with a call to RenderAction Which would evaluate a common controller action. However, in Rails render :action => '/view' seems to only allow full blown views to be rendered. I'm pretty new to Rails so I'm not sure about what the best practices are.

You can do a helper method as coder_tim suggests, but in my opinion that still leaves data access in the view.
The controller is the proper place for this and if you're worried about duplication, set up a before_filter that only acts on the actions that need this collection:
before_filter :get_active_tournaments, :only => [:new, :edit]
for example.
Hope this helps.

I like the smell of that code :) Simplicity over extremism. Less files = less methods to worry = cleaner code.
However, there are times when a dropdown is used too many times in your application, and it's a little more complicated than just calling a scope. In that cases, I write a helper.

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.

Rails render lookup value

First: Enter a certain emp_no using the textfield.
Second: I am trying to render/show the lookup value from a table through .
Here's the view:
<div class="field">
<%= f.label :emp_no %><br />
<%= f.text_field :emp_no%>
</div>
<div class="field">
<%= f.label :emp_name %><br />
<div><label><%= #emp_name %></label></div>
</div>
Here's my controller:
before_filter :customer
def customer
#emp_name = where(Employee.params[:employee_code])
end
Any inputs are much appreciated! Thank you!
We've achieved something similar at http://firststopcosmeticshop.co.uk (try the search at the top)
Search
What you're looking for, simply, is a piece of functionality which helps you search your data.
This can be achieved relatively simply, but you have to make sure your syntax is correct:
#config/routes.rb
resources :employees do
get :search
end
#app/controllers/employees_controller.rb
class EmployeesController < ApplicationController
layout: Proc.new {|controller| controller.request.xhr? ? false : "application" }
def search
#emp_names = Employee.where code: params[:employee_code]
end
end
#app/views/employees/search.html.erb
<% #emp_names.each do |employee| %>
<%= employee.name %>
<% end %>
This setup will provide you with the ability to load the search items if you wanted to show them, allowing you both the ability to use ajax (detailed below), or just plain HTML / HTTP
Ajax
If you wanted to use Ajax to deliver "real time" results, you'll want to use the following details:
#app/assets/javascripts/application.js
$(document).on("ajax:success", "form", function(status, data, xhr){
$(this).append(data);
});
This should render the results wherever you want on your front-end (requesting) page. The trick lies in the controller's assertion of the layout depending on the type of request received (HTTP or XHR)
Form
Finally, your form needs to reflect the functionality you wish to define. Currently, you don't have any way to send the request to your emp_names method, and so you need to create a better way to manage it:
#view
<%= form_tag employees_search_path, remote: true do %>
<%= text_field_tag :input %>
<%= submit_tag %>
<% end %>
Of course, the form can change considerably, but it basically means that you will send a simple request to your employees_search_path, which will then give you the capacity to manage the response in the controller; either normally, or with Ajax

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 - edit/new and show in same partial

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 %>

clever universal form partials in rails

I have a widget for currencies which I use throughout my application. Ie. the user changes the currency from EUR -> USD, or about 13 other currencies. I have a few other use-cases such as dates but this one is the easiest to explain. Currency basically just updates a session variable by calling the currency controller and then uses some JS to reload the page, but I'd like to only fetch certain elements of the page (ie that reflect the currency change and nothing else)...
$("#places_list").html("<%= escape_javascript(render :partial => 'places/list') %>");
or if another controller
$("#locations").html("<%= escape_javascript(render :partial => 'locations/places') %>");
but these elements are specific to the current controller, ie rendering a controller specific partial such as a list... (the currency math itself is in a an application helper, so no new logic is going on), and the currency controller is simple a partial loaded by different controllers. (Locations, Places, etc)
Other than making an action in every controller specific for this purpose, how can I make it behave in a way that I can render elements specific to the current controller, so I can replace them intelligently over js, instead of reloading? Ie. I can pass in to currency the current controller
<%= hidden_field_tag :info, controller.controller_name %>
I'm not sure if any of that makes sense, but I hope it does, at least in my own brain if not in anyone else's.
I have tried a little app and below would be a one solution for you,
first of all, in my case what I'm trying to do is to have an attribute called :info in each model , so that I can call it (<%= f.text_field :info %>) in my views (please note f can be any instance of a model)
for that I need to add :info attribute to each model. You have several ways of doing it
1 - create a class (which inherits from AR::Base and inherit all the other models from the given class) (but it requires some code changes)
2 - Quick and ugly way is to inject my :info attribute to AR::Base class (Which I did ;) )
create a file in config/initializers say curreny.rb and add this code
module ActiveRecord
class Base
attr_accessor :info
end
end
after that from any view which u uses AR, will have the :info attribute
Ex: users/new
New user
<% form_for(#user) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :info %><br />
<%= f.text_field :info %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', users_path %>
Please note: this is not a very good solution, but I think with this you can start thinking
cheers
sameera

Resources