More efficent way to write this if statement? (Clean up code) - ruby-on-rails

Originally, the idea was if the user had written a bio, show it on their profile. Otherwise, display a message.
<% if #user.bio.present? %>
<p class="user-bio"><%= #user.bio %></p>
<% else %>
<% if #user == current_user %>
<p class="user-bio">Press the green edit button and write something interesting about yourself.</p>
<% else %>
<p class="user-bio">Sorry, this user hasn't written anything about themselves yet.</p>
<% end %>
<% end %>
Then I thought of a way to improve this feature. Instead of making the user navigate to their settings page (devise/registrations/edit), let them edit their bio straight from their profile page (users/show).
To do this, the bio will be an input field that just looks like text, but when hovered over or clicked on, the user can see they can edit the text.
So I made this mock up version below and added resources to my user controller. But I get an error with the last else tag. I'm new to rails (and ruby) and I think this code is quite messy. Could someone with more experience help me write a better if statement that solves the same problem. I need a cleaner solution.
<% if #user.bio.present? %>
<p class="user-bio"><%= #user.bio %></p>
<% else %>
<% if #user == current_user %>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, multipart: true }) do |f| %>
<%= devise_error_messages! %>
<div class="input-group">
<%= f.text_field :bio, class: "form-control" %>
</div>
<div class="actions">
<%= f.submit "Update", class: "btn-signin" %>
</div>
<% else %>
<p class="user-bio">Sorry, this user hasn't written anything about themselves yet.</p>
<% end %>
<% end %>

You don't need to nest a new if block inside of the first else.
Also, as #JoeC pointed out in the comments, you need a closing <% end %> for your form_for.
<% if #user.bio.present? %>
<p class="user-bio"><%= #user.bio %></p>
<% elsif #user == current_user %>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, multipart: true }) do |f| %>
<%= devise_error_messages! %>
<div class="input-group">
<%= f.text_field :bio, class: "form-control" %>
</div>
<div class="actions">
<%= f.submit "Update", class: "btn-signin" %>
</div>
<% end %>
<% else %>
<p class="user-bio">
Sorry, this user hasn't written anything about themselves yet.
</p>
<% end %>
If you think this is still too much tag soup, you could put the form inside of a partial named _form.html.erb and render it. That way the main view template is focused on the logic of what to display rather than getting into the details of the form.
<% if #user.bio.present? %>
<p class="user-bio"><%= #user.bio %></p>
<% elsif #user == current_user %>
<%= render 'form' %>
<% else %>
<p class="user-bio">
Sorry, this user hasn't written anything about themselves yet.
</p>
<% end %>
The last thing that I'll point out is that you may want to switch around the first and second if tests. The way your logic is laid out, the user would never be able to edit their bio after they've entered one.

