Having same params management of link_to with button_to - ruby-on-rails

On my front-end I created a card with using the link_to helper and now I need to add another link_to in this card.
(A card entirely clickable with a button inside which lead to another action)
But anchor element cannot be nested so I changed my first link_to xx_path with a button_to xx_path, method: :get but I want to manage the params same with my link_to.
Here is what I tried
#My initial link_to I tried to change
link_to article_path(#article, top: params[:top], current_page: params[:page]) do ...
#This one doesn't pass any params
button_to aritcle_path(#article, top: params[:top], current_page: params[:page]), method: :get do ...
#This one send me every params even if they are nil which then lead to an ugly url like:
#localhost:3000/article/1?top=&current_page=
button_to aritcle_path(#article), params: { top: params[:top], current_page: params[:page] }, method: :get do ...
I guess maybe button_to is not the solution to my problem but I don't know how to do otherwise.

You do not need or want to use button_to when sending GET requests. button_to is for sending POST, PUT, PATCH or DELETE requests.
It doesn't do anything in that case that cannot be done with a simple anchor element. While you do occasionally use forms for GET requests that's because you want to use inputs.
Instead just pass a compacted hash to the path helper:
link_to article_path(
#article,
**{ top: params[:top], current_page: params[:page] }.compact_blank
) do ...
Using the double splat (**) to convert the hash to keywords is optional as the method doesn't actually use keyword arguments. But it will prevent the code from breaking in the future if that changes.
However.
Nesting a form inside a link is just as invalid as a link. The permitted contents are:
Transparent, except that no descendant may be interactive content or an a element, and no descendant may have a specified tabindex attribute.
So while this might solve the immediate problem you still need to rethink your whole approach.

Related

form_for and form_tag parameters for custom action

I would like to use a drop action in the friendships controller to drop requests of friendship, and I am struggling to understand how to create a working form and working routes.
I added the following route in config/routes.rb
resources :friendships, only: [:create, :destroy] do
member do
post :drop
end
end
This would generate the named route drop_friendship_path(id).
My question is now how to create a working form, that is what parameters am I necessarily required to use with form_for or form_tag.
Since in view I would iterate on #users requesting a friendship, I would not know the id of the request to delete, so I cannot use the above named route. Which one of the following is correct?
<%= form_for(current_user.friendship_request.find_by(requester_id: user.id), url: { action: drop }) %>
<%= form_tag({controller: "friendships_controller", action: "drop"}) do %>
I struggled to find documentation on parameters to use with form_for and form_tag. The api documentation says that the :url
argument for form_for may be represented in the same way as values passed to url_for or link_to. So for example you may use a named route directly. Documentation on url_for or link_to however does not add anything else.
I found only examples, not an exhaustive documentation, in the form helpers section of guides.rubyonrails.org for form_for and form_tag, so I realized that url for form_for can have a hash of subparameters (however only action is reported) , and that the controller and action parameters can be passed to form_tag.
What is the complete list of parameters for the url hash in the form_for helper?
What is the relative list of parameters for the form_tag helper?
Which ones are required in my form?
If, instead of specifying controller and action, I used inside form_for or form_tag:
url: drop_friendship_path(#friendship_request)
and defined #friendship_request in the drop action, would that work?
A better way is to use button_to or link_to helpers for your purpose. button_to generates a form. link_to generates a link, but can also send post request with {method: :post} option.
Why do you think you can't use the drop_friendship_path(id) helper method? You can:
<% request_id = current_user.friendship_request.find_by(requester_id: user.id) %>
<%= button_to "Drop", drop_friendship_path(request_id) %>
<!-- or -->
<%= link_to "Drop", drop_friendship_path(request_id), method: :post %>
Why don't you use the existing destroy action to delete friendships instead of drop ?
And also, sending a separate query for each user to get a friendship record is not a good thing. You should think of how you can avoid this. There are many solutions, but it is not the subject of the question.

parse form_for/form_tag to get the url or controller/action

I want to parse form_for and form_tag statements in erb files to determine which urls they post to or which controller/action will be called.
For example, given a ".html.erb" file, I want to get all "<%= form_for %>" tags out, somehow parse it, and get to know which exact controller/action pair will be called after I click to submit this form. For instance, the following file, https://github.com/jcs/lobsters/blob/master/app/views/stories/new.html.erb
line 7, "<%= form_for #story do |f| %>", can I determine which controller/action pair it will be mapped to by running some line of code? just like "routes.recognize_path 'form_for #story do |f|'" such kind of thing?
form_tag
form_tag(url_for_options = {}, options = {}, &block)
form_for
form_for(record, options = {}, &block)
Looks like I need to somehow get the url_for_options and options object out and get the url element out. Is there any easy way to do it or is there any existing tools that can achieve such functions? Does rails have any built-in functions for such thing?
i don't know if i understand your question correctly, especially the part about parsing your views etc, but you can generate urls by calling url_for.
for example in your rails console you can do the following:
include Rails.application.routes.url_helpers
default_url_options[:host] = "localhost"
url_for #user # => "http://localhost/users/1"

Identifying objects in Rails - Sometimes with :id and sometimes with the object itself?

another pretty general question about Ruby on rails:
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
Looking at the find method, I call it with the parameter id (which is incoming through the REST request, I guess)
Now here
<%= link_to 'Destroy Comment', [comment.post, comment],
method: :delete,
data:{confirm: 'Are you sure?' } %>
I assemble the route with the id of both comment.post + comment. To me it seems like the comment.post.id and comment.id are given implicitly (magic^^), which is fine for me, but I tried to code it, and I get an exception.
Why is it that way? And can somebody guide me to the right part of the documentation where exactly this behavior is described? Im pretty lost with all this smart and automatic stuff ruby does....
Thanks!
You're confusing two very different things.
This is doing a request to ActiveRecord to find an instance of Post with a matching id:
Post.find(id)
This next line is using the built in UrlHelper to construct an HTML link in your view for the given resource:
link_to 'Link Text', resource
In your particular case, you specify the :method as being :delete. Through the configuration in your routes.rb file, rails then maps this DELETE request for a [POST, Comment] nested resource to the posts_comment_path and passes in the post_id for comment.post and the id for comment.
This isn't so magic if you dig through the Rails source code a little. The link_to documentation states that the second argument can accept any valid options accepted by url_for. (click here for link_to docs)
The url_for documentation (available here) says this:
<%= url_for(#workshop) %>
# calls #workshop.to_param which by default returns the id
# => /workshops/5
# to_param can be re-defined in a model to provide different URL names:
# => /workshops/1-workshop-name
So, not so magic really. It's definitely worth spending a little time following the chain of calls that rails makes when you call something like link_to as it demystifies a lot of what you initially see as quite complex.

link_to custom action but wrong method?

all, I'm trying to get a custom action to work with a put method: in the
in _post.html.erb i have a link_to statement:
<%= link_to 'End now', post, :method => :put, :action => endnow %>
routes.rb contains:
resources :posts do
member do
put :endnow
end
and posts_controller.rb looks like:
class PostsController < ApplicationController
helper_method :endnow
[.. code for create, edit, destroy, etc ..]
def endnow
puts params
end
end
rake routes's relevant line looks like:
endnow_post PUT /posts/:id/endnow(.:format) posts#endnow
However, the action endnow helper doesn't run when clicking on this link.
Strangely, it does run with an index action (which i can tell from the puts command.
Of course, eventually the code for endnow will update #post, but for now, it just doesn't run properly.
Maybe i'm going about this the wrong way - all I'm trying to achieve is to update #post upon clicking the link to that post, and before showing it.
Any ideas / Alternatives?
Why not use the route helper method provided to you? Change your link to
<%= link_to 'End now', endnow_post_path(#post), method: :put %>
Things you're doing wrong:
If you want to specify the :action, use the Symbol for the action (you're missing a colon). :action => endnow should be action: :endnow
I will assume you have a #post instance variable you're passing from your controller to your action. You should be using that instead of post (unless you do in fact have a local post variable you're omitting from your code)
You are using endnow as an action; you should remove the helper_method :endnow line in your controller because it's not something you want to/should be accessing from your view.
This can all be avoided by using the route helper (for endnow_post you'd append _path to get the local route path: endnow_post_path), and pass in your #post as an argument.
Because you're trying to do a PUT request, you must make sure you have something like jquery-ujs included in your asset pipeline to convert these links to form submissions behind the scenes; browsers don't support PUT via the click of a link on their own.
As for why you're getting the template error when you get your link_to working, Rails is telling you that you need to create a app/views/posts/endnow.html.erb file. Your action has only puts params which does not terminate execution, leaving Rails to assume you still are trying to render some endnow.html.erb template.
Are there other ways to do what you're trying to do (change a single attribute of a specific model)? Sure. Are there better ways? That's pretty subjective; it may not be the most RESTful way, but it's arguably easier to deal with (if for example there are very specific authorization rules to check before updating the attribute you are modifying in endnow. Does the way you've started fleshing out work? Absolutely.
Finally, as a bump in the right direction, after you fix your link_to and remove the the helper_method as I have described above, your endnow action might look like this:
def endnow
post = Post.find!(params[:id])
post.some_attribute_here = some_new_value_here
post.save
redirect_to :root and return # <- this line sets a redirect back to your homepage and terminates execution, telling rails to do the redirect and **not** to render some endnow.html.erb file
end

Ruby on Rails: link-to using post method but parameters are in the URL

I'm using
link_to 'My link', path(:arg1 => session[:arg1], :arg2 => session[:arg2],:arg3 => anyobject.id), :method => :post
but the HTML link being generated includes (arg1,arg2,arg3) as URL query parameters.
How can remove them? Did I miss something in the documentation?
A link_to will always put the arguments into the query string as it is creating a get-style HTML link - even if you put :method => :post that just appends an extra ("special") argument _method.
What I think you really want is a button_to link - which will make it into a sort of form-post. It works the same, but it says button_to instead (for example, button_to 'My link', path(:params => :go_here). The downside is that it will look like a button. But you can give it a CSS class (eg "unbutton") and then change the styling on that CSS class to make it not look like a button.
Alternatively, if what you really want is actually to have no params passed to the controller at all... then just don't include them when making your link (for example, link_to "My link" path - there's no need for :post if you don't want to post any params).
Finally, if what you want is for the params to become a part of the URL (for example, stuff/[param_1]/more_stuff/[param_2], etc.) then you need to update your routes to include these parameters as options. Have a look at the routing section of the rdoc for how to do that.
You can use below code, which rails.js need data-method to switch to post mode in Ajax.
<%= link_to '<button id="print" type="submit" class="btn">Print</button>'.html_safe, { controller: :lots, id: #lot.containername, action: "print_#{print_template}", remote: true }, {'data-method' => :post} %>

Resources