Hello I want to write a small blog with Ruby on Rails (5), with category and doctors submitted via form.
But when I submit a doctor it is often shown twice, and I got no idea why.
Here is the source code of my project.
doctors_controller.rb file:
def create
#category = Category.find(params[:category_id])
#doctor = #category.doctors.create(doctor_params)
redirect_to category_path(#category)
end
def destroy
#category = Category.find(params[:category_id])
#doctor = #category.doctors.find(params[:id])
#doctor.destroy
redirect_to category_path(#category)
end
private
def doctor_params
params.require(:doctor).permit(:name, :address, :phno)
end
show.html.erb file:
<p id="notice"><%= notice %></p>
<center><h1>
<%= #category.name %>
</h1>
</center>
<p1><%= render #category.doctors %></p1>
<p2><%= render 'doctors/form' %></p2>
<%= link_to 'Edit', edit_category_path(#category) %> |
<%= link_to 'Back', categories_path %>
_doctor.html.erb file:
<table style="width:100%">
<tr>
<th>Name</th>
<th>Address</th>
<th>phone number</th>
</tr>
<% #category.doctors.each do |doctor| %>
<tr>
<th><%= doctor.name %></th>
<th><%=doctor.address%></th>
<th><%=doctor.phno%></th>
<th><%= link_to 'Delete doctor', [doctor.category, doctor],method: :delete,data: { confirm: 'Are you sure?' } %>
</th>
</tr
<% end %>
</table>
#Prabal-Kar, as Igor suggested are you sure you are 'clicking' the button only once? To prevent users doing this you can disable the button after clicking and you should also ensure you have some unique constraints on your doctor model.
Related
In the home page of my application I iterate over a collection of tennis players (#atp_ranks), and for each tennis player I create a table row with his attributes and a button which allows the current user to enlist that tennis player:
<table>
<thead>
<tr>
<th> Rank </th>
<th> Player </th>
<th> Points </th>
<th id="atp_count" class="tennis_stats"> <%= current_user.atp_ranks.count %> selected </th>
</tr>
</thead>
<tbody>
<% #atp_ranks.each do |tennis_player| %>
<tr id="tennist-<%= tennis_player.ranking %>">
<td class="atpranking"> <%= tennis_player.ranking %> </td>
<td class="atpname"> <%= tennis_player.name %> </td>
<td class="atppoints"> <%= tennis_player.points %> </td>
<% unless Time.now.month == 12 %>
<td>
<div id="atpenlist_form_<%= tennis_player.id %>">
<% if current_user.atpenlisted?(tennis_player) %>
<%= form_for(current_user.atp_selections.find_by(atp_rank_id: tennis_player.id), html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Dump", class: "btn btn-warning btn-sm" %>
<% end %>
<% else %>
<%= form_for(current_user.atp_selections.build, remote: true) do |f| %>
<div><%= hidden_field_tag :atp_id, tennis_player.id %></div>
<%= f.submit "Choose", class: "btn btn-primary btn-sm" %>
<% end %>
<% end %>
</div>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
In order to give the user the opportunity to immediately see the result of submitting the form, I added remote: true in each form, and saved these forms as partials in app/views/atp_selections.
Then I created in the above directory the create.js.erb and destroy.js.erb files. Below is the content of the create.js.erb file:
$("#atp_count").html('<%= current_user.atp_ranks.count %> selected');
$("#atpenlist_form_<%= #tennist.id %>").html("<%= escape_javascript(render('atp_selections/atpdiscard')) %>");
The jQuery code should manipulate the atp_count id and the atpenlist_form_<%= #tennist.id %> id, that is the id of the fourth th tag, and the id of the div containing the button's forms.
Below is an extract of my atp_selections controller, which is too long to report it entirely:
def create
#tennist = AtpRank.find(params[:atp_id])
rankings = current_user.atp_ranks.pluck(:ranking)
atp_selections = current_user.atp_selections
wta_selections = current_user.wta_selections
if atp_selections.count <= 15 && wta_selections.count < 16
if (1..5).include?(#tennist.ranking) && (rankings & (1..5).to_a).size == 0
current_user.atpenlist(#tennist)
respond_to do |format|
format.html { redirect_to root_url }
format.js
end
elsif (6..15).include?(#tennist.ranking) && (rankings & (6..15).to_a).size < 3
current_user.atpenlist(#tennist)
respond_to do |format|
format.html { redirect_to root_url }
format.js
end
...
As you can see, the create action of the atp_selections controller is made of multiple if-else statements that respond to enlistment rules. However, what is important here is that in each condition I included the required code with the respond_to method for requests to be handled by Ajax.
However the controller does not respond to Ajax, and changes to atp_count and atpenlist_form_<%= #tennist.id %> are visible only after a page refresh.
The rails console reports the following error:
Rendered atp_selections/create.js.erb (223.6ms)
Completed 500 Internal Server Error in 695ms (ActiveRecord: 83.2ms)
ActionView::Template::Error (undefined local variable or method `tennis_player' for #<#<Class:0x00000005948748>:0x0000000593f648>):
1: <%= form_for(current_user.atp_selections.find_by(atp_rank_id: tennis_player.id),
2: html: { method: :delete }, remote: true) do |f| %>
3: <%= f.submit "Dump", class: "btn btn-warning btn-sm" %>
4: <% end %>
app/views/atp_selections/_atpdiscard.html.erb:1:in `_app_views_atp_selections__atpdiscard_html_erb__451019467450256030_46643760'
app/views/atp_selections/create.js.erb:2:in `_app_views_atp_selections_create_js_erb__4477173780394533370_46811020'
tennis_player is the variable of the iteration and it seems as if it is not accepted when it is imported from a rendered partial.
When you call the same partial from within the js.erb file, tennis_player is not declared, and so you get the error undefined variable.
So you need to pass #tennist as tennis_player inside your partial from create.js.erb:
$("#atpenlist_form_<%= #tennist.id %>").html("<%= j render 'atp_selections/atpdiscard', locals: {tennis_player: #tennist} %>");
I´m very new to ROR and I have stumbled up on a problem. I believe it´s a newbie problem, but I´m not sure if it´s. I´m building this app were users should be able to enter the amount of paper use in their business and from there the app calculates their eco footprint so to say.
at login the user enters their staff number. The staff number is used to divide with paper_weight etc. ( see in view example).
I know it basically works the way it is now, but I´m aware of this is not the best practice.
I also wanted to move the div containing the running totals to application.html.erb but I can´t do that since my calculations are in the View but not in the Model.
here is a link to the github repo https://github.com/DadiHall/SprettaEMS1.5
Are there any other ways to do this? Can someone please advise me?
my index.html.erb view
<div class="row">
<div class="col-md-10 ">
<h1>Pappírsnotkun</h1>
<table class=" well table table-hover">
<thead>
<tr>
<th>Dags</th>
<th>Tegund</th>
<th>Þyngd</th>
<th>Kostnaður</th>
<th>Þar af umhvm. pappír</th>
<th>Pappírs magn m.v. Stöðugildi</th>
<th>Hl.f. Umhvm. Pappírs</th>
<th>Fjöldi trjáa m.v. magn pappírs</th>
</tr>
</thead>
<tbody>
<% paper_total_cost = 0 %>
<% paper_total_trees = 0 %>
<% paper_total_staff_ratio = 0 %>
<% #papers.each do |paper| %>
<tr>
<td><%= paper.date.strftime("%Y/%m/%d") %></td>
<td><%= paper.paper_type %></td>
<td><%= paper.paper_weight %> -kg.</td>
<td><%= paper.paper_cost %> -kr.</td>
<td><%= paper.env_paper_weight %> -kg.</td>
<td><%= (paper.paper_weight.to_i / current_user.staff) %> kg/stöðugildi </td>
<td><%= (( paper.env_paper_weight / paper.paper_weight)* 100) %>%</td>
<td><%= (( paper.paper_weight.to_f/1000)*15) %></td>
<td><%= link_to 'Sýna', paper, class: 'btn btn-xs btn-info' %></td>
<td><%= link_to 'Uppfæra', edit_paper_path(paper), class: 'btn btn-xs btn-warning' %></td>
<td><%= link_to 'Eyða', paper, method: :delete, class: 'btn btn-xs btn-danger', data: { confirm: 'Are you sure?' } %></td>
<% paper_total_cost = paper_total_cost + (paper.paper_cost.to_i) %>
<% paper_total_trees = paper_total_trees + (( paper.paper_weight.to_f/1000)*15) %>
<% paper_total_staff_ratio = paper_total_staff_ratio + (paper.paper_weight.to_i / current_user.staff) %>
</tr>
<% end %>
</tbody>
</table>
</div>
<div class="row">
<div class=" col-md-2 panel panel-default pull-right" >
<div class="panel panel-heading ">
<h5 class="user-name">User: <% if current_user && current_user %>
<%= current_user.name %>,
Staff: <%= current_user.staff %>
<% end %>
</h5>
</div>
<div class="panel-body">
<h6 class="pull-right">Pappír Kostnaður Samtals:<%= number_with_precision(paper_total_cost.to_f, precision: 2) %> kr </h6><br>
<h6 class="pull-right">Fjöldi Trjáa vegna Pappírs notkunar:<%= number_with_precision(paper_total_trees.to_f, precision: 2) %> Tré </h6><br>
<h6 class="pull-right">Magn Pappírs f. hvert stöðugildi:<%= number_with_precision(paper_total_staff_ratio.to_f, precision: 2) %> Kg </h6>
</div>
</div>
</div>
<%= link_to 'New Paper', new_paper_path %>
My papers_controller.rb
class PapersController < ApplicationController
before_action :set_paper, only: [:edit, :update, :show, :destroy]
def index
#papers = Paper.all
end
def create
#paper = Paper.new(paper_params)
if #paper.save
flash[:success] = "Messages sent."
redirect_to paper_path(#paper)
else
flash[:danger] = "Error occured, message has not been sent."
redirect_to new_paper_path
end
end
def new
#paper = Paper.new
end
def edit
end
def show
end
def update
if #paper.update(paper_params)
flash[:success] = "Line was successfully updated"
redirect_to papers_path(#paper)
else
render 'edit'
end
end
def destroy
#paper.destroy
flash[:danger] = "Line was successfully destroyed"
redirect_to papers_path
end
private
def set_paper
#paper = Paper.find(params[:id])
end
def paper_params
params.require(:paper).permit(:paper_type, :date, :paper_weight, :paper_cost, :env_paper_weight)
end
end
my paper.rb Model
class Paper < ActiveRecord::Base
has_many :users
end
You can move the calculations into the model by defining functions in the model.
The first calculation uses current_user, which will not be in the model by default, so that'll have to be a parameter. The other two are just using paper's attributes; pardon my poor naming, I'm not clear what the second two metrics are.
class Paper
has_many :users
def paper_weight_per_capita(current_user)
paper_weight.to_i / current_user.staff
end
def paper_weight_metric1
( env_paper_weight / paper_weight)* 100)
end
def paper_weight_metric2
(( paper_weight.to_f/1000)*15)
end
end
And in your view
<%= paper.paper_weight_per_capita(current_user) %>
<%= paper.paper_weight_metric1 %>
If you want the running total in your layout then you'll need to load the paper objects on every page. You can do this with an application_controller before_action, though some folks don't like this approach and here, but I think it is a reasonable way for you now.
class ApplicationController < ActionController::Base
before_action :load_running_totals
def load_running_totals
# method 1. Similar to how the calculation is done in the view.
# #paper_weight_per_capita = Papers.all.map(&:paper_weight_per_capita, current_user).reduce(&:+)
# method 2. does the summation in the database without having to load all the records into ruby. (weight needs to be a number in the db.)
#paper_weight_per_capita = Papers.sum(:weight) / current_user.staff
end
end
Now in any view (including your layout):
<%= #paper_weight_per_capita %>
So, basically, I have Features nested within Elements which is nested within Apps. The Apps show page contains a Table of Elements, and each Element has a table of Features. I know that this will look a bit messy, but I'm more worried about functionality right now, and I'll clean up the HTML later.
Full error:
Showing ~/app/views/apps/show.html.erb where line #41 raised:
No route matches {:action=>"new", :controller=>"apps/elements/features",
:element_id=>nil} missing required keys: [:element_id]
My FeaturesController (only a few methods shown for the sake of space)
class Elements::FeaturesController < ApplicationController
before_action :set_feature, only: [:show, :edit, :update, :destroy]
# GET /features
# GET /features.json
def index
#features = Feature.all
respond_with(#features)
end
# GET /features/1
# GET /features/1.json
def show
respond_with(#element.features)
end
# GET /features/new
def new
#element = Element.find(params[:element_id])
#feature.element = #element
#feature = Feature.new
end
# GET /features/1/edit
def edit
end
# POST /features
# POST /features.json
def create
#element = Element.find(params[:element_id])
#feature = Feature.new(feature_params)
#feature.element = #element
#feature.save
if #feature.save
flash[:notice] = "#{#feature.name} purchase was added to the #{#feature.element.name}."
redirect_to(#feature)
else
flash[:error] = "There was a problem adding the purchase."
render :show
end
end
My show.html.erb for Apps:
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= #app.name %>
</p>
<p>
<strong>Infivewords:</strong>
<%= #app.infivewords %>
</p>
<p>
<strong>Description:</strong>
<%= #app.description %>
</p>
<div class="media">
<div class="media-body">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Delete</th>
<tr>
</thead>
<tbody>
<% #app.elements.each do |element| %>
<tr>
<td><h4><%= element.name %></h4><br><h4><%= element.description %></h4></td>
<td>
<% element.features.each do |feature| %>
<td><h4><%= feature.name %></h4><h4><%= feature.description %></h4>
</td>
<% end %>
<td><h4><%= link_to "Delete", [#app, element], method: :delete, confirm: "Are you sure?" %></h4></td>
<td><%= link_to 'Add a Feature', new_element_feature_path(#element), class: 'pull-right' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<br>
<br>
<%= link_to 'Add an Element', new_app_element_path(#app) %>
<br>
<%= link_to 'Edit', edit_app_path(#app) %> |
<%= link_to 'Back', apps_path %>
Routes:
Rails.application.routes.draw do
resources :apps do
resources :elements, controller: 'apps/elements'
end
resources :elements do
resources :features, except: [:index], controller: 'apps/elements/features'
end
root to: 'apps#index'
Please excuse the mess. I'm new to rails and I've been tearing my code apart trying to figure this out. Please let me know if I should post any more code. Thank you in advance!
try changing the path inside
<td><%= link_to 'Add a Feature', new_element_feature_path(#element), class: 'pull-right' %></td>
to
new_element_feature_path(element)
since you are still looping #app.elements, right?
Solution:
In view, changed params to #element.feature:
<%= link_to 'Add a Feature', new_app_element_feature_path(#element.feature), class: 'pull-right' %>
I am creating a simple restaurant ordering system with a one to many relationship between a menu and its items. One menu has many items. For the sake of simplicity it is not a many to many.
I am able to create menus fine, but would like to be able to add and show the menu items for that menu in the menus show action.
The menu show action displays okay but when I try to add a new menu item I get the following error:
ActiveRecord::RecordNotFound in ItemsController#create
Couldn't find Menu with 'id'=
raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
And here is the queries from the terminal:
Started POST "/items" for ::1 at 2015-01-11 16:09:44 +0000
Processing by ItemsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"***", "item"=>{"name"=>"Test", "price"=>"23", "course"=>"Main", "vegetarian"=>"1", "allergy"=>""}, "commit"=>"Add item"}
Menu Load (0.3ms) SELECT `menus`.* FROM `menus` WHERE `menus`.`id` = NULL LIMIT 1
Completed 404 Not Found in 8ms
EDIT: I followed doon's advice and looked into nested resources and this is indeed a better way of doing it. The updated code is below:
routes.rb
resources :menus do
resources :items
end
menus_controller.rb
def show
#menu = Menu.find(params[:id])
#items = #menu.items
end
items_controller.rb
def create
#menu = Menu.find(params[:menu_id])
#item = #menu.items.create!(item_params)
if #item.save
flash[:success] = "Item added!"
redirect_to #menu
else
flash[:danger] = "Errors found!"
redirect_to #menu
end
end
private
def item_params
params.require(:item).permit(:name, :price, :course, :vegetarian, :allergy, :menu_id)
end
And the Menus
show.html.erb
<%= link_to "<< Back", menus_path, data: { confirm: back_message } %>
<h1><%= #menu.name %> menu</h1>
<center><button id="toggleButton" class="btn btn-sm btn-info">Show/Hide Add Item Form</button></center>
<br>
<div class="row">
<div class="col-xs-offset-3 col-xs-6 toggleDiv hideDiv">
<%= form_for [#menu, Item.new] do |f| %>
<table class="table table-condensed table-no-border">
<tr>
<th scope="row" class="col-xs-2">Name:</th>
<td class="col-xs-10"><%= f.text_field :name %></td>
</tr>
<tr>
<th scope="row">Price:</th>
<td><%= f.text_field :price %></td>
</tr>
<tr>
<th scope="row">Course:</th>
<td><%= f.select(:course, options_for_select([['Starter', 'Starter'], ['Main', 'Main'], ['Dessert', 'Dessert'], ['Drink', 'Drink']]), prompt: "Please select...", class: 'form-control') %></td>
</tr>
<tr>
<th scope="row">Vegetarian:</th>
<td><%= f.check_box :vegetarian %></td>
</tr>
<tr>
<th scope="row">Allergy:</th>
<td><%= f.text_field :allergy %></td>
</tr>
<tr><td colspan="2"><%= f.submit "Add item", class: "btn btn-sm btn-success col-xs-offset-4 col-xs-4" %></td></tr>
</table>
<% end %>
</div>
</div>
<table class="table table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Course</th>
<th>Vegetarian</th>
<th>Allergy</th>
</tr>
</thead>
<tbody>
<% #items.each do |item| %>
<tr>
<td><%= item.name %></td>
<td><%= number_to_currency(item.price, unit: "£") %></td>
<td><%= item.course %></td>
<td><%= item.vegetarian %></td>
<td><%= item.allergy %></td>
</tr>
<% end %>
</tbody>
</table>
What I would probably do in a case like this (especially if your item doesn't exist outside of a menu) is to use Nested Resources:
http://guides.rubyonrails.org/routing.html#nested-resources
http://railscasts.com/episodes/139-nested-resources (a bit dated, but still a decent grounding)
Below are some changes to get you pointed in this direction.
config/routes.rb
resources :menu do
resources :items
end
now our urls will look like
/menu/:menu_id/items/
So we need to adjust the items_controller to get the menu by looking at :menu_id, and we don't need the hidden field anymore. I put it in a before_action as every method in the controller will build through the association.
items_controller.rb
class ItemsController < ApplicationController
before_action :find_menu
...
def create
#item = #menu.items.new(item_params)
if #item.save
redirect_to #menu, notice: 'item added'
else
redirect_to #menu, warning: 'item failed'
end
end
...
private
def find_menu
#menu = Menu.find(params[:menu_id])
end
end
if you want to show it on the menu, we need a new Item to display.
menus_controller.rb
def show
#menu = Menu.find(params[:id])
#item = #menu.items.new
end
Then we need to make use of the nested resource in the menus/show view. By passing in the array of the menu and the item, rails will generate the correct path.
menus/show.html.erb
<%= form_for [#menu,#item] do |f| %>
Are you posting to "/post" or "post/[:id]"?
#menu = Menu.find(params[:id])
Will not find anything if you do not pass it an id. The id can come from the URL params, but this means you should be sending your request to "/post/[:menuid].
You can use the gem https://github.com/charliesome/better_errors to debug your program at the controller level and execute rails console there. just put in "fail" in your controller and then you will have a command line interface where you can inspect your params value and ensure it has the id included and play with the console there.
I have now solved it. Thanks to better_errors I was able to better understand what was going on.
I implemented the show action in menus_controller.rb from aaron.
def show
#item = Item.new
#menu = Menu.includes(:items).find(params[:id])
end
I needed a way to pass the menu id into the menu_id field, so I added a hidden field in the table or show.html.erb passing the current #menu.id into the :menu_id field.
<%= f.hidden_field :menu_id, :value => #menu.id %>
I have read online that passing values through hidden fields is not a great idea though.
Then in the create action of the Items Controller I was able to make a new record as usual redirecting to the existing show view using the menu id from the item object.
def create
#item = Item.new(item_params)
if #item.save
redirect_to menu_path(#item.menu_id)
else
redirect_to menu_path(#menu.menu_id)
end
end
It feels like a bit of a hack so open to suggestions on how to improve.
EDIT: The first question is my premise itself. Is rails/html SUPPOSED to generate a "Create Subject" button without me explicitly asking it to?
So here is the controller that is working with the view
class SubjectsController < ApplicationController
def index
list
render('list')
end
def list
#subjects = Subject.order("subjects.position ASC")
end
def show
#subject = Subject.find(params[:id])
end
def new
#subject = Subject.new(:name => 'default')
end
def create
#instantiate a new object using form params
#subject = Subject.new(params[:subject])
#save the subject
if #subject.save
#if save succeeds redirect to list action
else
#if save fails, redisplay form
render('new')
end
end
end
And here is the misbehaving view (html.erb) file which isn't generating my button
<%= link_to("<< Back to List", {:action => 'list'}, :class => 'back-link') %>
<div class="subject new">
<h2>Create Subject</h2>
<%= form_for(:subject, :url => {:action => 'create'}) do |f| %>
<table summary="Subject form fields">
<tr>
<th>Name</th>
<td><%= f.text_field(:name) %></td>
</tr>
<tr>
<th>Position</th>
<td><%= f.text_field(:position) %></td>
</tr>
<tr>
<th>Visible</th>
<td><%= f.text_field(:visible) %></td>
</tr>
</table>
<% end %>
</div>
Currently, the output on the browser is:
'<< Back to List' (link)
<h2>Create Subject</h2>
Name [blank-form]
Position [blank-form]
Visible [blank-form]
[missing button location]
There is supposed to (according to lynda.com) be a button which says "Create Subject" in the missing button location, but it's not there.
Nothing in your code is supposed to generate a button.
You'll need to add:
<%= f.submit 'Create Subject' %>
inside the form. Maybe between </table> and <% end %>