I want to pass the current project's id to the tickets controller (creating a ticket for the project), which I try to do below. However, the way I do it below gives me the following link:
tickets/new?project_id=8
...when I just want it to be this way:
tickets/new
...even though I want the project_id to be accessible in the controller.
How can I do this? Just to clearify: I don't want the project_id to be a part of the URL, I just want to pass it (in some way) as a parameter to the controller.
from view:
<h1><%= #project.title %></h1> <-- the project's attributes is reachable here
<%= link_to "Create ticket", new_ticket_path(:project_id => #project.id), :class => "btn edit_button" %>
tickets controller:
1. class TicketsController < ApplicationController
2. def new
3. #ticket = Ticket.new
4. #id = params[:project_id]
5.
6. #project = Project.find(#id)
7. end
8. end
The route that link_to points to looks like the following:
new_ticket GET /tickets/new(.:format) tickets#new
If the tickets belong to the project, you might want to consider nesting resources.
Right now, your routes probably look like this:
resources :projects
resources :tickets
This generates routes like /projects/new and /tickets/new. What you can do instead is this:
resources :projects do
resources :tickets
end
This will give you routes like /projects/8/tickets/new. You link to a new ticket by doing new_project_ticket_path(#project). The actual form of the route generate is: /projects/:project_id/tickets/:id. So params[:project_id] will give you the id of the project.
Related
I need to capture a field added by a user in a form_for, inside the product show page.
My product.rb model as follows:
belongs_to :user
has_many :complaints
My complaint.rb model as follows:
belongs_to :product
belongs_to :user
My user.rb model as follows:
has_many :products
My product controller is a basic controller with all the new, create, edit, update actions and all the routes are good.
User looks at the product show page like this, and it's all good
http://localhost:3000/products/1
My goal is to create a complaint from the product show page, when user views the specific product. So I have created a complaints_controller.rb to capture all the details of the product, and create a complaint. I have an issue with capturing the complaint_number which is a field inside the complaints table.
Here is my form inside the product show page
<%= form_for([#product, #product.complaints.new]) do |f| %>
<%= f.number_field :complaint_number, placeholder: "Enter complaint number you were given" %>
<%= f.submit 'Complaint' %>
<% end %>
Here is my complaints_controller.rb
Goal is to capture the complaint_number fields and run the make_complaint method to create a complaint and populate rest of the fields in the newly created row of the complains table.
class ComplaintsController < ApplicationController
before_action :authenticate_user!
def create
# Will Get product_id from the action in the form in product show page.
product = Product.find(params[:product_id])
# This complaint_number does not seem to work
complaint_number = product.complaints.find_by(complaint_number: params[:complaint_number])
# Now I want to run a make_complaint method and pass the product and the complaint number. This fails, I can't capture the complaint_number in the form from user input.
make_complaint(product, complaint_number)
redirect_to request.referrer
end
private
def make_complaint(product, complaint_number)
complaint = product.complaints.new
complaint.title = product.title
complaint.owner_name = product.user.name
complaint.owner_id = product.user.id
# Note: complaint_number and current_complaint are a fields in the Orders table
# Note:
complaint.current_complaint = complaint_number
if complaint.save
flash[:notice] = "Your complaint has been sent!"
else
flash[:alert] = complaint.errors.full_messages
end
end
end
For routes I have added resources :complaint, only: [:create] inside the resources of products to get products/:id/complaints
My routes.rb is like this
Rails.application.routes.draw do
get 'products/new'
get 'products/create'
get 'products/edit'
get 'products/update'
get 'products/show'
root 'pages#home'
get '/users/:id', to: 'users#show'
post '/users/edit', to: 'users#update'
resources :products do
member do
delete :remove_image
post :upload_image
end
resources :complaint, only: [:create]
end
devise_for :users, path: '', path_names: { sign_in: 'login', sign_up: 'register', sign_out: 'logout', edit: 'profile' }
Your form has complaint_quantity:
<%= form_for([#product, #product.complaints.new]) do |f| %>
<%= f.number_field :complaint_quantity, placeholder: "Enter complaint number you were given" %>
<%= f.submit 'Complaint' %>
<% end %>
Your controller has complaint_number:
complaint_number = product.complaints.find_by(complaint_number: params[:complaint_number])
If you check your params from the server log, I bet you'll see the value you are looking for is coming across as complaint_quantity and not complaint_number.
UPDATE
With the form misspelling corrected, the error persists, so let's check into more areas:
complaint_number = product.complaints.find_by(complaint_number: params[:complaint_number])
So, break that down:
1. What does params actually include?
Is :complaint_number being submitted from the form?
If not, the form still has an error somewhere.
2. Does product.complaints actually include a complaint that could be matched by complaint_number?
I don't know your data structure well enough to tell, but it looks to me like you might actually want to do:
Complaint.find_by(complaint_number: params[:complaint_number])
instead of:
products.complaints.find_by(complaint_number: params[:complaint_number])
UPDATE #2
You know the problem is with your params.
I'm confident you aren't accessing your params correctly since you are using a nested form:
form_for([#product, #product.complaints.new])
Should mean your params are structured like { product: { complaint: { complaint_number: 1234 }}}
So params[: complaint_number] is nil because it should really be something like params[:product][:complaint][:complaint_number]
Please look at your server log in your terminal right after you submit the form to see the structure of your params. Or insert a debugger in the controller action and see what params returns.
ALSO, Instead of accessing params directly, you should whitelist params as a private method in your controller.
Something along these lines:
private
def product_complaint_params
params.require(:product).permit(:id, complaint_params: [ :complaint_number ])
end
See this: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html
I'm new to rails, and I've already learnt how to do CRUD using scaffold and using resource, I wanna know how to Do CRUD without using resource, However what I want to do is create custom methods for CRUD in the controller that will be like the traditional CRUD model. Please help me with this.
Actually, for the action index / new / show / create / edit / update / destroy, this is a convention in Ruby On Rails
If I'm right and if you're trying to change their name using resources in routes.rb (Changing by exemple the new action into def my_restaurant), Rails will render
Unknown action The action 'new' could not be found for
RestaurantsController
Netherless, you can create some methods to perform some particular action into the index, and add them in the "private section" you want to hide them to the public. There is no routes attach to this method.
class RestaurantsController < ApplicationController
def index
#restautants = Restaurant.all
#restaurants.sort_by_name
end
private
def sort_by_name
#some action here
end
end
If you want to create your own name method, you can personnalise it but you can't use resources "shortcut" in the routes.rb
by exemple :
#reviews_controller.rb
class ReviewsController < AplicationController
def index
#reviews = Reviews.all
end
def update
#review = Review.find(review_params[:id])
end
def history
#some action
end
private
def review_params
params.require(:review).permit(:liked, :comment, :id)
end
end
Then add a view
#app/views/reviews/history.html.erb
Don't forget the routes :
Rails.application.routes.draw do
resources :reviews, only: [:index, :update] do
collection do
get :history
end
end
end
I hope this will help you and complete the previous answer.
as for your second question :
I still do have one doubt tho..... Will <%= form_for #post do |f| %>
this form be enough for new_post and edit_post, will it automatically
identify them? If that's not enough can you please tell me the what
comes at new_post.html.erb and edit_post.html.erb....Thanks again for
the help.
If the form is the same for your new_post and edit_post, i may suggest you to put into a partial file. (For this example I used simple_form gem)
#app/views/posts/_form.html.erb
<%= simple_form_for(#post) do |f| %>
<%= f.input :name, label: "post name" %>
<%= f.input :photo, as: :file %>
<%= f.submit "Save", class:"btn btn-small btn-success" %>
<% end %>
and then render the partial in your views new file.
#app/views/posts/new.html.erb
<div>
<h1>New Post</h1>
</div>
<%= render "form" %>
Well I hope I could help you with this answer.
Do not hesitate too to read ruby documention. You may find more information that you're looking for too.
My answer may be redundant but it's the better way for me to clearly explain it...
In oder to use your own custom methods you need to create them in your controller, setup the route and if needed create an view.
# PostsController
def create_post
# Do your create stuff here
end
def read_post
# Do your read stuff here
end
def update_post
# Do your update stuff here
end
def delete_post
# Do your delete stuff here
end
# routes
post '/create_post', to: 'posts#create_post'
get '/read_post/:id', to: 'posts#read_post'
put '/update_post/:id', to: 'posts#update_post'
delete 'delete_post/:id', to: 'posts#delete_post'
With the controller and routes setup you will only need a view for the read_post method. The best way to do that is create the file: views/posts/read_post.html.erb
There is 7 CRUD routes to Create, Read, Update and Delete.
Rails.application.routes.draw do
get "restaurants", to: "restaurants#index"
get "restaurants/new", to: "restaurants#new", as: :new_restaurant
post "restaurants", to: "restaurants#create"
# NB: The `show` route needs to be *after* `new` route.
get "restaurants/:id", to: "restaurants#show", as: :restaurant
get "restaurants/:id/edit", to: "restaurants#edit", as: :edit_restaurant
patch "restaurants/:id", to: "restaurants#update"
delete "restaurants/:id", to: "restaurants#destroy"
end
So once the route create, you can create in the controller, the action that you need
class RestaurantsController < ApplicationController
def index
#restaurants = Restaurant.all
end
end
and the view
app/views/restaurants/index.html.erb.
The best practice is to create only the routes that you will need and to work in silos.
1 route, 1 controller action, 1 view.
I'm new to rails. I have a table TeamMemberships that has a foreign key to Students and a foreign key to Teams. I want to be able to have a link in the show.html.erb page for a specific team that redirects to a form that lets me add a row to TeamMemberships in which the Team foreign key is the team whose show.html.erb page the link was clicked on and the Student foreign key would be determined by the form. Is this possible?
Thanks!
Add a new resources in your routes.rb file like so:
Rails.application.routes.draw do
resources :teams do
resources :team_memberships
end
end
Which will create the CRUD routes:
team_team_memberships GET /teams/:team_id/team_memberships(.:format) team_memberships#index
POST /teams/:team_id/team_memberships(.:format) team_memberships#create
new_team_team_membership GET /teams/:team_id/team_memberships/new(.:format) team_memberships#new
edit_team_team_membership GET /teams/:team_id/team_memberships/:id/edit(.:format) team_memberships#edit
team_team_membership GET /teams/:team_id/team_memberships/:id(.:format) team_memberships#show
PATCH /teams/:team_id/team_memberships/:id(.:format) team_memberships#update
PUT /teams/:team_id/team_memberships/:id(.:format) team_memberships#update
DELETE /teams/:team_id/team_memberships/:id(.:format) team_memberships#destroy
What is interesting you here is the new_team_team_membership GET /teams/:team_id/team_memberships/new(.:format) that you can call using the named route new_team_team_membership.
You will be able to pass the team ID for which you'd like to create the TeamMembership in the route with new_team_team_membership_path(#team.id).\
Regarding the student_id you will pass it in the HTTP POST request body.
Here is how to create the link from the team show page to the TeamMembership form:
<%= link_to 'Add a student', new_team_team_membership_path(#team.id) %>
You will have to create the TeamMembershipsController and the new action. The action should initialise the #new_team_membership variable that will be used by the form and load the student list like so:
class TeamMembershipsController < ApplicationController
def new
#new_team_membership = TeamMembership.new(team_id: params[:team_id])
#students = Student.all
end
end
Then you have to create the app/views/team_memberships/ folder and add the new.html.erb file with the following form:
<%= form_for #new_team_membership do |f| %>
<%= f.select :student_id, options_for_select(#students.collect{ |student| [student.name, student.id]) %>
<%= f.submit %>
<% end %>
Then in your TeamMembershipsController controller, you have to define the create action that will receive in parameter the team_id, and from the form the selected student_id. You will then be able to create the TeamMembership instance.
Update
Regarding the create action of the TeamMembershipsController controller, here is how it should be done:
class TeamMembershipsController < ApplicationController
def new
# ...
end
def create
# The form should have embedded all the params within a key with the model's name
# like `:team_membership`.
student = Student.find(params[:team_membership][:student_id])
#team_membership = TeamMembership.new(
team_id: params[:team_id],
student: student
)
if #team_membership.save
redirect_to :team_path(params[:team_id])
else
# Reload again the student as we will show errors.
# The student dropdown would be empty.
#students = Student.all
# Render again the new form
render :new
end
end
end
I am doing a simple CR and trying to put some link/button(add,edit etc), when the user will click the add it will direct to that view..
Question: How should I indicate which view will be load when I click the specific link/button?
Index.html.erb
<h1>My First CRUD!</h1>
<%= link_to "Add Page", posts_path %>
<%= link_to "Edit Page", posts_path %>
<%= link_to "Showx Page", posts_path %>
Posts Controller
class PostsController < ApplicationController
def index
end
def addItem
end
def create
#post = Post.new(post_params)
#post.save
redirect_to #post
end
def show
#post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:item, :description)
end
end
Routes
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :posts
root "posts#index"
resources :posts
root "posts#addItem"
You can declare all the root routes that you want in your routes.rb, but it'll take just the one that was declared first, it doesn't matter if the definition has some mistake, it won't tell you.
And the route where the link_to will redirect is what you declare as second argument (in this case).
In your example you have three link_totags, which are pointing to the same path. If you'd like to make the "Edit" link_toredirect to the posts#edit method you firstly must create the edit method in your posts_controller and then add it to your route as:
get '/posts/edit/:id', to: 'posts#edit'
But in your case as you have resources :posts it makes the whole work for you and creates all the routes defined in the posts#controller, what resources do is to map a number of related requests to actions in a single controller, that's to say;
When Rails application receives a request for:
GET /posts/edit/1
That's what happen when you go to edit the a certain post by passing the id of 1, then it asks the router to map it to a controller action. If the first matching route is:
resources :posts
Then Rails dispatchs that request to the edit action on the posts controller with { id: '1' } in params.
Short answer, to create a common <a> tag create a link_to, add a text to specify to where will this redirect, then the route that you've defined, you can check them by running rails routes if you're using Rails 5 or bin/rake routes if you're with Rails < 5.
I'm viewing 1 product in show.html.erb and there's a link below that says "View other products from this company". This link_to connects to another non-restful action in same controller which retrieves from DB other products of same company as was shown in show.html.erb.
Can link_to pass the :id of the current product in show to action it's rendering? I'm new to rails and please let me know if question is not making sense. I'm not sure if routes need to be defined as well. Thanks.
products_controller.rb
def show
#company_products = Product.by_company
end
show.html.erb
<%= link_to "View other products from this company", company_products_path(:anchor => "#{#company_products}") %>
routes.rb
get '/company_products_' => 'products#company_products'
I finally resolved it by passing the :id of object in show via link_to to a non-restful action.
I'm open to suggestions if entire #company_products in #show can be passed as it is because I'm first finding if there are any other products for that company and if there are, passing an id only in link_to and in controller#company again running a query to get same data of all products to display. so running same query twice is not DRY.
controller#show remains the same as originally posted.
routes.rb
resources :products do
get :company, on: :member
end
show.html.erb
<%= link_to "View other products from #{#company_name}", company_product_path(#product.company_id) %>
controller#company
def company
#products_of_company = Product.where(company_id: params[:id])
end
Now in company.html.erb, the list is just displayed.
You want to do something like this:
#company_products.each do |company|
link_to "View other products from this company", products_path(company)
end
routes:
resources :products