Building a Helper to Output buttons - ruby-on-rails

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

Related

Bootstrap Popover and Rails

Okay so this question has to do with the proper syntax for html.erb code. What I'm trying to do is use a popover button to display a form. I am able to make a button through the button_tag function, and I am able to make the form from the form_tag function, but I am not able to embed one inside the other. I'm not even entirely sure that this is something I should be doing at all. My understanding was that best practices is to avoid using html when you can use erb to do the work of generating the page.
Anyway, here is the code I have thus far:
<%= button_tag "Remove Friend", :title=>"Remove from Friend List",
:id=>"removeFriend", :class=>"btn btn-default", :rel=>"popover",
:data => {:html=>"true", :placement=>"top", :content=>
"form_tag(\"/friend\", method: \"post\") do
label_tag(:symbolInput, \"Enter Username\")
text_field_tag(:symbolInput)
submit_tag(\"Remove\")
end"}%>
So what this does is generate the following HTML
<button class="btn btn-default" data-content="form_tag("/friend",
method: "post") do
label_tag(:symbolInput, "Enter Username")
text_field_tag(:symbolInput)
submit_tag("Remove")
end" data-html="true" data-placement="top" id="removeFriend"
name="button" rel= "popover" title="Remove from Friend List"
type="submit">Remove Friend</button>
So essentially it just literally copied the erb code as text into the popover.
I have also tried framing each line with <%= %>, but I am very unclear on what the syntax for this would be, or when you should do that.
Basically what needs to happen is that the erb for the form has to be translated to html, which will then be passed to the :content section of the button_tag.
Should I be doing this in this way, or is there some other method to accomplish what I am trying to do? Being new to rails, I'm not sure what the best practices are.
By the way, if I use html to code either the form or the button and erb for the other one, it works perfectly, so there is a work around.
The minute you are inside the Embedded Ruby Tags eg <% %> then everything you do inside here is just ruby or to say it another way must be valid ruby, can be any ruby.
The first refactoring I would do is to move all that content code out own it's own. This halfway point is tidier and should give you a better idea.
<% remove_friend_form = "form_tag(\"/friend\", method: \"post\") do
label_tag(:symbolInput, \"Enter Username\")
text_field_tag(:symbolInput)
submit_tag(\"Remove\") end" %>
<%= button_tag "Remove Friend", :title=>"Remove from Friend List",
:id=>"removeFriend", :class=>"btn btn-default", :rel=>"popover",
:data => {:html=>"true", :placement=>"top", :content=> remove_friend_form }%>
After you isolate the content it becomes more obvious that the 'remove_friend_form' could be isolated even further. To do this move this content into it's own partial.
# create new file in the same folder as the current view.
# _remove_friend_form.html.erb
<%= form_tag("friend", method: "post") do |f| %>
<%= f.label_tag(:symbolInput, "Enter Username") %>
<%= f.text_field_tag(:symbolInput) %>
<%= f.submit_tag("Remove") %>
<% end %>
The main page now looks like this
<%= button_tag "Remove Friend", :title=>"Remove from Friend List",
:id=>"removeFriend", :class=>"btn btn-default", :rel=>"popover",
:data => {:html=>"true", :placement=>"top", :content=> (render partial: 'remove_friend_form') }%>

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

Draper with form_for in method

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.

Rails 3 views refactoring: conditionals and finds

What would you recommend as the best way to refactor these two bits of view code?
<%if Supplydetail.find_all_by_isbn_id(#isbn).first.nil? %>
<%else%>
<%if Productavailability.find_by_code(Supplydetail.find_all_by_isbn_id(#isbn).first.productsupply_supplydetail_productavailability).nil? %>
<%else%>
<li><%= Productavailability.find_by_code(Supplydetail.find_all_by_isbn_id(#isbn).first.productsupply_supplydetail_productavailability).value %></li>
<%end%>
<%end%>
and (using formtastic)
%li.tip
= tooltip(:test, :hover)
= f.input :relatedmaterial_relatedproduct_idvalue, :label => "Related ISBN", :as => :select, :collection => Isbn.all, :label_method => :descriptivedetail_titledetail_titleelement_titlewithoutprefix, :value_method => :productidentifier_idvalue
%li.list
= link_to "Edit list", isbns_path
I have examples of each of these about a bazillion times in my app, and would like to know I'm refactoring in the best way before I dive in to this rather huge job.
First of all, an empty if branch in an if/else usually (but not always!) smells bad so don't do that, it just makes your code harder to read and understand.
Also, you're computing Supplydetail.find_all_by_isbn_id(#isbn) and Productavailability.find_by_code(...) twice just to get your <li> output. Don't do that either.
And you might want to push most of that logic into your controller (or maybe a helper depending on where and how often it is used) to cut down on the ERB-noise.
Maybe something like this would serve you (and whoever gets to maintain your code) better; a bit of controller stuff first:
#avail = nil
by_isbn = Supplydetail.find_all_by_isbn_id(#isbn).first
if by_isbn
#avail = Productavailability.find_by_code(by_isbn.productsupply_supplydetail_productavailability)
end
And then in your ERB:
<% if #avail %>
<li><%= #avail.value %></li>
<% end %>
If you're doing a lot of this sort of thing then you could add a Productavailability.for_isbn convenience class method in your model. Then your controller would just need:
#avail = Productavailability.for_isbn(#isbn)
But I wouldn't worry about it until you start repeating yourself.
I'm not familiar with formtastic so I can't help you with that.

Partial template submit_tag

I would like to create a login field everywhere on the top of my page, so I've add a :
in application.html.erb :
<%= render :partial => 'sessions/new' %>
in .../views/sessions/_new.html.erb
<%= form_tag do %>
<div>
<label for="name">Email :</label>
<%= text_field_tag :name, params[:name] %>
<label for="password">Mot de passe :</label>
<%= password_field_tag :password, params[:password] %>
</div>
<div>
<%= submit_tag "Connection" %>
</div>
</fieldset>
But it's work only if I am in a sessions controller when I test it in my browser,
I think that :
<%= submit_tag "Connection" %>
refers to his current controller (sessions) that's why it's doesn't work in ads/index for exemple but do its job in sessions/index.
What can I do ?
Do I have to specify the controller in the submit_tag ?
Thanks a lot :)
You need to tell the form tag the url that the form should submit to. Maybe by default it submits to the current action or something? You should never rely on the default whatever it is.
Read the api
http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M002551&name=form_tag
oh and btw the submit tag is just a button, it doesn't have anything to do with why the form does or doesn't work. There's a lot of confusion among rails novices about forms - a lot of people don't really understand how forms work. Before using any rails helpers at all, i'd strongly recommend making your form in pure html. That way you will understand what is actually going on, and the form helpers will be just that, ie "things that help you to do something more quickly" rather than being these magical things that leave you totally clueless when they don't do what you expect.
You need to specify the controller but on the form_tag not the submit_tag
e.g. <%= form_tag :controller => 'sessions', :action => 'new' %>

Resources