Draper with form_for in method - ruby-on-rails

I am trying to create a method in a draper decorator that will spit out a form_for. I have a form that i'm using for searching records on the index view and have dozens of resources, so I really want to maintain this logic in one place if possible (application decorator). My problem is that I have no idea how to render a form_for in the decorator. I've come across some uses of concat and capture to try and accomplish this, with no luck. All i've been able to get is a couple of my divs to display (not as html either, just as plain text). Any ideas on how I can do this? This is the code i've got:
def crud_index_search(search_obj)
h.concat "<div id='basic_search' style='display:none;'>"
search_form_for search_obj do |f|
h.concat '<div class="input-append">'
f.text_field :name_or_description_cont, :placeholder => 'Quick search ...', :id => 'search'
h.concat "<button class='btn'><i class='icon-search'></i></button>"
h.concat '</div>'
link_to 'Advanced Search', '#', :id => 'advanced_search_btn', :class => 'pull-right'
end
h.concat '</div>'
h.concat "<div id='advanced_search' style='display:none;'>"
search_form_for search_obj do |f|
f.condition_fields do |c|
h.concat render "crud/partials/condition_fields", :f => c
end
h.concat "<p>#{ link_to_add_fields 'Add Conditions', f, :condition }</p>"
f.submit 'Search'
h.concat "#{link_to 'Basic Search', '#', :id => 'basic_search_btn', :class => 'pull-right'}"
end
h.concat '</div>'
end
and in my view...
<%= #categories.crud_index_search #search %>
Any help would be greatly appreciated!
FYI, I've already put this into a partial and that works, however there is some more complex logic I will need to add to this that will make it differ on a per resource basis, so a partial is not ideal for me.
Thanks

Draper is excellent for putting logic pertaining to the view and does well at simple presentation, however you should in principle be leaving the content presentation to the view since that is what it is there for.
A pattern that I recommend is using the method on your Draper object to facilitate the logic and for each logic path render an appropriate partial.
So you might have something like this:
def crud_search_index(search_object)
if search_object.something_to_check
h.render 'shared/one_version_of_my_form'
else
h.render 'shared/another_version_of_my_form'
end
end
And, even better, since your form is directly related to that search_object, I would actually create a Draper Decorator for that search object and put the method to generate the form on there instead of an "application" decorator.
Then your view is something like:
<%= #search.form %>
And if you need a reference to the #categories then pass it in to that method.

Related

Rails Routing and link_to

