Elixir/Phoenix calling update action from nested - post

I have orders and order_items controllers and models generated automatically.
My router looks like this:
resources "/orders", OrderController do
resources "/order_items", OrderItemController
end
On the order show.html page I'm calling different order_item actions. I can create and delete order_item associating it with order:
<%= render Pos1.OrderItemView, "item_quantity.html", changeset: #order_item_changeset, action: order_order_item_path(#conn, :create, #order) %>
<%= link "delete", to: order_order_item_path(#conn, :delete, #order, #order_item), method: :delete %>
However if i try to call update action (not from edit.html of order_item, but show.html of order, like create/delete above) it returns following error:
no route found for POST /orders/53/order_items/62 (Pos1.Router)
Well, I understand that the method should be PUT (for update), but if I use link to use method: "put"
<%= link "+", to: order_order_item_path(#conn, :update, #order, #order_item), method: "put" %>
Still can't make it work. It seems weird to me that I can call create and delete, but not update action from the show.html of the order. What's more, I can call update from order_item edit.html page and it works perfectly. What am i doing wrong? Have been struggling for a while with this.
Thanks in advance!
Edit: My update action from order_item:
def update(conn, %{"id" => id}) do
order_item = Repo.get!(OrderItem, id)
order = Repo.get!(Order, order_item.order_id)
changeset = OrderItem.changeset(order_item, %{quantity: order_item.quantity + 1})
case Repo.update(changeset) do
{:ok, order_item} ->
conn
|> put_flash(:info, "Order item quantity increased successfully.")
|> redirect(to: table_order_path(conn, :show, order.table, order))
{:error, _changeset} ->
conn
|> put_flash(:error, "Failed to increase order item!")
|> redirect(to: table_order_path(conn, :show, order.table, order))
end
end
Error:
Order_item.html.eex:
<tr>
<td><%= #order_item.food.name %></td>
<td><%= #order_item.quantity %></td>
<td><%= link "+", to: order_order_item_path(#conn, :update, #order_item), method: :put %></td>
<td><%= link "-", to: order_order_item_path(#conn, :delete, #order, #order_item), method: :delete, class: "btn btn-danger btn-xs" %> </td>
</tr>

You're missing an argument in the call to order_order_item_path.
This:
<td><%= link "+", to: order_order_item_path(#conn, :update, #order_item), method: :put %></td>
should be:
<td><%= link "+", to: order_order_item_path(#conn, :update, #order, #order_item), method: :put %></td>

Related

Passing parameters using link_to

I'm trying to pass parameters using link_to with ruby on rails, but it says the id parameter I'm sending is null.
code from where I'm sending the id.
<% #conference.papers.each do |paper| %>
<tr>
<td><%= paper.title %></td>
<td><%= paper.author %></td>
<td><%= link_to "Download Paper", paper.attachment_url %></td>
<td><%= link_to 'Reviews', paper %></td>
<% if (paper.accepted) %>
<td><%= "Accepted" %></td>
<% else %>
<td><%= "Not accepted" %></td>
<% end %>
<% if (#state1 && paper.accepted == false) %>
<td><%= button_to "Accept", accept_paper_path(id: paper.id), class: "btn btn-danger", data: { confirm: "Are you sure that you wish to accept #{paper.title}?"} %></td>
<% end %>
<% if (#state2) %>
<% session["a"] = paper.id %>
<td><%= link_to "Review paper", new_review_path(id: paper) %></td>
<% end %>
</tr>
<% end %>
code for the review controller
def new
#paper = Paper.find_by_id(params[:id])
#review = Review.new()
end
You missed .id in
link_to "Review paper", new_review_path(id: paper.id)
But it is not a good solution. If your Paper model has_many :reviews, it would be better to nest reviews routes in paper's ones. Like this:
# config/routes.rb
resources :papers do
resources :reviews
end
And so, your link_to will look like:
link_to "Review paper", new_paper_review_path(paper)
which will generate
/papers/:paper_id/reviews/new
You can learn more about Rails routing here.
Lets start by setting up the routes properly:
resouces :papers do
member do
patch :accept
end
end
This will let you accept a review by PATCH /papers/:id. To create the button use:
<%= button_to accept_paper_path(paper), method: :patch %>
Note that this should use the PATCH or PUT http method - not GET since it is a non-idempotent action.
Note that you can just pass the model instead of doing accept_paper_path(id: model) or accept_paper_path(id: model.id).
For reviews you will want to create what is called a nested resource:
resouces :papers do
member do
patch :accept
end
resources :reviews, only: [:new, :create]
end
This gives you the route /papers/:paper_id/reviews/new.
<%= link_to "Review paper", new_paper_review_path(paper) %>
To set the form to create a new review to the use correct path use an array containing the parent and child:
<%= form_for([#paper, #review]) %>

Nesting under the Devise in Rails4

I have a problem with Controller Show Product. Can anyone help me out?
No route matches {:action=>"show", :controller=>"products", :id=>nil, :vendor_id=>"3"} missing required keys: [:id]
<td>
<%= link_to 'Show', vendor_product_path(current_vendor, #product) %><br>
<%= link_to 'Edit', edit_product_path(product) %><br>
<%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %>
</td>
my controller in product
def show
#product = Product.find(params[:vendor_id])
end
If you're trying to find all the products for a specific vendor, it would be something like:
def show
#vendor_products = Vendor.find(params[:vendor_id]).products
end
If you are just trying to find a single product by id:
def show
# Since :id is currently nil in params, this will not work
#product = Product.find(params[:id])
end
In the view, where are you getting #product from? Seems like #product is not yet saved to the database, and so it is nil in the view (in the line <%= link_to 'Show', vendor_product_path(current_vendor, #product) %>. Which view file have you shown, and what is the corresponding controller action?
In that controller action, make sure you add a line to save the product #product.save
In case the #product is valid, then try passing in the fields explicitly:
<%= link_to 'Show', vendor_product_path(:vendor_id => current_vendor.id, :id => #product) %>

No route matches [DELETE]

I don't know what is it happened with my app. I have some controllers that are working perfectly but this one not.
my sylabus_controller.rb
# encoding: utf-8
module Admin
class SylabusController < BaseController
def destroy
#sylabu = #topic.sylabus.find(params[:sylabus])
#sylabu.destroy
redirect_to admin_course_topic_sylabu_path(#course, #topic), notice: 'Sylabus deleted'
end
my /views/admin/sylabus/index.html.rb
<% #sylabu.each do |syla| %>
<tr>
<td><%= syla.mupet_code %></td>
<td><%= syla.name %></td>
<td style="width:155px">
<%= link_to '<i class="icon-pencil"></i>'.html_safe, edit_admin_course_topic_sylabus_path(#course,
#topic,
syla),
class: 'btn' %>
<%= link_to '<i class="icon-trash icon-white"></i>'.html_safe, [:admin, #course, #topic, syla], class: 'btn btn-danger', method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to '<i class="icon-eye-open"></i>'.html_safe, [:admin, #course, #topic, syla],
class: 'btn' %>
</td>
</tr>
<% end %>
The target web is in the button delete is localhost:3000/admin/courses/1/topics/2/sylabus.8 and with the following error message
Routing Error
No route matches [DELETE] "/admin/courses/1/topics/2/sylabus.8"
Try running rake routes for more information on available routes.
If I execute rake routes from my console
POST /admin/courses/:course_id/topics/:topic_id/sylabus(.:format) admin/sylabus#create
new_admin_course_topic_sylabu GET /admin/courses/:course_id/topics/:topic_id/sylabus/new(.:format) admin/sylabus#new
edit_admin_course_topic_sylabu GET /admin/courses/:course_id/topics/:topic_id/sylabus/:id/edit(.:format) admin/sylabus#edit
admin_course_topic_sylabu GET /admin/courses/:course_id/topics/:topic_id/sylabus/:id(.:format) admin/sylabus#show
PUT /admin/courses/:course_id/topics/:topic_id/sylabus/:id(.:format) admin/sylabus#update
DELETE /admin/courses/:course_id/topics/:topic_id/sylabus/:id(.:format) admin/sylabus#destroy
Sincerely I don't know from the error is coming because it's a copy and paste from other controllers that are working perfectly.
Well thank you very much for your answers.
Have a great day
By the .8 suffix it looks like it's using the index path instead of the delete path. Try being explicit
<%= link_to 'blah'.html_safe, admin_course_topic_sylabus_path(:admin, #course, #topic, syla), method: :delete %>

New instance of a linked model, pass 'caller' id

I'm creating a simple inventory app, there is a view that lists 'items'. It has tables with these rows:
<tr>
<td><%= item.title %></td>
<td><%= item.desc %></td>
<td><%= item.value %></td>
<td><%= item.room.name %></td>
<td><%= item.user.username %></td>
<td>
<%= link_to 'View', item %>
<%= link_to 'Edit', edit_item_path(item) %>
<%= link_to 'Delete', item, method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to 'Add Comment', !?????! %>
</td>
/tr>
I have a linked model for 'comments' set up but don't know how to pass the 'item_id' to it when creating a new one.
The URL helpers actually accept the object to make a route for an association. Meaning, assuming you have a nested route for comments within items,
resources :items do
resources :comments
end
you can link_to the new_item_comments_path(item).
The method new_item_comments_path(item) makes a string URL based on the new_item_comments route, which you feed to link_to to make an HTML <a> tag.
To be clearer, in your view you would have:
<%= link_to 'View', item %>
<%= link_to 'Edit', edit_item_path(item) %>
<%= link_to 'Delete', item, method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to 'Add Comment', new_item_comments_path(item) #-> (instead of ???) %>
In this case, the item you are passing is the reference to your current item, which allows the URL helper to make a URL for it from the route.
The Rails guide for routing should be a useful read for you.
Now that's assuming your Comment controller assigns the right stuff at the right place. You seemed to have figured that out, but I'll explain for the sake of clarity (and future visitors)
class CommentsController < ApplicationController
# GET /item/:item_id/comments/new
def new
#comment = Comment.new
#item = Item.find(params[:item_id])
#comment.item = #item
# render
end
# POST /item/:item_id/comments
def create
#comment = Comment.new(params[:comment])
#item = Item.find(params[:item_id])
#comment.item = #item
# if #comment.save blah
end
end
All credit should go to #jonallard
The solution is all about routing it seems, you need to pass a url to the form that makes new comments (linked models).
to do this:
Add this (or similar depending on object names) to the page that is calling the creation:
<%= link_to 'Add Comment', new_item_comment_path(#item) %>
In both the new and the create method of the comments_controller there is a line starting #comment = Comment.new. Under that line add:
#item = Item.find(params[:item_id]) AND
#comment.item = #item
edit the top line of the comments template for to: <%= form_for(#comment, {:url => item_comments_path(#item)}) do |f| %>
Edit routes to somethings like:
resources :items do
...
resources :comments
end
and Read this: http://guides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects!
As mentioned before all credit goes to #jonallard, his answer and his expertise.

How to create custom method in Rails?

I'm currently trying to use a custom method in Rails and I do not know how to do so. Apart from the default methods in the controller, I wanted to add the following:
def cancel
#newsletter = Newsletter.find(params[:id])
respond_to do |format|
#SendGrid Newsletter API - Delete Newsletter Schedule
SendGrid.delete_schedule(#newsletter.name)
#newsletter.status = "CANCELLED"
#newsletter.save
format.html { redirect_to newsletters_url }
format.json { head :no_content }
end
end
The method is just like the default :destroy method but it doesn't actually destroys the object.
In my view, I had the following:
<% #newsletters.each do |newsletter| %>
<tr>
<td><%= newsletter.identity %></td>
<td><%= newsletter.name %></td>
<td><%= newsletter.recipients %></td>
<td><%= newsletter.subject %></td>
<td><%= newsletter.html %></td>
<td><%= newsletter.text %></td>
<td><%= newsletter.schedule %></td>
<td><%= newsletter.status %></td>
<td><%= link_to 'Show', newsletter %></td>
<td><%= link_to 'Edit', edit_newsletter_path(newsletter) %></td>
<td><%= link_to 'Destroy', newsletter, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<td><% if newsletter.status == "SCHEDULED" %><%= link_to 'Cancel', newsletter, method: :cancel, data: { confirm: 'Cancel Schedule?' }%><% end %></td>
</tr>
<% end %>
I got the error: No route matches [POST] "_newsletter url__"
When I rake routes, there isn't any route for the error above. May I know how to add the route and why is the route needed?
UPDATE
Currently, I still have the no route matches error. Below are all my files related to the 'Cancel' method:
routes.rb
resources :newsletters do
match '/cancel/:id' => 'newsletters#cancel', :as => :cancel
end
newsletters_controller.rb
def cancel
#newsletter = Newsletter.find(params[:id])
respond_to do |format|
#SendGrid Newsletter API - Delete Newsletter Schedule
SendGrid.delete_schedule(#newsletter.name)
#newsletter.status = "CANCELLED"
#newsletter.save
format.html { redirect_to newsletters_path(#newsletter) }
format.json { head :no_content }
end
end
newsletters/index.html.erb
<%= link_to 'Cancel', newsletter_cancel_path(newsletter) %>
You should have a line like this in your config/routes.rb file
resources :newsletters
You'll want to change it to this
resources :newsletters do
member do
put 'cancel'
end
end
You'll want to take a look at the routing guide that Иван Бишевац posted. You'll also want to understand basic restful routing and how Rails handles GET, POST, PUT, DELETE, etc.
I think you're mistaking the method: argument in the link_to as corresponding to the method in the controller. Actually it is referring to the RESTful HTTP verb i.e. :post, :delete and :put. So you don't pass the controller action through this way.
Instead you can pass in :controller and :action arguments...
Better still create a route in routes.rb and use the path that rails generates.
match "/cancel/:id", to: "controller#cancel", as: :cancel
and then the link_to would be something like:
link_to 'Cancel', cancel_path(newsletter)
Update:
The error you're getting is with the redirect_to in your cancel method (in the controller). Change the newsletters_url to newsletter_path(#newsletter) instead.
If you want to redirect back to the show page for a single newsletter, then you need to do the above (where you pass in the #newsletter parameter), if you want it to go back to the newsletters index page then it'll be newsletters_path.
You can check the existence of the routes by typing rake routes in your terminal. You'll see all the route names there.
Do you still get an error after changing to redirect_to newsletter_path(#newsletter)?
The thing that doesn't quite strike true is that you're getting a no POST route defined - which usually points to a malformed form_for. Examine your newsletter related forms especially any where you don't do the regular form_for #newsletter do |f|.
Here is complete explanation about routing:
http://guides.rubyonrails.org/routing.html

Resources