Ruby on Rails: How to display everything associated with user? - ruby-on-rails

I have some associations that is connected to the user
User model
has_many :lists
has_many :ideas
How do I display the lists and ideas in the user's page?
In my users controller, show method
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
In my show.html.erb
I can only display user name, i.e.:
<%= #user.username %>
I'm trying to see what I need to put in the show action so I can do something like
#user.lists.name, or #user.ideas.name
I'm new to rails still and I'm trying to understand how to link everything together with user?
Hopefully this is enough information?
Thanks

Those associations returns collections, an array of many objects. So you must iterate though all the association records.
<p>Name: <%= #user.username %></p>
<p>Ideas:</p>
<ul>
<% #user.ideas.each do |idea| %>
<li><%= idea.name %></li>
<% end %>
</ul>
<p>Lists:</p>
<ul>
<% #user.lists.each do |list| %>
<li><%= list.name %></li>
<% end %>
</ul>
Which might render something like:
<p>Name: Andrew</p>
<p>Ideas:</p>
<ul>
<li>Light Bulb</li>
<li>Cotton Gin</li>
<li>Smokeless Ashtray</li>
</ul>
<p>Lists:</p>
<ul>
<li>Chores</li>
<li>Shopping</li>
<li>Wishlist</li>
</ul>

Related

(Rails) Trying to show the correct item when someone clicks on the link_to

Hello I'm still fairly new to Rails but, currently I have been working on a Rails project for bit now and my last issue with it is when someone clicks on a specific recipe it only shows the very first one a user ever created. I've accessed my database through my console to see if these recipes are saving and they are but when I click on any of the links to a specific recipe it still shows the incorrect one and it won't show the recipe name either.
Here's my recipe controller
class RecipesController < ApplicationController
before_action :require_login
def show
#recipe=Recipe.find_by(params[:name])
binding.pry
end
def index
#recipes =Recipe.all
#binding.pry
end
def new
#recipe = Recipe.new
#recipe.ingredients.build(name: "name")
end
def create
#recipe = Recipe.new(recipe_params)
#recipe.save
#binding.pry
redirect_to recipes_path
end
private
def recipe_params
params.require(:recipe).permit(:id,:name,:content, ingredients_attributes: [
:recipe_id,
:user_id,
:name,
:quantity
]
)
end
end
Index Page
<h1>All Recipes</h1>
<ul>
<% #recipes.each do |recipes| %>
<li><%= link_to recipes.name, recipes %></li>
<% end %>
</ul>
Show Page
<% #recipe.name do |r| %>
<h2> <%= r.name %></h2>
<h2> <%= r.content %></h2>
<%end%>
<h3>Ingredients</h3>
<ul>
<% #recipe.ingredients.each do |ingredient| %>
<li><%= "#{ingredient.name} X #{ingredient.quantity}" %></li>
<% end %>
</ul>
Any help would be appreciated
Thank you!
In your show method it's either one of those
Recipe.find_by(name: params[:name])
# or ...
Recipe.find(params[:id])
...depending on what setup you got going in your routes, the second one is the usual Rails way of doing things.
There are a few issues with your code. In your RecipesController, change the show action code to this:
def show
#recipe = Recipe.find(params[:id])
end
In your index.html.erb view, change the code that iterates through your recipes to this:
<% #recipes.each do |recipe| %>
<li><%= link_to recipes.name, recipe %></li>
<% end %>
And finally, in your show.html.erb view, change the code to this:
<h2><%= #recipe.name %></h2>
<h2><%= #recipe.content %></h>
<h3>Ingredients</h3>
<ul>
<% #recipe.ingredients.each do |ingredient| %>
<li><%= ingredient.name %> X <%= ingredient.quantity %></li>
<% end %>
</ul>
Summary of the changes
In the show action of the RecipesController, you search for the recipe by the id passed in from the view. That id comes from this line:
<%= link_to recipe.name, recipe %>
recipe gets to_param called on it, which returns the id of that particular recipe which you then use in the show action of the RecipesController to find the correct recipe.
In the index.html.erb view, you iterate through all of the recipes, via the #recipes variable, and output each recipe. Since you are outputting each recipe, you normally use recipe instead of recipes as the block variable.
In the show.html.erb view, you don't need to iterate through all recipes because you only have one recipe from the show action of the RecipesController. That recipe is stored in the #recipe variable, so you can use that variable directly in the view.

How to display error messages in a multi-model form with transaction?