It's a little late here so maybe this a trivial question where I'm missing something simple. But when I click a button (with link_to) I created the following gets appended to my URL:
%23<ActionView::Helpers::FormBuilder:0x3ef1fd8>
Why is this, and how can I prevent this? Again, I apologize if this is a shallow question. I can post more information regarding routes and whatnot if that is needed.
Thanks!
Edit: More information as requested.
View:
<%= link_to "Index", welcome_path(f), :class => 'button' %>
with f being part of a form_for loop. I think I'm passing the wrong parameter but I'm unsure.
Relevant Route:
get "index" => 'welcome#show', :as => 'index'
Update:
Thanks for the help everyone. I ended up getting it working by pluralizing my controller (I don't know why I didn't have that before) and utilizing welcome_url instead. That seemed to do the trick.
Check out the very first example and paragraph in the Rails API docs for ActionView::Helpers::FormBuilder:
<%= form_for #person do |f| %>
Name: <%= f.text_field :name %>
Admin: <%= f.check_box :admin %>
<% end %>
What this is saying is that f represents an instantiated FormBuilder object that you are passing to the welcome_path method in your link_to helper.
Typically, you would not mix #index and #show in your routes. Depending on what you want to use the WelcomesController for, you might actually want to route your root_path to welcome_index:
get "welcome/show" => 'welcome#show', :as => 'welcome'
root 'welcome#index'
You should run: $ rake routes in the terminal to get an idea of path view helpers that you can use in your app.
Maybe you're trying to send users to a personalized welcome page. You could have something like this for your corresponding link_to helpers would look best like this:
<%= link_to "Show", welcome_path(#user.id), :class => 'button %>
<%= link_to "Index", root_path, :class => 'button' %>

Rails: Nested form with STI inheritance informing forms, how to make work with AJAX?

Okay, first and foremost, I am doing something very complicated. It's possible I've gone in a wrong direction.
What I currently have is a STI inheritance model, StateDescription. Subclasses of StateDescription describe a specific state I could care about, such as "LocationHasItem" or "ItemNearOtherItem" or what not.
I understand that I may eventually want to upgrade this to a multi-table inheritance model, as not all subclasses of StateDescription use all possible variables (though there is a high degree of overlap).
These StateDescriptions are owned by another class in a "has_many/belongs_to" relationship.
So, inside the form for this other class, I have:
<%= f.fields_for :conditions do |e| %>
<br>
<%= render :partial =>"/state_descriptions/form", :locals => {:e => e, :universe => #story_section.universe, :div_id => "condition"}%>
<Br>
<% end %>
The StateDescription itself checks for which type it should render, then renders the appropriate partial like so (only showing one subclass, for clarity's sake):
<div id="<%=div_id%><%=e.object.id%>">
<li>
<%= e.select(:type, StateDescription.subclasses.collect{|x| x.to_s}) %>
<br>
<%= e.label "Inverted?" %>
<%= e.check_box :invert %>
<Br>
<% if e.object.type.to_s == "StateDescription::ItNear" %>
<%= render :partial =>"/state_description/it_nears/form", :locals => {:e => e, :universe => universe, :div_id => div_id}%>
<% end %>
<% end %>
</li>
</div>
The subclass partial looks like:
<%=e.collection_select 'item_id', universe.items, :id, :title%>
<%= e.object.title_middle_fragment%>
<%=e.collection_select 'item2_id',universe.items, :id, :title%>
Thus the form starts out the same for all subclasses, and only differs as required.
This works PERFECTLY and I was very happy with it...until I decided I wanted to have ajax update the webpage with the correct form when the sublclass is chosen out of select input field.
Then I realized I couldn't pass the form helper reference ("e" in this case) to the partial from a controller.
IS there a best practice for this case, or I am just doing something so complicated I should do straight jQuery or something and leave rails out of it?
If I put the entire form (including the things each subclass has in common) in each subclasses form, that doesn't seem very dry. Not only that, I'm not sure that I would be able to then associate the StateDescription subclass back up to the parent class...
But, if I do it AJAX, I suppose I could not worry about nested forms and just have the parent be a hidden field or something, and have the StateDescriptions save on their own through AJAX?
Would this be the best solution (if it even works?) or is there some simple Rails way that I am missing?
Well, I still don't know if this is the easiest, or DRYist way to do it, but I ended up putting the whole form in the partial, and then just not bothering with nested forms. This worked just fine with the AJAX solution.
It makes me sad to repeat the form elements they have in common every time, but it's worth it for the AJAX functionality.
I'll try making a bit more DRY, it occurs to me I could try making the subclass partial not need a form, but it could still call a super class form partial to insert the elements each subclass has in common.
I had a similar problem with STI model, which is owned by another class in a "has_many/belongs_to" relationship. In order to build the form for this other class dynamically, I used cocoon gem, together with simple_form.
Cocoon adds methods that dynamically add/remove fields for each concrete class, that inherits from your base STI class.
For example (Publication is the owner class, Item is the base STI model, and Post, Video and Image inherit from it):
# _form.html.haml
= simple_form_for #publication, :html => { :multipart => true } do |f|
= f.simple_fields_for :items do |item|
= render 'item_fields', :f => item
= link_to_add_association 'Add a Post', f, :items, :wrap_object => Proc.new { |item| item = Item.new }
= link_to_add_association 'Add an Image', f, :items, :wrap_object => Proc.new { |item| item = Image.new }
= link_to_add_association 'Add a Video', f, :items, :wrap_object => Proc.new { |item| item = Video.new }
= f.button :submit, :disable_with => 'Please wait ...', :class => "btn btn-primary", :value => 'Save'
The :wrap_object proc passes the concrete class to item_fields which renders the correct partials, such as image_fields or video_fields or whatever.
I hope this helps.
I wrote a longer explanation for this problem at: http://www.powpark.com/blog/programming/2014/05/07/rails_nested_forms_for_single_table_inheritance_associations

Rails Controllers - Adding a Custom Action

I have an Article resource and have defined resourceful routes for it. I want to create a simple page that shows the articles of the current user. I am aware that it is possible to do so by adding another action, for example 'search' to articles controller which will contain the custom code that searches for articles that have the same user id. And for the routes:
resources :articles do
get 'search'
end
But I'm not sure if adding a custom action is a good idea in this case. I'm thinking I can still use the index action (which shows all articles) and pass some sort of parameter from the url so that it can distinguish if the user wants to see all articles or just his own. But I'm not sure exactly how this can be done. Any help would be great. Thanks!
You can use the query string to pass parameters. see here
So you can pass something like .../articles?user_id=2
In your controller, just change the behavior according to the user_id parameter.
you don't need to create a new action/view for it.
You can add a small form to filter all articles or only my articles, for example:
<%= form_tag articles_path, method: :get do %>
<%= radio_button_tag :search, "all", :checked => true %>
<%= label_tag :all %><br />
<%= radio_button_tag :search, "my" %>
<%= label_tag :my_articles %><br />
<%= submit_tag "filter", name: nil %>
<% end %>
than in your controller:
def index
if params[:search] == 'my'
#articles = current_user.articles
else
#articles = Article.all
end

Handling an undefined variable

I render a partial in one view:
<%= render 'video', :video => #video, :video_id => 'video_show_id' %>
and have this code in the partial:
<% if video_id %>
<%= link_to "video", video.video_url, :class => "oembed", :id => video_id %>
<% else %>
<%= link_to "video", video.video_url, :class => "oembed" %>
<% end %>
The problem is that this partial gets rendered in a number of places in my app, and in those other views I do not want to pass :video_id into the partial. Therefore my app throws an error that video_id is undefined. I could pass :video_id => "" into the partial in the other views, but since the partial is rendered in many places, that is kind of a pain. Is there a simpler way to handle this?
Try defined? and not that it really matters but it's actually an operator.
<% if defined? video_id %>
I think the better way to handle this is to create a helper that then manages the values of variables that need to fed to a partial. Something like:
module VideoHelper
def show_video(video, options = {})
options[:id] ||= ""
render 'video', :video => video, :video_id => options[:id]
end
end
Now, instead of having to have that long render partial line in your view, you get to shorten it to a show_video call.
Also, I've found that in the long term, this allows for a lot more flexibility and I have to think a lot less about what variables the partial needs at this time and whether or not they are defined.
Change the check to <% if defined? video_id %>

Building a Helper to Output buttons

I'd like to output a form button:
<% content_tag :button :type => :submit, :class => :positive do %>
<%= image_tag "icons/tick.png"%>
Save
<% end %>
Which should generate:
<button type="submit" class="positive">
<img src="/images/icons/tick.png" alt=""/>
Save
</button>
I have this throughout my site, it's getting messy and I just learned that Rails has helper methods. I was curious. Would it be possible to build a helper method so I could just say something like this in rails
<%= form_button(submit) %>
What do you think? Would this live in the application_helper.rb file?
Something like:
def form_button (type)
if type == 'submit'
<% content_tag :button :type => :submit, :class => :positive do %>
<%= image_tag "icons/tick.png"%>
Save
<% end %>
end
end
To answer your first question: Yes it is possible and that is what the Rails framework encourages: reuse of code (otherwise known as the DRY-principle). Specific to view logic, there is a helper for each and every one of your models. This is the default/convention, though it is not something you have to adhere to. I'll explain more about this in the next paragraph. So yes, put your form_button method into a helper -- not necessarily application_helper.rb.
To answer your second question: You could stick it in application_helper.rb, but there's nothing stopping from you making things a little more logical (i.e., creating a buttons_helper.rb). In Rails 3 specifically, all helpers are available to each and every view (though this has ruffled some feathers). So what you could do instead is create a new helper for yourself (i.e., rails g helper Buttons), specifically for creating your buttons, and put your logic in there.
Take a look at this Railscast. It describes in more detail exactly what I've said above. I had the same question and found it very helpful. http://railscasts.com/episodes/101-refactoring-out-helper-object
If the helper is going to be all around the site then application is probably the best place to put it for helpers.
You could do what you have, you don't need to put the <%%> tags since those are for erb Embeded Ruby which are your view extensions and helpers are just .rb so there is not need for that.
I checked the API and it looks like you don't need to role your own helper for this:
content_tag(:p, "Hello world!")
# => <p>Hello world!</p>
content_tag(:div, content_tag(:p, "Hello world!"), :class => "strong")
# => <div class="strong"><p>Hello world!</p></div>
content_tag("select", options, :multiple => true)
# => <select multiple="multiple">...options...</select>
A better way to do this is apply the image as a background image in you stylesheet or do it inline. You don't need to make a helper for this and BTW what happens if the type is not 'submit'? Do you have other inputs to check for or a default? If not it doesn't make sense to wrap it in a helper.
so you can do this"
<% submit_tag "Save", :class => "your_class", :style => "font-size:10px;"%>
or
<% f.submit "Save", :class => "your_class", :style => "font-size:10px;"%>

Resources