I agree 100% with Chris Peters. You can place the form code into a separate file called partial:
app/views/YOUR_CONTROLLER_NAME/_form.html.erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name),
html: { method: :put, multipart: true }) do |f| %>
<%= devise_error_messages! %>
<div class="input-group">
<%= f.text_field :bio, class: "form-control" %>
</div>
<div class="actions">
<%= f.submit "Update", class: "btn-signin" %>
</div>
<% end %>
Some information about partials you can find here: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials
If you decide to follow Chris's advice, you can transform the code further:
<% if #user.bio.present? %>
<p class="user-bio"><%= #user.name %></p>
<% else %>
<%= render 'form' if #user == current_user %>
<p class="user-bio">
Sorry, this user hasn't written anything yet.
</p>
<% end %>
Ruby If statements examples: Great Ruby Shorthands For If…Then…Else
You can also consider using content tags as below:
<% if #user.bio.present? %>
<%= content_tag(:p, #user.name, class: "user-bio") %>
<% else %>
<%= render 'form' if #user == current_user %>
<% no_bio_msg = "Sorry, this user hasn't written anything yet." %>
<%= content_tag(:p, no_bio_msg, class: "user-bio") %>
<% end %>
Hope this will help.

Related

(Ruby on Rails) Trying To Edit A Post Using Partials But Having Problems

I'm making a bloglike application. I want to make it so that blog posts are editable if clicked.
This is my partial that displays all the posts of a user and makes them clickable
<% if #feed_items.any? %>
<% #feed_items.in_groups_of(3, false).each do |feeds| %>
<div class="row">
<% feeds.each do |feed| %>
<div class="col-md-4">
<ol class="posts">
<%= link_to edit_post_path(feed) do %>
<%= render feed %>
<% end %>
</ol>
</div>
<% end %>
</div>
<% end %>
<% end %>
This is the Edit view that it redirects to when a post is clicked:
<% if logged_in? %>
<div class="row">
<%= render 'shared/post_form' %>
</div>
<% end %>
And this is the '_post_form' partial which is the layout for an editable post:
<%= form_for(#post) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new post..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
When I click a post to edit it it does redirect however it gives me the error: "First argument in form cannot contain nil or be empty"
Any suggestions on how to resolve this?
In your controller's edit method you have to set #post like,
def edit
#post = Post.find(params[:id])
end

rails submit_tags to different actions

<%= simple_form_for#equipment, :url => equipments_path, :method => :post do |f| %>
....
<% if #equipment.id.present? %>
<div class="actions">
//TODO submit_tag to action Update
</div>
<% else %>
<div class="actions">
<%= submit_tag "Adicionar Equipamento" %>
</div>
<% end %>
<% end %>
In this example I have two buttons,if the object exists I have the first button and when not exists I have the second button. The second button send a request to controller Equipments#Create. How can I send a request to Equipments#Update in first button ?
<%= simple_form_for #equipment do |f| %>
<div class="actions">
<%= submit_tag(#equipment.persisted? ? "Create Equipment" : "Update Equipment") %>
</div>
<% end %>
This is the short way to do it.
Usually you would use I18n to translate these labels.
(see: I18n for model-specific Rails submit button)

ActionView::TemplateMissing error in Rails 4 when rendering shared errors

I'm learning Rails and am implementing an app for which I decided to go through the Site Point tutorial.
According to it, I've done the following in apps/views/users/new.html.erb
<%= form_for #user do |f| %>
<%= render 'shared/errors', object: #user %> #=> error
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, placeholder: 'Your display name', class: 'form-control', required: true %>
</div>
<% end %>
But I'm getting
ActionController:MissingTemplate in Users#new
How can I get rid of it? I've posted the full trace here
The tutorial you're following assumes you have an _errors.html.erb file in the app/views/shared directory.
<%= render 'shared/errors', object: #user %>
That line of code is trying to render that partial and pass #user which will be used in the partial.
If you want to remove that error then remove that line of code. If you want to display those errors then create the relevant file and read up on how to display errors in the view.
<% if object.errors.any? %>
<div id="error_explanation">
<ol>
<% object.errors.full_messages.each do |msg| %>
<li class="alert alert-danger"><%= msg %></li>
<% end %>
</ol>
</div>
<% end %>
Put this in your app/views/shared/_errors.html.erb

Rails 4 not rendering code

I am trying to render a partial on a page in rails. For some reason the code is not being rendered into html. I have tried taking it out of the partial and just placing it in the profile page but still nothing. I am getting no errors and have restarted the server but still nothing. This is in development mode. Also all code except the message code works fine. Any help would be appreciated. Here is the code.
profile.html.erb
<% unless #pictures.nil? %>
<div id="container" style="width: 500px; height: 450px;">
<div id="slides">
<% #pictures.each do |picture| %>
<%= image_tag(picture.image.url(:thumbnail)) %>
<% end %>
<%= image_tag("left.png") %>
<%= image_tag("right.jpeg") %>
</div>
</div>
<% end %>
<% render 'message' %>
_message.html.erb
<% form_for #message, :url => messages_create_path do |f| %>
<% f.hidden_field :from, value: current_user.id %>
<% f.hidden_field :to, value: #user.id %>
<% f.text_area :message %><br />
<% f.submit "Send Message" %>
<% end %>
To make your form_for code block to render you need to use <%= tag as follows:
<%= form_for #message, :url => messages_create_path do |f| %>
<% f.hidden_field :from, value: current_user.id %>
<% f.hidden_field :to, value: #user.id %>
<% f.text_area :message %><br />
<% f.submit "Send Message" %>
<% end %>
To elaborate on vinodadhikary's answer, ERB has output tags and evaluation tags. To evaluate something you would use <% expression %>, and to output you would use <%= output.me %>. The earlier is usually used for flow control in templates, and outputs nothing. The output happens within after a decision is made. The latter is used to output stuff.

How to hide the form elements if the field is empty in ruby on rails?

I am using OpenStruct form for in my view like the following,
<%= form_for(OpenStruct.new(params[:f]), as: :f, url: product_types_path, method: :get, remote: true, html: {class: 'type'}) do |f| %>
<div class="field">
<%= f.label :product_type_id %>
<%= foreign_key(f, :product_type_id, Asset::Product::Type) %>
</div>
<% end %>
I want to hide the other form elements if product_type_id is nil or blank or empty?
I tried this,
<% unless product_type_id.blank? %>
<div class="field">
<%= render 'form' %>
</div>
<% else %>
<p>Select Product Type</p>
<% end %>
So if I got it right what you want to do is not to render the form if what you received with the param product_type_id is blank.
Is it possible for you to instatiate the variable in the controller instead of defining it directly in the form builder?
For instance in your controller:
def index
#product_type = OpenStruct.new(params[:f])
end
and in your view:
<% unless #product_type.product_type_id.blank? %>
<div class="field">
<%= render 'form', product_type: #product_type %>
</div>
<% else %>
<p>Select Product Type</p>
<% end %>
and finally your partial:
<%= form_for(product_type), as: :f, url: product_types_path, method: :get, remote: true, html: {class: 'type'}) do |f| %>
<div class="field">
<%= f.label :product_type_id %>
<%= foreign_key(f, :product_type_id, Asset::Product::Type) %>
</div>
<% end %>

Resources