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.
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?
A beginner here, so, please bear with me. :)
I have a partial in a partial that's displaying correctly in my views > products > index.html.erb:
<div>
<table>
...
<tbody>
<%= render product %>
</tbody>
...
</table>
</div>
This is the _product partial:
<div>
<td>
<%= render "product_row", product: product, order_item: #order_item %>
</td>
</div>
..which points to this _product_row partial:
<div>
<%= form_for order_item, remote: true do |f| %>
<%= number_to_currency product.price %>
<%= f.number_field :quantity, value: 1, class: "form-control", min: 1 %>
<%= f.hidden_field :product_id, value: product.id %>
<%= f.submit %>
<% end %>
</div>
All is well, BUT I want to display _product_row in my views > products > show.html.erb instead. So, I copy and paste it and get this error:
NameError in Products#show
undefined local variable or method `product' for #<#<Class:...>
Extracted source:
<%= render "product_row", product: product, order_item: #order_item %>
...so I go in my products_controller and put this:
def show
#product = Product.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #product }
end
end
'still the same error.
Any help will be appreciated.
Thanks!
You are rendering your partial from index.html.erb file like:
<%= render product %>
here, product variable is not defined as a local variable in the view file. From your controller's index action pass product variable as a instance variable like #product or define it in the index.html page.
Or, you can do simply like that:
in products_controller > index
def index
#products = Product.all
end
in products > index.html.erb
<% #products.each do |product| %>
<div>
<table>
...
<tbody>
<%= render product %>
</tbody>
...
</table>
</div>
<% end %>
I am guessing you probably have some loop as follows in your index.html.erb
<% #posts.each do |post| %>
<%= render product %>
<% end %>
Here product in the render call refers to the local variable you defined in the block of the iterator.
But when you try to copy/paste the same code to your show.html.erb, product will is considered undefined as it is out of the loop.
So you should use the following:
<%= render #product %>
in your show.html.erb, assuming you must have assigned #product in your show action.
#Emu and #Dharam suggested that I put
<%= render #product %> instead of <%= render product %>
in my Product show.html.erb and making sure #product is defined in my products_controller.rb, which I did:
def show
#product = Product.find(params[:id])
end
It worked! No more undefined errors.
I did have some styling issues with this, though. Long story-short, I copied and pasted what was in my _product_row partial (see original code in the question above) into my Product show instead of rendering it. Then I modified it a bit basing on Emu and Dharam's suggestion. Basically it planted the idea to try to add #s where it errors out as undefined:
<div>
<%= form_for #order_item, remote: true do |f| %>
<h4 style="font-family: 'Share Tech Mono', sans-serif;">The Small Price to Pay:
<span style="color: green">
<%= number_to_currency #product.price %>
</span>
</h4>
<h4 style="font-family: 'Share Tech Mono', sans-serif;">How many:</h4>
<div class="input-group">
<%= f.number_field :quantity, value: 1, class: "form-control", min: 1 %>
<div class="input-group-btn, add-to-cart-button">
<%= f.hidden_field :product_id, value: #product.id %>
<%= f.submit "ADD IT", class: "btn btn-danger" %>
</div>
</div>
<% end %>
</div>
Then added this to my products_controller.rb:
def show
#product = Product.find(params[:id])
#order_item = current_order.order_items.new
end
I hope this makes sense. Thanks, again, all. :)
In this block of code, i want to put a button "edit" inside the "show" views. However, due to some reason, it did not works out
My home_controller.rb
class HomeController < ApplicationController
def index
#inputs = Person.all
end
def new
#input = Person.new
end
def create
#input = Person.new(input_params)
respond_to do |x|
if #input.save
x.html {redirect_to :action => 'index'}
else
x.html {render :action => 'new'}
end
end
end
def show
#input = Person.find(params[:id])
end
def edit
#input = Person.find(params[:id])
end
def update
#input = Person.find(params[:id])
respond_to do |x|
if #input.update(input_params)
x.html {redirect_to :action => 'index'}
else
x.html {render :edit}
end
end
end
private
def input_params
params.require(:inputs).permit(:name, :weight, :height, :color, :age)
end
end
My routes file only have two lines:
resources: home
root 'home#index'
My index.html.erb
<p id="notice"><%= notice %></p>
<h1>Listing</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th> Weight</th>
<th> Height</th>
<th> Color</th>
<th> Age</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #inputs.each do |person| %>
<tr>
<td><%= person.name %></td>
<td><%= person.weight %></td>
<td><%= person.height %></td>
<td><%= person.color %></td>
<td><%= person.age %></td>
<td><%= link_to 'Show',home_path(person.id) %></td>
<td><%= link_to 'Edit', edit_home_path(person.id) %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Test', new_home_path %>
my show.html.erb:
<p>
<strong>Name:</strong>
<%= #input.name %>
</p>
<p>
<strong>Weight:</strong>
<%= #input.weight %>
</p>
<p>
<strong>Height:</strong>
<%= #input.height %>
</p>
<p>
<strong>Color:</strong>
<%= #input.color %>
</p>
<p>
<strong>Age:</strong>
<%= #input.age %>
</p>
<% link_to 'Edit', edit_home_path(#input) %>
<%= link_to 'Back', home_index_path%>
My form.html.erb
<%= form_for #input, url: {action: "update"} do |person| %>
<div class="field">
<%= person.label :name %><br>
<%= person.text_field :name %>
</div>
<div class="field">
<%= person.label :weight %><br>
<%= person.number_field :weight %>
</div>
<div class="field">
<%= person.label :height %><br>
<%= person.number_field :height %>
</div>
<div class="field">
<%= person.label :color %><br>
<%= person.text_field :color %>
</div>
<div class="field">
<%= person.label :age %><br>
<%= person.number_field :age %>
</div>
<div class="actions">
<%= person.submit %>
</div>
<% end %>
My edit.html.erb
<h1>Editing Data</h1>
<%= render 'form' %>
<%= link_to 'Show', home_path %> |
<%= link_to 'Back', home_index_path %>
The error i get is:
My code <% link_to 'Edit', edit_home_path(#input) %> is point the Edit button to the route home/edit with the obeject #input, that is how i understand it, but it still not working.
Any idea how can i fix this?
Many thanks
The problem is in your show.html.erb. You need to change #person to #input as you have #input defined in your show method
This is really simple, but i'm going slightly mad and probably missing something that's staring me in the face. Can anyone help?
Basically, I have a simple each loop that's returning an extra rogue line. Even when there's nothing in the db, I get one line returned!
My show view including the loop is:
<p id="notice"><%= notice %></p>
<p>
<b>Header:</b>
<%= #mailer.header %>
</p>
<p>
<b>Subtext:</b>
<%= #mailer.subtext %>
</p>
<div id="" class="" padding-left: 30px;>
<h3>Mailer Products </h3>
<ol id="mailer-Product-list">
<% #mailer.mailer_products.sort_by { |mailer_products| mailer_products.position }.each do |mailer_product| %>
<%= content_tag_for :li, mailer_product do %>
<%= mailer_product.product.cat_no %>
<% end %>
<% end %>
</ol>
<%#= link_to 'Done', #product, :class => "standard-button" %>
</div>
<%= form_for([#mailer,#mailer.mailer_products.build]) do |f| %>
<div class="field">
<%= f.label :product_id %><br />
<%= f.text_field :product_id %>
</div>
<div class="field">
<%= f.hidden_field :mailer_id, :value => #mailer.id %>
</div>
<div class="actions">
<%= f.submit "Add Product" %>
</div>
<% end %>
<%= link_to 'Edit', edit_mailer_path(#mailer) %> |
<%= link_to 'Back', mailers_path %>
The controller code is:
class MailersController < ApplicationController
def show
#mailer = Mailer.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #mailer }
end
end
class MailerProductsController < ApplicationController
def index
#mailer_products = MailerProduct.find(:all)
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #mailer_products }
end
end
end
end
Your call to form_for looks like this
form_for([#mailer,#mailer.mailer_products.build]) do |f|
You get an extra blank item because that's what calling .build on mailer_products does: it appends a new instance to the array
When the form is after the loop this doesn't matter, but when things are the other way around the loop will be on the modified array
My usual mistake is adding a <%= instead of a <% on the loop...
<%= #foo.each do |itme| %>
# do stuff
<% end %>
which should be
<% #foo.each do |itme| %>
# do stuff
<% end %>
Double check your tags...
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 %>