Hi I worked through Michael Hartl's RAILSTUTORIAL book and I have a question about how he builds the user's show page.
The page is supposed to list all the posts a user has made.
UsersController
def show
#user = User.find(params[:id])
#posts = #user.posts.paginate(:per_page => "10",:page => params[:page])
#title = #user.name
end
users/show.html.erb
<table class="profile" summary="Profile information">
<tr>
<td class="main">
<h1><%= #user.name %></h1>
<%= render 'follow_form' if user_signed_in? %>
<% unless #user.posts.empty? %>
<table class="posts" summary="User posts">
<%= render #posts %> # this goes to posts/_post and sends the object as post
# that makes the _post view use a local variable correct?
</table> # is there a way to do with with an #post variable?
<%= will_paginate #posts %>
<% end %>
</td>
<td class="sidebar round">
<%= link_to avatar_for(#user), #user.avatar.url %><br />
<strong>Name</strong> <%= #user.name %><br />
<strong>URL</strong> <%= link_to user_path(#user), user_path(#user) %>
<strong>Posts</strong> <%= #user.posts.count %>
<%= render 'shared/stats' %>
</td>
</tr>
</table>
posts/_post.html.erb
<tr>
<td class="post">
<span class="title"><strong><%= link_to post.title, post %></strong></span><br />
<span class="timestamp">
Posted <%= time_ago_in_words(post.created_at) %> ago. </span>
Likers<span id="likers"><br />
</span>
</td>
<% if current_user?(post.user)%>
<td>
<%= link_to "delete", post, :method => :delete,
:confirm => "You sure?",
:title => post.content %>
</td>
<%end%>
</tr>
I need to render a partial in the users view that uses the post object, but it asks for it as #post and since no #post has been defined in the show action of the user's controller I get a nil error.
It seem odd to me to go from the user's controller to the posts view and use a local variable, which if I understand local variables correctly can't be used outsider that view. Is there a way to assign the value of post in that view to #post in the users view?
Thanks for the help
You need to use a local variable in the partial, and assign it in the locals hash. This line is a shortcut for iterating through the array and rendering the partial. I'm not sure if this works anymore in Rails 3:
<%= render #posts %>
The way I would do it:
<% #posts.each do |post| %>
<%= render 'posts/post', :post => post %>
<% end %>
The slightly older way of rendering partials:
<% #posts.each do |post| %>
<%= render :partial => 'posts/post', :locals => {:post => post} %>
<% end %>
Related
I have an index page with a partial form to submit new records to Package model. I keep this form in the index page, so the user doesn't need to leave the page when repeating this action, several times.
In the same page I have a form_tag fir multiple updates for the same controller, namely packages_controller.
Everything works fine, except the following: when hit the update button, going to the form BUT instead of submitting I go back (with the browser) and try to select other records to be updated then I have a routing error:
Routing Error
No route matches [PUT] "/projects/47/orderlines/18/packages"
My index page looks like this:
<% if current_user %>
<%= render "packages/form" %>
<% end %>
<% if #packages.count >= 1 %>
<table class="table table-striped">
<thead>
<tr>
<th> <input type="checkbox" id="selectAll" value="selectAll"></th>
<th>Packed </th>
<th>#No.</th>
<th>Type</th>
<th>Gross weight</th>
<th>Length</th>
<th>Width</th>
<th>Height</th>
<th></th>
<th>Container</th>
</tr>
</thead>
<%= form_tag edit_multiple_project_orderline_packages_path, method: :get do %>
<tbody>
<% for package in #packages %>
<% if package.packed== true %>
<% #label_type="success" %>
<% else %>
<% #label_type="default" %>
<% end %>
<tr>
<td><%= check_box_tag "package_ids[]", package.id %></td>
<td><span class="label label-<%= #label_type %>"><% if package.packed==true %>Packed<% else %>Unpacked<% end %></span></td>
<td><%= package.package_no %></td>
<td><%= package.package_type %></td>
<td><%= package.gross_weight %></td>
<td><%= package.length %></td>
<td><%= package.width %></td>
<td><%= package.height %></td>
<% if #orderline.packages.count >= 1 %>
<td><%= link_to 'Delete', [package.orderline.project, package.orderline, package],
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
<td><%= #containers.find(package.container_id).container_id if package.packed %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<%= submit_tag "Add to container", class: "btn btn-primary" %>
<% end %>
<br />
<%= will_paginate %>
<br>
And the multiple_edit form
<div class="col-sm-4">
<%= form_tag update_multiple_project_orderline_packages_path, method: :put do %>
<ul>
<% #packages.each do |package| %>
<li>
<%= hidden_field_tag "package_ids[]", package.id %>
<%= package.package_no %>
<%= package.container_id %>
<% package.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</li>
<% end %>
</ul>
<%= fields_for :package do |f| %>
<div class="field">
<%= f.label :package_no %><br />
<%= f.text_field :package_no, :class => "form-control" %>
</div>
<br />
<div class="field">
<%= f.label :container_id %><br />
<%= select_tag 'package[container_id]', options_from_collection_for_select(#project.containers, 'id', 'container_id', default_blank: true), prompt: "- Select container -", :class => "form-control" %>
</div>
<br />
<div class="field">
<%= f.label :packed %><br />
<%= f.select :packed, [["Packed", true], ["Unpacked", false]],{ prompt: "- Packing -"},{ :class => "form-control" } %>
</div>
<% end %>
<div class="actions">
<br />
<%= submit_tag "Update", :class => "btn btn-primary" %>
</div>
<% end %>
</div>
And the packages controller edit_multiple actions:
def edit_multiple
#project = Project.find(params[:project_id])
#packages = Package.find(params[:package_ids])
end
def update_multiple
#packages = Package.find(params[:package_ids])
#packages.reject! do |package|
package.update_attributes(package_params.reject { |k,v| v.blank? })
end
if #packages.empty?
redirect_to project_orderline_packages_url
else
#package = Package.new(package_params)
render "edit_multiple"
end
end
packages_controller create action:
def create
project = Project.find(params[:project_id])
orderline = project.orderlines.find(params[:orderline_id])
#package = orderline.packages.new(package_params)
#package.save
if #package.save
flash[:success] = "Package(s) was successfully added."
redirect_to :back
else
render 'new'
end
And my routes:
resources :projects do
resources :containers
resources :orderlines do
resources :packages do
collection do
put :packed
get :edit_multiple
put :update_multiple
end
end
end
end
I just added my routes here:
edit_multiple_project_orderline_packages_path GET /projects/:project_id/orderlines/:orderline_id/packages/edit_multiple(.:format)
packages#edit_multiple
update_multiple_project_orderline_packages_path PUT /projects/:project_id/orderlines/:orderline_id/packages/update_multiple(.:format)
packages#update_multiple
project_orderline_packages_path GET /projects/:project_id/orderlines/:orderline_id/packages(.:format)
packages#index
POST /projects/:project_id/orderlines/:orderline_id/packages(.:format)
packages#create
new_project_orderline_package_path GET /projects/:project_id/orderlines/:orderline_id/packages/new(.:format)
packages#new
edit_project_orderline_package_path GET /projects/:project_id/orderlines/:orderline_id/packages/:id/edit(.:format)
packages#edit
project_orderline_package_path GET /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
packages#show
PATCH /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
packages#update
PUT /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
packages#update
DELETE /projects/:project_id/orderlines/:orderline_id/packages/:id(.:format)
your form_tag code is update_multiple_project_orderline_packages_path
I think it should be update_multiple_project_orderline_package_path(project_id, orderline_id, package_id)
I am not 100% sure with my statement above, because you gave scrambled Rails Routes, hard to read
and your form action seems goes to packages#edit_multiple controller
so paste your edit_multiple method, not create method
are you implementing your scenario above with javascript, or just plain HTML?
I have an app where a user has a portfolio. A portfolio can have many positions and each position can have many movements.
My portfolio show page is built like this:
<p>
<strong>Name:</strong>
<%= #portfolio.name %>
</p>
<h1>Positions</h1>
<div class = 'table'>
<table>
<thead>
<tr>
<th>Name</th>
<th>Quantity</th>
<th>Stock</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody id = "positions">
<%= render #positions %>
</tbody>
</table>
</div>
<h3>New Position</h3>
<%= render 'positions/form' %>
<%= link_to 'Edit', edit_portfolio_path(#portfolio) %> |
<%= link_to 'Back', portfolios_path %>
My position partial is built:
<tr>
<td class="col-md-1"><%= position.name %></td>
<td class="col-md-1"><%= position.quantity %></td>
<td class="col-md-1"><%= position.ticker %></td>
<td>
<%= form_for [#portfolio, position, #movement] do |f| %>
<div id = "movement-errors">
<% if #movement.errors.any? %>
<%= render 'shared/error_messages', object: #movement %>
<% end %>
</div>
<div class="field">
<%= f.label :quantity %>
<%= f.number_field :quantity, class: 'form-control' %>
</div>
<div class="actions">
<%= f.submit class: 'btn btn-primary' %>
</div>
<% end %>
</td>
</tr>
And my shared errors partial is:
<% 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 %></li>
<% end %>
</ul>
</div>
<% end %>
When I submit a nil value on a movement form, I want the errors to pop up and tell the user the message. I have validations checking for presence of quantity.
class Movement < ApplicationRecord
belongs_to :position
validates :quantity, presence: true
And my controller movements#create action looks like
class MovementsController < ApplicationController
before_action :load_portfolio_and_position
def create
#movement = Movement.new(movement_params)
#movement.position_id = #position.id
#movement.update_price
#movement.date = DateTime.now
#movement.trade ='buy'
respond_to do |format|
if #movement.save
#position.update_attribute(:quantity, (#movement.quantity + #position.quantity))
format.html { redirect_to #portfolio, notice: 'Movement was successfully created.' }
format.js
else
format.html { render template: 'portfolios/show' }
format.json { render json: #movement.errors, status: :unprocessable_entity }
format.js
end
end
end
As it stands right now, when I submit an empty form, I get this error using the better_errors gem:
'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
And the render #positions line is highlighted from my portfolio show page. I thought I was implementing a partial path. Is my syntax wrong?
When passing an object, needs to be assigned like this.
<%= render partial: 'shared/error_messages', locals: {object: #movement} %>
http://guides.rubyonrails.org/layouts_and_rendering.html#passing-local-variables
When you pass an object to a partial using the object: attribute, then the partial references that object by the name of the partial
e.g.
When you call
<%= render 'shared/error_messages', object: #movement %>
the shared/error_messages partial will have a local variable error_messages that references #movement
When I am only passing one object to a partial I like to use 'as'
e.g.
<%= render 'shared/error_messages', object: #movement , as: :movement %>
That lets you specify the name of the local variable. If I'm passing multiple objects, I'd use the method #7urkm3n described.
I am struggling to troubleshoot why this ajax call on a standard Destroy method won't work. From looking at my HTML source, I think it might be a problem with either how I'm creating divs using div_for OR it has something to do with my js. I'm at a loss for troubleshooting javascript.
My view:
<% #quizzes.each do |quiz| %>
<%= div_for quiz do %>
<tr>
<td>
<%= link_to quiz.name, quiz_review_path(quiz.id) %>
</td>
<% if quiz.finished? %>
<td>
<%= link_to "Results", quiz_results_path(quiz) %>
</td>
<% elsif quiz.questions.first != nil %>
<td>
<%= link_to "Take quiz", question_answer_path(question_id: quiz.questions.first.id) %>
</td>
<% else %>
<td>
<%= link_to "Broken, delete!", nil %>
</td>
<% end %>
<td>
<%= link_to "Delete", quiz_path(quiz), method: 'delete', remote: true %>
</td>
</tr>
<% end %>
The controller:
def destroy
#quiz = Quiz.find(params[:id])
#quiz.destroy
respond_to do |format|
format.html { redirect_to quizzes_path }
format.js
end
end
My views/quizzes/destroy.js
$(document).ready(function() {
$('#<%= dom_id(#quiz) %>').fadeOut();
})
From looking at the Rails s logs, I can see that the delete request comes in and is processed by the js, so my best guess is it's either an issue with div_for and dom_id OR my js is bad (highly likely).
Fixed it: trying to wrap in a with div_for creates invalid html. Removed the tables and now the ajax works fine.
I'm working through the Rails Tutorial Chapter 12, and get the following error on the Home/Main page when user is signed out (signed in is OK):
I'm a newbie to Rails so please be explicit in your response! Many thanks..
NoMethodError in PagesController#home
undefined method `feed' for nil:NilClass
Rails.root: /Users/fkhalid2008/Documents/First-app
Application Trace | Framework Trace | Full Trace
app/controllers/pages_controller.rb:6:in `home'
Pages Controller
class PagesController < ApplicationController
def home
#title = "Home"
#post = Post.new if signed_in?
#feed_items = current_user.feed.paginate(:page => params[:page])
end
def contact
#title = "Contact"
end
def about
#title = "About Us"
end
end
Home Page View (/app/views/pages/home.html.erb)
<% if signed_in? %>
<table class="front" summary="For signed-in users">
<tr>
<td class="main">
<h1 class="post">What's up?</h1>
<%= render 'shared/post_form' %>
<%= render 'shared/feed' %>
</td>
<td class="sidebar round">
<%= render 'shared/user_info' %>
</td>
</tr>
</table>
<% else %>
<h1>Palazzo Valencia</h1>
<p>
This is the home page for the
Palazzo Valencia
sample application.
</p>
<%= link_to "Sign up now!", signup_path, :class => "signup_button round" %>
<% end %>
Feed Partial
<% unless #feed_items.empty? %>
<table class="posts" summary="User posts">
<%= render :partial => 'shared/feed_item', :collection => #feed_items %>
</table>
<%= will_paginate #feed_items %>
<% end %>
Feed_item Partial
<tr>
<td class="gravatar">
<%= link_to gravatar_for(feed_item.user), feed_item.user %>
</td>
<td class="post">
<span class="user">
<%= link_to feed_item.user.name, feed_item.user %>
</span>
<span class="content"><%= feed_item.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
</span>
</td>
<% if current_user?(feed_item.user) %>
<td>
<%= link_to "delete", feed_item, :method => :delete,
:confirm => "You sure?",
:title => feed_item.content %>
</td>
<% end %>
</tr>
The user is not signed in. Therefore, the current_user method is returning nil, and ruby can't find the feed method.
You could change the code to this:
#title = "Home"
if signed_in?
#post = Post.new
#feed_items = current_user.feed.paginate(:page => params[:page])
end
Now, the new post and the feed items will only be retrieved when the user is signed in.
app/controllers/static_pages_controller.rb
def home
if logged_in?
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
I've checked out a lot of questions for putting multiple objects on one form, but they seem to be out of date.
I have a bunch of user objects:
def index
#users = User.all
#user = current_user
end
that I need to edit in a form. They all have roles which will be edited on the form. The form is rendered in a partial, and nothing shows up, just 'admin form' plaintext.
users/_admin.html.erb
admin form
<% form_for "user[]", :url => users_path do |f| %>
<ul>
<li class="layout">
<div class="header"><h2>Users</h2></div>
<table>
<thead>
...
</thead>
<tbody>
<% #users.each do |user| %>
<% puts "USER #{user}" %>
<tr>
<td><%= f.check_box(:editor) %></td>
<td><%= f.check_box(:admin) %></td>
<td><%= user.first_name %> <%= user.last_name %></td>
<td><%= user.email %></td>
</tr>
<% end %>
</tbody>
</table>
</li>
</ul>
<%= submit_tag "Save"%>
<% end %>
Just the plaintext is rendered, but no form. Any ideas on how to fix it? I've tried the suggestions in these posts: one two three, but they're out of date.
Thank you.
You need to use <%= .. %> in Rails 3.1:
<%= form_for "user[]", :url => users_path do |f| %>