Rails form multiple views? - html

I'm looking for a way to have a contact form in the application layout and show it on all pages.
Ideally i'd like to just do a
form_for #contact_us
I've tried creating the instance variable in the application controller but it doesnt seem to be in scope when the layout loads..
(does the layout load before the result of the action??)
I guess id settle for a form_tag.
Whats the ususal way of doing this kinda thing?
Thanks!

You can use a partial. Put the form code (use form_tag) in the partial and render the partial in the layout.
More about partials here.

What data are you assigning to #contact_us? You might consider using form_tag rather than form_for if your form doesn't require a resource.
Set up whatever you need in your application controller...
before_filter :prepare_contact_form
def prepare_contact_form
#contact_us = "The stuff your form needs"
end
Create a partial view containing your form. Assuming form_tag meets your needs, for lack of more information...
<!-- app/views/_contact_form.html.erb -->
<%= form_tag "/contact_us" do %>
<%= #contact_us %>
<%= text_field_tag :from_email %>
<%= text_field_tag :message %>
<%= submit_tag 'Submit' %>
<% end %>
Render the partial in your application layout...
<!-- app/views/layouts/application.html.erb -->
render :partial => "contact_form"
Then handle the request in whichever controller action /contact_us is routed to.

Related

How to render html.erb partial dynamically depending on the origin of the render call?

I've got a _topbar.html.erb partial with element for that I render from the index of homepage.
I want to call the same partial in other pages, but depending on the endpoint that's rendering it, I want to display a different content.
Is there any way to apply a dynamic content based on the origin of the render on Rails?
One way to do this is by using current_page? So you could do something like:
<!-- _topbar.html.erb -->
<% if current_page?(posts_path) %>
Here are my posts
<% else %>
Check out my <%= link_to "posts page", posts_path %>
<% end %>
(docs are here)

Using one '_form.html.erb' partial but have different elements appear for certain views