Two models, Organization and User, have a 1:many relationship. I have a combined signup form where an organization plus a user for that organization get signed up.
The problem I'm experiencing is: When submitting invalid information for the user, it renders the form again, as it should, but the error messages (such as "username can't be blank") for the user are not displayed. The form does work when valid information is submitted and it does display error messages for organization, just not for user.
How should I adjust the code below so that also the error messages for user get displayed?
def new
#organization = Organization.new
#user = #organization.users.build
end
def create
#organization = Organization.new(new_params.except(:users_attributes)) #Validations require the organization to be saved before user, since user requires an organization_id. That's why users_attributs are above excluded and why below it's managed in a transaction that rollbacks if either organization or user is invalid. This works as desired.
#organization.transaction do
if #organization.valid?
#organization.save
begin
# I executed next line in debugger (with invalid user info), which correctly responds with: ActiveRecord::RecordInvalid Exception: Validation failed: Email can't be blank, Email is invalid, Username can't be blank, etc.
#organization.users.create!(users_attributes)
rescue
# Should I perhaps add some line here that adds the users errors to the memory?
raise ActiveRecord::Rollback
end
end
end
if #organization.persisted?
flash[:success] = "Yeah!"
redirect_to root_url
else
#user = #organization.users.build(users_attributes) # Otherwise the filled in information for user is gone (fields for user are then empty)
render :new
end
end
The form view includes:
<%= form_for #organization, url: next_url do |f| %>
<%= render partial: 'shared/error_messages', locals: { object: f.object, nested_models: f.object.users } %>
<%= f.text_field :name %>
# Other fields
<%= f.fields_for :users do |p| %>
<%= p.email_field :email %>
# Other fields
<% end %>
<%= f.submit "Submit" %>
<% end %>
The error messages partial is as follows:
<% object.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
Update: Following the steps from Rob's answer I arrived at the errors partial below. This still does not display error messages for User. I added debugger responses inside the code below and for some reason nested_model.errors.any? returns false, while the debugger inside the controller (see above) does return error messages for user.
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
</ul>
</div>
<% end %>
<% if defined?(nested_models) && nested_models.any? %>
# Debugger: responds with "local-variable" for "defined?(nested_models)" and for "nested_models.any?" returns true.
<div id="error_explanation">
<ul>
<% nested_models.each do |nested_model| %>
# Debugger: "nested_model" has the same values as "nested_models.any?", as you would expect. But for "nested_model.errors.any?" it returns false, which it shouldn't.
<% if nested_model.errors.any? %> #Initially had "unless nested_model.valid?" but then errors for User are immediately displayed on loading the form page (new method).
<ul>
<% nested_model.errors.full_messages.each do |msg| %>
<li><%= msg.html_safe %></li>
<% end %>
</ul>
<% end %>
<% end %>
</ul>
</div>
<% end %>
Try adding validates_associated :users under your has_many :users association in Organization.
http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_associated
Did you code successfully create a person during the rescue block?
rescue ActiveRecord::RecordInvalid => exception
# do something with exception here
raise ActiveRecord::Rollback
#organization.users.build if #organization.users.blank?
render :new and return
This code looks like it will create a new empty User regardless of incorrect validations. And render new will simply return no errors because the user was successfully created, assuming Organization has no Users.
The control flow of this method has a few outcomes, definitely needs to be broken down some more. I would use byebug and walk through the block with an incorrect Organization, then incorrect name. Then an empty Organization with incorrect User attributes.
organization has_many :users and user belongs_to :organization
organization.rb
accepts_nested_attributes_for :users
new.html.erb
<%= form_for #organization, url: next_url do |f| %>
<%= render 'shared/error_messages', object: #organization %>
<%= f.text_field :name %>
# Other fields
<%= f.fields_for(:users,#organization.users.build) do |p| %>
<%= p.email_field :email %>
# Other fields
<% end %>
<%= f.submit "Submit" %>
<% end %>
In controller
def create
#organization = Organization.new(new_params)
if #organization.save
flash[:success] = "Yeah!"
redirect_to root_url
else
render :new
end
end
This is very related to this question. The key is that <%= render 'shared/error_messages', object: f.object %> is, I assume, only calling the .errors method on the object it is passed (in this case, organization).
However, because the user errors reside with the user object, they won't be returned and therefore will not be displayed. This requires simply changing the view logic to also display the results of .errors on the various user models. How you want to do so is up to you. In the linked thread, the accepted answer had the error message display code inline instead of in a partial, so you could do it that way, but it would be somewhat redundant.
I would modify my shared/error_messages.html.erb file to check for another passed local called something like nested_models. Then it would use that to search the associated models and include the errors on that. We just would need to check whether it is defined first so that your other views that don't have a nested model won't cause it to raise an error.
shared/error_messages.html.erb
<% if object.errors.any? %>
<div class="error-messages">
Object Errors:
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% if defined?(nested_models) && nested_models.any? %>
Nested Model(s) Errors:
<ul>
<% nested_models.each do |nested_model| %>
<% unless nested_model.valid? %>
<li>
<ul>
<% nested_model.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</li>
<% end %>
<% end %>
</ul>
<% end %>
</div>
<% end %>
Then you would just need to change a single line in your view:
<%= render partial: 'shared/error_messages', locals: { object: #organization, nested_models: #organization.users } %>
Looks like you have a lot of untestable logic in your controller. Looks like for you logic will be better to use simple FormObject pattern.
https://robots.thoughtbot.com/activemodel-form-objects

Render Restforce::Collection

I am using the restforce gem and I have had success pulling the restforce collection and rendering the first user in the collection.
#users = client.query("select Id,Name from User where Id != ''")
# => #<Restforce::Collection >
#user = #users.first
I am stuck on what seems to be a pretty basic problem. I cannot seem to create a view with a partial to iterate through the collection and show a list of all the users my query has returned. See view below:
<ul class="users">
<%= render 'user', collection: #users %>
</ul>
See partial below:
<%= #user.Name %>
Not a lot going on here, just looking for a basic way to show a list of the user's names.
You could just iterate the collection.
<% #users.each do |user| %>
<ul class="users">
<li><%= user.Name %></li>
</ul>
<% end %>
If you really need to, you can still use a partial like <%= render 'user', user: user %>

update partial with object content in rails 4

Well, i have a problem, and i was wondering if it could be solved with rails only.
I have setup a view (home.html.erb) vith 3 partials, like this:
<%provide :title, 'Reader'%>
<div class = "row">
<div class = "span4">
<div class = "row">
<%= render 'layouts/add_subscription'%>
</div>
<div class = "row">
<%= render 'layouts/subscription_list'%>
</div>
</div>
<div class = "span8">
<div class = "row">
<%= render 'layouts/view' %>
</div>
</div>
</div>
where subscription_list shows up a list of links pointing to the list action after a redirection, each of them with the id of the subscription:
<ul>
<% current_user.subscriptions.each do |s| %>
<li><%= link_to s.url, "/list?s_id=#{s.id}" %></li>
<% end %>
</ul>
So, each of these links points to the list action in the controller, which tries to fetch the feed list of the subscription just clicked, and update the home view with the list of titles for the selected subscription:
def list
s_id = params[:s_id]
feed = ""
if !s_id.blank?
s = Subscription.find_by(id: s_id)
feed = Feedzirra::Feed.fetch_and_parse(s.url)
#render partial: "layouts/view", :locals => {:f => feed}
end
The problem is that I'm stuck at this point. I've tried to do a redirect_to home_path with feed as a parameter, and even a render (the line before the end of the list method) to see what happened, but nothing updates 'just' the layouts/view partial:
<ul>
<% if defined? feed and !feed.blank? %>
<% f.entries.each do |entry|%>
<li><%= entry.title %></li>
<% end %>
<% end %>
</ul>
So, I was wondering if it's possible to update the partial and see the result after a page reload using only rails methods, or if it can/must be done using javascript, and a clue to how to do this. Thanks in advance.
The goal you want to achieve is to show feed entries in the home.html.erb after clicking a link.
You can do it by pointing your links to the home action instead of list so that rails will automatically render your home.html.erb view and
you have to assign the instance variable #feed so it will be visible in your view.
You can do it like this (refactored a bit):
controller
def home
s_id = params[:s_id]
if s_id.present?
s = Subscription.find_by(id: s_id)
#feed = Feedzirra::Feed.fetch_and_parse(s.url)
end
end
layout/view
<ul>
<% if #feed.present? %>
<% #feed.entries.each do |entry|%>
<li><%= entry.title %></li>
<% end %>
<% end %>
</ul>
I'm not sure what is the path to your action, I assume here that home is the root ("/")
layouts/subscription_list
<ul>
<% current_user.subscriptions.each do |s| %>
<li><%= link_to s.url, "/?s_id=#{s.id}" %></li>
<% end %>
</ul>

Rails - error messages can't be displayed

When I send wrong email, validation can't pass but error messages in views don't be displayed :(
I have in models:
validate :recipient_not_have_invitation, :notice => "That user have already invitation"
def recipient_not_have_invitation
errors.add :notice, 'That user have already invitation' if InvitationToGroup.find_by_recipient_email_and_group_id(recipient_email, group_id)
end
in controller:
(...)
if #invitation_to_group.save
Mailer.invitation_to_group(#invitation_to_group).deliver
redirect_to root_url, :notice => "Successfully send invitation to user #{#invitation_to_group.recipient_email}"
else
redirect_to new_invitation_to_group_path(:group_id => #invitation_to_group.group_id)
end
In views (invitation_to_groups/new.html.erb)
<h2>New Invitation to group </h2>
<% if notice %>
<p id="notice"><%= notice %></p>
<% end %>
<%= form_for #invitation_to_group do |f| %>
<% if #invitation_to_group.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#invitation_to_group.errors.count, "error") %> prohibited this user from being invitation:</h2>
<ul>
<% #invitation_to_group.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
(...)
The problem is that you are using redirect_to after else in your controller. So you are going to new action and in this action you have InvitationToGroup.new. So you build new object without errors ;) You need to use render method instesd.
PS. you should really consider using "formtastic". It will imporve your code in views. Please watch this two railscasts: http://railscasts.com/episodes/184-formtastic-part-1 and http://railscasts.com/episodes/185-formtastic-part-2

Resources