rails extra action when clicking on link - ruby-on-rails

I have a rails app with notifications. Notification has :checked_at datetime attribute. On the notification index page I link every notification to the notifiable object like <%= link_to posts_path(anchor: "post_#{notification.notifiable_id}") do %>.
What I wanna achieve is when clicking on this link (that takes to the notifiable object) the notification.checked_at attribute would get updated.
I have some different solutions in my mind like an extra AJAX call when clicking on the link OR having an extra route like notifications#check_notification the link helper would point to and after checking the attribute it would redirect_to the original posts#index page.
I'm not sure which one is the preferred way (maybe a 3rd one) in rails. Could sby tell me what to do?

You could add another parameter to the route, something like update_checked_at, then on your index action inside the controller, you check if it exists and it is true.
<%= link_to posts_path(
anchor: "post_#{notification.notifiable_id}",
update_checked_at: true) do %>
def index
if params[:update_checked_at]
# Do your thing

Related

Why is link_to running a method inside it when the view is visited?

I have this link in the view:
<%= link_to "Pay in PayPal", #order.paypal_url(edit_order_url, payment_notification_url), class: "btn btn-success" %>
And this in the model:
class Order < ActiveRecord::Base
def paypal_url(return_url, notify_url)
# ... Do stuff
self.update_attribute(:payment_in_process, true)
# Do more stuff...
end
When I visit the view, the model gets its attribute updated. I do not want this. I only want the attribute updated if the link is clicked.
Know how to accomplish this?
Let me start with a brief overview of link_to method. According to api, this method creates a link tag of the given name using a URL created by the set of options:
link_to(name = nil, options = nil, html_options = nil, &block)
In your particular case, the name is "Pay in PayPal", which is a plain string. The second parameter, URL, is a bit more complicated, as you do not pass any kind of an absolute or relative path to content on the website, but call the method on an object instead:
#order.paypal_url(edit_order_url, payment_notification_url)
What happens here is that particular method paypal_url being instantly called, and link_to expects this method to return a correct URL. The same happens with the familiar examples from documentation, e.g.:
link_to "Profile", profile_path(#profile)
# => Profile
Here profile_path is a method which accepts an argument to get its unique id and therefore being able to generate a correct path. Same rule applies here: this method gets called as soon as user requests the page with the link.
So the first thing to do is to remove everything related to postprocessing of a click out of paypal_url method.
But you want to make those things work after a link is clicked. Actually a decision on whether a user clicked a link (or not) is made in a controller layer. Saying it simply, in case a controller action is called, then you know that a user clicked a link.
So the step two is to make some kind of request to your inner controller action and put the related logics there. In case paypal_url leads a user to your inner website page, you probably have that action implemented already. On the other hand, your paypal_url might lead to outer page, like actual paypal processing... In that case you could achieve the same with redirection or ajax request.

using rails how do i only show the id submitted via a text box from a table

I've got a table full of information at the moment, Ideally i need the information from a database table to be viewed via a link.
I only have the controller and some standard html (the html is just a h1 tag at the moment)
The HTML will be standard throughout like a template.
The way i'm seeing what i want in my head is the users will get a link which would be events_prev/{{id from DB here}} and depending on the ID the information on the page will be populated from the corrisponsing DB Row
Heres my controller
class Events::EventsPrevController < ApplicationController
def index
#events = Event.where(id: id)
end
def show
render :nothing => true
end
end
Sorry if its super confusing.
Welcome to rails.
Ok, there's a couple of things that will get you in the right directions. Firstly, you REALLY need to do a little reading to understand how the controller and the routes and the views are linked together in rails, that'll help you tons.
But moving on to your specific issues:
Parameters:
All data passed via a url (get, post, put, doesn't matter the method) is available in the controller in an array object called params - So that means when want to access the data the user submitted, you'll use something like
#event = Event.where(id: params[:id])
Routes:
It looks like you're trying to use the index page. In rails index is a RESTful route which generally points to a collection of model objects. The show route will point to an individual object so you should instead make your link point to the show path instead of the index path.
You can view the routes available on a model on a command line using:
bundle exec rake routes
An example of what your routes might look like:
prev_events GET /prev_events(.:format) prev_events#index
POST /prev_events(.:format) prev_events#create
new_prev_event GET /prev_events/new(.:format) prev_events#new
edit_prev_event GET /prev_events/:id/edit(.:format) prev_events#edit
prev_event GET /prev_events/:id(.:format) prev_events#show
PATCH /prev_events/:id(.:format) prev_events#update
PUT /prev_events/:id(.:format) prev_events#update
DELETE /prev_events/:id(.:format) prev_events#destroy
Link
Based on the routing table, you now should see that the link you need your users to click on might look like this (given that event is your object:
<%= link_to event.title, event_path(event.id) %>
or shortcutted
<%= link_to event.title, event %>
View
For the view this is entirely dependent on the data in the Event model. Since the data is stored in #event you'll simple use the attributes on the event model to render the html however use like, e.g.
<h3><%= #event.title %></h3>
<span><%= #event.start_time %></span>
You should read up on Rails controllers: by default the action index is used to show all of the records and what you're talking about should belong to the show action. The default routes take care of the id passing to your show action.
Index action is mean to show list of items in view and Show action is used to show a single item.
what you are doing in index is actually mean to be in show action.
Reason:
#events = Event.where(id: id)
this line will give u a single record in every case it means it should be in Show action.
your code should look like:
def show
#event = Event.find(params[:id])
[your logic: what you want to do with that #event]
end

how to add a `preview` action to resources?

I am looking for the right approach to include a preview action between new and create actions.
Let's assume I have the following:
resources :users
By default, when the form is submitted:
if new, call create action;
if edit, call update action.
In this way, I can use the same form (partial) for new and create, which is great!
How can I configure the resources to include a preview between actions. I mean, forcing new to call preview and then preview to call create.
I could add a new route/action and point the form for that action, however the same form cannot be used for new and edit.
There is a way to configure the resources to do that?
Have a look at this railscast: multibutton form, it shows a form with both a 'preview' and 'submit' button, maybe that's something you might want to do.
By the way, couldn't you use the same form by passing locals to the partial? For example:
<%= form_for #profile, url: dynamic_path do |f| %>
...
<% end %>
<%= render 'form', dynamic_path: profile_preview_path %>
you have several ways to do this:
you change the url of the form to your preview action (for which you have to add a route).
you use your create action for preview and create:
i.e. you add a parameter (like ':go_to_preview') to the form submit request. if you find it in the controller you render preview.
when the user wants to confirm the preview, you submit the data again (without that parameter) and this time create the record.
there are also 2 more dynamic possibilities:
you create the preview in real-time - if that is possible (like here on SO) - and use just the create action,
a variation of the first option: when the user submits the form, you send an ajax post request to a preview action, render a partial and include it on the page, then while your user still has the form he just filled, the user decides if she wants to modify or submit definitely.
I would suggest adding a DateTime column "finished_at", "published_on", etc... whatever is appropriate for your domain.
Using blog posts as an example:
scope :published, where("published_on IS NOT NULL")
scope :draft, where(:published_on => nil)
Use the scopes and new field where appropriate to limit the follow up actions.
This approach gives you more than you asked for :
a way to limit processing based on "state"
Data on creation times versus publishing times

Use a params[:value] to reference a controller method in Rails

I currently have a form (using form_tag). One of the fields is a dropdown list of options. Each option value matches the name of a method in my controller. What I want to do is when the form submit button is clicked, it runs the controller method corresponding directly to the value selected in the dropdown field.
I've built a work-around right now, but it feels too verbose:
def run_reports
case params[:report_name]
when 'method_1' then method_1
when 'method_2' then method_2
when 'method_3' then method_3
when 'method_4' then method_4
else method_1
end
# each method matches a method already defined in the controller
# (i.e. method_1 is an existing method)
I had thought that it may work to use the dropdown option value to run the corresponding method in my controller through the form_tag action (i.e. :action => params[:report_name]), but this doesn't work because the action in the form needs to be set before the params value is set. I don't want to use javascript for this functionality.
Here is my form:
<%= form_tag("../reports/run_reports", :method => "get") do %>
<%= select_tag :report_name, options_for_select([['-- Please Select --',nil],['Option 1','method_1'], ['Option 2','method_2'], ['Option 3','method_3'], ['Option 4','method_4']]) %>
<%= submit_tag "Run Report" %>
<% end %>
Any suggestions?
Can I change my controller method to look something like this - but to actually call the controller method to run? I'm guessing this won't run because the params value is returned as a string...
def run_reports
params[:report_name]
end
WARNING: this is a terrible idea
You could call the method via a snippet of code like this in the controller:
send(params[:report_name].to_sym)
The reason this is a terrible idea is that anyone accessing the page could manually construct a request to call any method at all by injecting a request to call something hazardous. You really, really do not want to do this. You're better off setting up something to dynamically call known, trusted methods in your form.
I think you should rethink the design of your application (based on the little I know about it). You have a controller responsible for running reports, which it really shouldn't be. The controllers are to manage the connection between the web server and the rest of your app.
One solution would be to write a new class called ReportGenerator that would run the report and hand the result back to the controller, which would run any of the possible reports through a single action (for instance, show). If you need variable views you can use partials corresponding to the different kinds of reports.
As for the ReportGenerator, you'll need to be a little creative. It's entirely possible the best solution will be to have an individual class to generate each report type.

Passing param using link_to and UJS

I'm having a hard time understanding the proper way to pass a param using link_to and UJS.
I have the following resources:
Photos
Comments
Users
A user is trying to comment on a photo by clicking "Add Comment." When this happens a box pops up using UJS showing a form rendered by utilizing a new.js.erb file. After "Create Comment" is posted the create.js.erb file is called to handle the update, which just hides the comment box and adds the comment to a list of comments.
In my index.html.erb for my photos I am doing the following:
I specify a link to add comments passing in the id of the current photo.
<%= link_to 'Add Comment', new_comment_path( photo_id: photo.id ), remote: true %>
This gives me the url: 0.0.0.0:3000/comments/new?photo_id=1, which is what I expect.
Now my question is, how do I handle this passed parameter in my new action such that I can specify something like
#comment.photo_id = photo_id
or
#comment.photo_id = params[:photo_id]
in my comments_controller.rb?
Is there something I can do in JS that will help me save the photo_id value to my #comment.photo_id column for the comment added?
First of all I'll suggest you to start using nested routes for things like comments or likes. You will find the railscast here nested_routes_railscast
Coming back to your question, use #comment.photo_id = params[:photo_id] in your controller.
There is a better approach to accomplish this, You can have the popup already on the photo show page. In the popup you can have a form for new comment model. After clicking on the specific photo you just have get the id of that photo using javascript and copy it into the hidden field for :photo_id in the form.
Yes, you can get photo_id in params, they way you have specified.
Suppose Photo has many comments in your case.
So in your case when in you get params[:photo_id] in comments_controller
you can do:-
#photo = Photo.find_by_id(params[:photo_id])
#photo.comments.create(params[:comment])
Please read about nested resources from guides.rubyonrails.org, so you can generate create comments route in restful manner.

Resources