Is it possible to display certain elements within a partial for a particular view in Rails? For example I'd like the submit button's text to change depending on the view: so if I'm in the new.html.erb I'd like the submit button to appear as, <%= f.submit 'Create Account' %> and <%= f.submit 'Update Account' %> for edit.html.erb. The unconventional way would be to manually add the custom code into the each view but is there a clever way to do this in my _form.html.erb partial?
First of all, I would recommend putting it into the new and edit views. However, you can switch off of params[:action] if you want to. As in
<%= f.submit(((params[:action] == 'new') ? 'Create' : 'Update') + ' Account') %>
Use simple_form with i18n for that. SimpleForm make it automatically.
Example:
<%= simple_form_for(#message) do |f| %>
<%= f.error_notification %>
<%= f.input :title %>
<%= f.input :description %>
<%= f.button :submit, class: "btn btn-primary" %>
<% end %>
I second kdeisz's answer if your intention is to use a single partial. The line he wrote will not be necessary if you use two separate views - You can just use different names on the same button in each view without any need for conditional logic.
To answer your supplemental questions: There is a tradeoff here between future changeability and DRY code. If your new and edit needs will start to differ significantly, you will have a lot of bloated, difficult-to-change conditional logic in your partial if you use it to render major features.
If you keep the views separated, this may repeat a significant amount of code, but it will also make the individual pages easier to change; the functions of each view will be tailored very specifically to the needs of each HTTP verb.
The answer isn't to conform completely to REST or to DRY "just because", but to ask yourself what will result in more work down the road. If your new and edit pages will be basically the same but for a few very minor features, the single partial (DRY) is more practical. If you see them diverging significantly in the future, keep them separated into two views (less DRY but more changeable).
Params. Each request made to Rails will automatically include an action and a controller based on the route the user requests; for example, navigating to /foo/bar might trigger action bar for controller foo, depending on how you've set up config/routes.rb. Rails fills in params[:action] and params[:controller] with these automatically. A good explanation of how this works, and how to access path and request params, can be found here.

What is the difference between using rails form_tag and html form code in an erb file?

I had a problem with a rails app I am making. It is an e-commerce site, and on the page where the user enters their billing info, posting the form causing the cart information to be lost.
I solved the problem by changing the markup in the erb file. The first code for the form was:
<form method="post" id="checkout" action="<%= url_for :action => :place_order %>" >
<%= submit_tag "Place Order" %>
</form>
This didn't work, so I tried the following:
<% form_tag :method => 'post', :controller => 'checkout', :action => 'place_order' do %>
<%= submit_tag 'Place Order 2' %>
<% end %>
Does anyone know why the second piece of code does not cause the cart information to be discarded?
I was going to write this as a comment, but it's too long
Helpers
As a rule, you generally need to use the Rails helpers (of which form_tag is one) wherever possible.
The reason being that as Rails (& HTML) develops, you'll find that the helpers will adapt to keep up with current syntax, whereas using "naked" HTML will not. This not only keeps your application conventional, but DRY
By manually typing out an HTML form, you not only open yourself up to syntax change issues, but also you won't get any of the benefits that Rails-integration provides, such as CSRF protection
form_tag
A quick note on your form_tag
You've defined the route for the form_tag by using :controller & :action. You'd be much better using one of the route_helpers that will be defined in your Rails routes file:
#config/routes.rb
resources :checkout, path_names: { create: "place_order" }
This will allow you to use the following:
<%= form_tag checkout_place_order_path do %>
It must also be noted that the form_for tag will actually employ the :post method by default
--
Params
You mention the params for your item are not being passed using your standard form.
The reason for this is actually quite simple - Rails builds a params hash whenever you send data to the controller via an HTML form.
The issue you have is that if you define your form with "naked" HTML, those params will not be generated correctly (they are built from your form element names). This means if you want to send the correct data through to your application, you'll be much better suited to using one of the helpers, such as form_for (which is what you've now discovered)
form_for
As mentioned by DMKE, you may wish to use form_for. This is different than form_tag, as it relies on having an ActiveRecord object to work with. You'd have to do it like this:
#app/controllers/checkout_controller.rb
Class CheckoutController < ApplicationController
def new
#checkout = Checkout.new
end
def create
#checkout = Checkout.new(checkout_params)
#checkout.save
end
end
You can then populate a form_for with the following:
#app/views/checkout/new.html.erb
<%= form_for #checkout do |f| %>
Rails usually adds CSRF protection token in form of hidden fields into your HTML form, but only if you use the Rails helper methods. Compare this view
<% form_tag method: 'post', controller: 'checkout', action: 'place_order' do %>
<%= submit_tag 'Place Order 2' %>
<% end %>
with this rendered output:
<form accept-charset="UTF-8" action="/checkout/place_order" method="post">
<div style="display:none">
<input name="utf8" type="hidden" value="✓">
<input name="authenticity_token" type="hidden" value="q5PYT8i+XTlnoKeVqCoz8VMtKKSJP+CXQb/E+G0Vxlk=">
</div>
<input type="submit" name="submit" value="Place Order 2">
</form>
Now, to get your orginal form up and running, you can either insert this snippet into your <form> tag:
<%= hidden_field request_forgery_protection_token, value: form_authenticity_token %>
or you can disable CSRF protection (not recommended at all!):
# in your app/controllers/application_controller.rb
skip_before_action :verify_authenticity_token
You should at least read the Rails Security Guide on why disabling the CSRF protection is a bad thing.
N.B. Is there a reason why you don't use form_for?

Render Partial Help and Breakdown

When a person uses:
<%= render user.posts %>
what is happening behind the code that is tells it to search for the partial _post.html.erb and know to pass the a specified user inside the call?
I have seen many example where a partial is called from outside the index, such as:
<%= render partial: "posts", locals: {post: post} %>
But that is different from the example above
render user.posts detects a model/s as a parameter, and searches for /posts/_post.html.erb partial, see here, section 3.4.5.
render :partial => 'xxx/yyy' searches xxx/yyy file in views path app/views, i.e. app/views/xxx/yyy, you can see in which places these views are searched by looking at your rails server console.
Figured out what I was trying to achieve. I knew I was missing some simple.
Inside my show view:
<% #user.posts.each do |post| %>
<%= post.title %>
<% end %>

using html tags within ruby on rails if statement

i have a method in my application controller that checks whether a user is in the correct group to access certain functions.
My problem is that when a user edits their own account settings i dont want them to be able to edit their group, unless they are part of the transport group. Currently i have this
<%= if logged_in_as_transport? %>
<div class="field">
<%= f.label :user_type %><br />
<%= f.select :user_type, [['Transport','1'],['Staff','2']] %>
</div>
<% end %>
My method works fine as it is used to check the permissions on the page. It would be ideal if the drop down box was not visible for other groups but just being disabled would work too.
the above code creates a SyntaxError "unexpected tRPAREN"
');#output_buffer.append= ( if logged_in_as_transport? );#output_buffer.safe_concat('
if anyone can help that would be great.
<%= if logged_in_as_transport? %>
should be
<% if logged_in_as_transport? %>
an if statement's conditional doesn't have a return value to output to your view's HTML (which is what your'e saying you want to have happen by using <%= instead of <%)