How to make a Controller Function call from Views Template in rails? - ruby-on-rails

I am new to Rails and have a function in product_controller.rb
def detach
#product.photo = nil
#product.save
end
now I want to call this method from views file show.html.erb so the method get executed. How to do it ? I can see the 7 methods do get called through .find(params[id]) but that is also not clear to me.

You'll need to add a route, something like this in routes.rb:
resources :products do
member do
get 'detach' # /products/:id/detach
end
end
That will give you detach_product_path(#product) which you can use in your view. You'll probably also want a redirect in the detach method:
def detach
#product = Product.find(params[:id])
#product.photo = nil
if #product.save
redirect_to #product, notice: 'Photo was detached!'
end
end

Try changing as follow
<%= link_to 'detach_image', product_detach_path(#product) %>
I would suggest you to have a look at guides.rubyonrails.org/routing.html.
you can do as follow,
you can use match
match '/update_profile', :to => 'users#update_profile'
or
resources :users do
get 'update_profile', on: :member
end
and then you would definitely have method in your users controller
def update_profile
#user = User.find(params[:id])
if #user.save
redirect_to #user, notice: 'user updated successfully!'
end
end

I have fixed the Simon answer. However, you are still facing the problem because you are not passing the product with the path:
<%= link_to 'detach_image', detach_product_path %>
You need to pass the product to the action:
<%= link_to 'detach_image', detach_product_path(#product) %>
Otherwise, the Product.find(params[:id]) will not find any product, and the #product will get empty...
Edit to reply your questions:
1 - product_detach_path is a helper for the action detach in the controller product. There is also the product_detach_url, which does the same thing, but also includes the current host, port and path prefix. More details here.
However, it does not pass any param, so Product.find(params[:id]) cannot find the product. For this reason, you must specify what product are you trying to find. #product is defined in the show action, so it is available in your view, but you could send any other product for the detach action.... maybe the first one: product_detach_path(Product.first)
2 - the resources :products generates seven default routes: index, new, create, show, edit, update and destroy.
In order to add more routes to it, you can use member or collection. Basically, member will add a route to a product (products/1/detach), while collection will add a route to the controller, like index (products/detach). More information here.
I hope it helps...

Related

Rails "No route matches" error

I'm getting the following error in my Rails app:
No route matches {:action=>"edit", :controller=>"customers", :customer_id=>1}
Following is the jobs view line to link to edit customer details (customer has many jobs, but no nested resources):
<%= link_to job.customer_id, edit_customer_path(:customer_id => job.customer_id) %>
Following is edit definition in the controller:
def edit
if params[:customer_id]
#customer = Customer.find(params[:customer_id])
elsif params[:id]
#customer = Customer.find(params[:id])
end
respond_to do |format|
format.html # edit.html.erb
format.json { render json: #customer }
end
end
rake routes gives the following:
edit_customer GET /customers/:id/edit(.:format) customers#edit
NOTE:
If I change the view to:
<%= link_to job.customer_id, edit_customer_path(:id => job.customer_id) %>
then I get the same error but with ":id=nil" (i.e. with no value passed at all):
No route matches {:action=>"edit", :controller=>"customers", :id=>nil}
I am a little new to this, but can someone please explain what is going on?
Thanks!
Update
Try writing your path like this
edit_customer_path(job.customer) if job.customer
In your routes, specify one parameter for customers.
resources :customers, param: :customer_id
so you always know what the id is going to look like. (there is some trickery required to make this work all the way through resources).
Also, another potential issue (which has caught me out a few times) is that I have instantiated on the same page a blank instance of the same class I am trying to route to. eg #customer = Customer.new, which the link to is looking for, but doesn't find an id as the record hasn't been saved.
I find named routes a hassle, and much prefer using polymorphic urls as they degrade gracefully.
In your case it would mean something like this.
link_to 'whev', [:edit, #customer]

Rails not redirecting to object after save

When saving our Video object we get a no method vide_url error when trying to redirect to the video#watch action and view the object
Admin/Video/Controller
def create
#video = Video.create(user: User.first, title: params['title'], description: params['description'], file: params['video']['file'])
redirect_to #video
end
Video/Controller
def index
#videos = Video.page(params[:page]||1)
end
def watch
#video = Video.find_by!(id: params[:id])
end
Routes
get "video/index"
get "video/watch/:id" => 'video#watch'
namespace :admin do
resources :video
resources :playlist
end
Any idea what is going on? Is it because we are using custom routes for the videos?
Yes, it is your custom routes. redirect_to #video essentially calls url_for #video. From the docs for url_for:
Relying on named routes
Passing a record (like an Active Record) instead of a hash as the
options parameter will trigger the named route for that record. The
lookup will happen on the name of the class. So passing a Workshop
object will attempt to use the workshop_path route. If you have a
nested route, such as admin_workshop_path you’ll have to call that
explicitly (it’s impossible for url_for to guess that route).
So, because you've got a namespace around that resource you'll need to do:
redirect_to admin_video_path(#video)
or
redirect_to admin_video_url(#video)
Update
If you want to redirect to the watch action you'll need to either redirect to a hash of options including that action:
redirect_to controller: :video, action: :watch, id: #video.id
Or give your watch route a name in routes.rb:
get "video/watch/:id", to: 'video#watch', as: :watch_video
And redirect to that named route:
redirect_to watch_video_url(#video)
Please, Try the followings.
def create
#video = Video.create(user: User.first, title: params['title'], description: params['description'], file: params['video']['file'])
redirect_to admin_video_path(#video)
end

rails multiple forms in one view

I have index method that displaying two forms Sign in and Sign up, that means user can create an account and a log in from same place.
so I have users controller with index method that displaying a view with Sign in and Sign up form with two partials one is _signin.html.erb and _signup.html.erb in index.html.erb.
Any Idea How can I handle new and create methods from users and sessions controllers (may be I can ignore new method)?
As long as each form is being rendered with the correct model object and/or the correct value to the :url option, each form should send the expected request (assuming you're rendering your forms with form_for).
For example, your sign in form should start with something like this::
<%= form_for :session, :url => sessions_path %>
#...
<% end %>
As long as a POST request (the default from form submissions) is sent to a "collections" resource (i.e. /sessions) it will route the request to the create action in your SessionsController or whatever you named your controller.
For signing up, you probably have something like this:
<%= form_for #user do |f| %>
# ...
<% end %>
The #user model object will assume the request should go to /users. Again this will call your create action in your UsersController.
Of course, all this is also assuming your config/routes.rb file is just declaring each resource with something like:
resources :users
resources :sessions, :only => [:create, :destroy]
You usually have each form in separate views such as
match 'sign_in', :to => 'sessions#new'
match 'sign_up', :to => 'users#new'
But if you only want to display these forms in an index.html.erb view then these routes are no longer necessary.
<%= form_for :signin,:url=>{:controller=>"yourcontroller",:action=>"signin"},:html=>{:id=>"signin_form"} do |f|%>
...
<%end%>
<%= form_for :signup,:url=>{:controller=>"yourcontroller",:action=>"signup"},:html=>{:id=>"signup_form"} do |f|%>
...
<%end%>
One simple workaround that I found was that if the two forms have at least one uniquely named parameter, then you can simply route the POST request to a single action. Then within the action check which parameter exists and execute the corresponding code. You end up having essentially two actions within one action in your controller.
def create
if params[:username] and !params[:name]
# You know that the user pressed submit on whichever form
# has a field that fills params[:username].
# So do the action with that form's parameters here
# i.e, login an existing user
respond_to do |format|
format.html {redirect_to '/home', notice: "Login successful"}
end
elsif params[:name] and !params[:username]
# You know that the user pressed submit on whichever form
# has a field that fills params[:username].
# So do the action with that form's parameters here
# i.e, create a new user
respond_to do |format|
format.html {redirect_to '/onboard', notice: "Thanks for signing up"}
end
end
end
Just be sure to have it configured in your routes.rb so that when a POST request comes from the page the two forms are on, it will direct to this action in this controller.
Hope this helps!

form_tag action not working in rails

I have this form in my application.html.erb.
<%= form_tag(:action=>"index", :controller=>"posts") %>
<p>
// code here
</p>
I dont understand why is this getting directed to posts->create instead of posts->index?
Thanks.
Basically, Rails observes and obeys "RESTful" web service architecture. With REST and Rails, there are seven different ways to interact with a server regarding a resource. With your current code, specifying the form's action as index doesn't make sense: Rails' form helpers can either POST, PUT or DELETE.
If you wanted to create a post, then redirect to the index, you can do so in the applicable controller action:
class PostsController < ApplicationController
...
def create
#post = Post.new
respond_to do |format|
if #post.save
format.html { redirect_to(:action => 'index') }
end
end
While your form would look like:
<% form_for #post do |f| %>
# put whatever fields necessary to create the post here
<% end %>
You seem to be a little mixed up with respect to the uses for each action. Here's a quick summary of typical RESTful usage:
Index -> view a list of items
New/Edit -> form where items are added or edited
Create/update -> controller action where items are created/updated
The reason your routes file is not taking you to index is because index is not an action where posts are typically created or updated. The best way is to go RESTful. Unless you have a very unusual situation, the best way to set your system up is probably a little like this:
# routes.rb
resources :posts
# application.html.erb (or better: posts/_form.html.erb).
<% form_for #post do |f| %>
<% end %>
# posts controller, whichever action you want to use
def new
#post = Post.new
end
By putting the form in a partial called form you can access it in new, edit, or wherever else you need to manipulate a post in your system.

What to do if more than one view needs to link to a destroy action?

I'm not sure what to do here. I have two scaffolds: Groups and Users. In two different Group views I'm listing group users and calling the Destroy method of the users_controller.
Since a 2nd view is now calling the destroy action, I need some way of detecting what view called the Destroy action because I need a different redirect and custom flash notice for each of the two group views.
Is there a simple way of solving this, or would the solution be something like making a copy of the Destroy method and mapping a new route for it?
-thanks!
edit: maybe this is a stupid idea, but I was thinking... For the two involved views, what if I stored their view names in the session when the views are generated (as a flag for the Destroy action to know which view to redirect to and what custom flash notice to send back)?
Pass the parameter with the links and check those parameters in your action.
Like ,
link_to "Delete", :controller => "groups", :action => "destroy", :pass_par => "view1"
link_to "Delete", :controller => "groups", :action => "destroy", :pass_par => "view2"
Controller:
def destroy
if params[:pass_par] == "view1"
redirect_to view1
else
redirect_to view2
end
end
The destroy method is not very long, so yes, go ahead and copy it.
If it looks like this:
# DELETE /users/1
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to users_url
end
It should be not repeating yourself at all, and it will make your code simpler to read in the end.

Resources