How to do the calculation without any models in Rails? - ruby-on-rails

I need to get an integer(#integer) from the form in my root_path, do multiplication (#integer*45) and display the result on the same page. How can I do it without any models in my application?
Please, share your best practice. Thank you!
I was trying to do next:
CalculatorsController
def calculation
#integer = params[:integer]
#result = #integer*45
end
def result
end
root.rb
root :to => 'calculators#result'
resources :calculators, :collection=>{:result => :get, :calculation => :post}
calculators/result.html.erb
<% form_tag root_path, :html => {:method => :post} do %>
<%= label_tag 'integer' %>
<%= text_field_tag :integer %>
<div><%= submit_tag 'OK' %></div>
<% end %>

I'll do it with ajax, so there is no need for page refresh:
First, update the routes, for your example you only need two routes, one get (or root) and one post.
routes.rb:
Rails.application.routes.draw do
root 'calculators#result'
post 'calculators/calculation'
end
Next, update your view:
Change the url in your form_tag where the data will be sent (to calculation action instead of result).
Add remote: true option to enable ajax.
Add a tag where you will display your result.
result.html.erb:
<% form_tag calculators_calculation_url, remote: true do %>
<%= label_tag 'integer' %>
<%= text_field_tag :integer %>
<div><%= submit_tag 'OK' %></div>
<% end %>
<div id="total"></div>
And create a view for calculation action, but since you are using ajax, you will create it as js.erb and include the required javascript (or jQuery) to update your view (i'm using jQuery in the example).
calculation.js.erb:
$('#total').html('<%= #result %>')
Now when you click submit, your form will be sent to calculation action and will update the div with #result.

Just add the field to your form...
<% form_tag root_path, :html => {:method => :post} do %>
<%= label_tag 'integer' %>
<%= text_field_tag(:integer, #integer) %>
<% if #result.present? %>
<br>
Result is: <%= #result %>
<br/>
<% end %>
<div><%= submit_tag 'OK' %></div>
<% end %>
And then render result in your calculate...
def calculation
#integer = params[:integer].to_i
#result = #integer*45
render :result
end

Your result view (result.html.erb) is getting its data from the result method, not calculation. Update your controller as follows:
def calculation
#integer = params[:integer]
end
def result
#result = #integer*45
end
You then need a tag to display your result in the view, something like:
<p> <%= #result %> </p>

Related

How to make a link_to that redirects and submits a form

I have a modal that will serve as a disclaimer in my app and I need the link at the bottom of the modal that says "agree & continue" to toggle a boolean and input the time that the boolean was toggled. I have created the button as a form with hidden links but I cant seem to see how to make it submit the form AND redirect to the path i specify. Here is my link_to code now.
<% if current_user.user_record.blank? %>
<%= form_for :user do |f| %>
<% f.hidden_field :disclosure_acceptance, :value => true %>
<% f.hidden_field :disclosure_date, :value => Time.now %>
<%= link_to("Agree & Continue", user_steps_path(current_user), class: "btn btn-primary") %>
<% end %>
<% end %>
First, create a new method in your user_records_controller or at whichever controller action the form is displayed at:
def new
#user_record = current_user.build_user_record
end
Put this in your view:
<% if current_user.user_record.blank? %>
<%= form_for #user_record do |f| %>
<%= f.hidden_field :disclosure_acceptance, :value => true %>
<%= f.hidden_field :disclosure_date, :value => Time.now %>
<%=f.submit "Agree & Continue", class: "btn btn-primary") %>
<% end %>
<% end %>
Make a create action for the user_record that looks like this:
def create
#user_record = current_user.build_user_record(permitted_params)
if #user_record.save
redirect_to user_steps_path(current_user)
else
render :new
end
end
private
def permitted_params
params.require(:user_record).permit(:disclosure_acceptance , :disclosure_date) #etc
end
UPDATE
If you directly want to jump to the 'create' action, you can make your configuration like this:
Add a custom action to your routes:
post 'rate/:article_id' => 'user_records#create' :as => :create_user_record
#or whichever controller/action you wish
You should update the route on your form:
= form_tag create_user_record_path, :method=>'post' do
#etc
In order to create a user_record from the controller, you need to change things a little bit:
def create
current_user.user_record.create(:user_id => current_user.id, :disclosure_acceptance => params[:disclosure_acceptance] , :disclosure_date => params[:disclosure_date])
if current_user.user_record.save
#etc
end

How do I pass params through an AJAX form to create.js while retaining the params?

I have some comments that have a vote score, and I filter them by recent and popular using a toggle. When I click popular, it passes sort: popular as params, and resorts the comment list based on that. Then when a user posts another comment using AJAX, i'm trying to keep the comments sorted by popular (instead of reverting to the default recent) by passing the sort params through the comment form to the controller, and on to create.js, where it sorts the comments based on the params pass in. This works once, but as soon as I post 2 comments, it reverts back to sorting by recent, because i'm not able to continue passing the sort parms.
My sort toggle:
<% if params[:sort] == 'popular' %>
sorted by <%= link_to("Recent", video_path(video), remote: true, class: 'gray-link') %> |
<%= link_to("POPULAR", video_path(video, sort: 'popular'), remote: true, class: 'gray-link') %>
<% else %>
sorted by <%= link_to("RECENT", video_path(video), remote: true, class: 'gray-link') %> |
<%= link_to("Popular", video_path(video, sort: 'popular'), remote: true, class: 'gray-link') %>
<% end %>
The comments form, where I pass my params as a hidden field called :sort - note, they are nested comments, hence the form_for syntax, but it should be irrelevant here.
<%= form_for [#video, #comment, , :html => {:class => 'form_height'}], :remote => true, method: :post, url: video_comments_path(#video.id) do |f| %>
<div id="comment-form-errors">
<%= render :partial => "/videos/comment_form_errors" %>
</div>
<%= f.hidden_field :parent_id, :value => parent_id %>
<%= f.hidden_field :sort, value: params[:sort] %>
<%= f.text_area :post, placeholder: 'Comment', id: 'comment-box' %>
<% if parent_id != nil %>
<%= f.submit "Reply" %>
<% else %>
<%= f.submit %>
<% end %>
<% end %>
Then in the controller I create a variable #sort to collect the sort params from the comment form:
def create
#comment = #video.comments.build(comment_params)
#comment.user = current_user
#sort = params[:comment][:sort]
respond_to do |format|
if #comment.save
format.html { redirect_to video_path(#video.id), notice: "You said something. Let's hope it didn't suck." }
format.js { }
else
format.html { render 'videos/show', alert: "There was an error." }
format.js {}
end
end
end
Then I order the comments based on whether the popular sort params was passed:
<% if #comment.errors.present? %>
$('#comment-form-errors').html("<%= escape_javascript(render(:partial => '/videos/comment_form_errors')) %>");
<% else %>
<% if #sort == 'popular' %>
$('#comment-list').html("<%= j nested_comments (#video.comments).arrange(:order => 'cached_weighted_score DESC') %>");
<% else %>
$('#comment-list').html("<%= j nested_comments (#video.comments).arrange(:order => 'created_at DESC') %>");
<% end %>
$('#review-form-errors').html('');
$('textarea#comment-box').val('');
$('#comment-counter').text("<%= pluralize(#video.comments.count, 'comment') %>");
$('.error-explanation').text('');
<% end %>
HERE'S THE PROBLEM: The first time around my server logs show params being sent as: Parameters: {"utf8"=>"✓", "comment"=>{"parent_id"=>"321", "sort"=>"popular", "post"=>"c"}, "commit"=>"Reply", "video_id"=>"283"} - which is great, I post a comment and the comments, if sorted by popular, stay sorted by popular.
But if I post a second consecutive comment, it's not passing the sort params: Parameters: {"utf8"=>"✓", "comment"=>{"parent_id"=>"322", "sort"=>"", "post"=>"d"}, "commit"=>"Reply", "video_id"=>"283"} - my comments reverts to sorting by the default, recent.
I don't know where to go from here. I feel like i'm missing something small, but no amount of searching around has found the solution. Can anyone point me in the right direction, I would really appreciate any ideas on how to get this working, or if need be, how to rework my solution if there's a better way to do this.
THANKS!
EDIT: With MravAtomski's help I was able to get this working. I changed my comment form to:
<% if params[:comment] %>
<%= f.hidden_field :sort, value: params[:comment][:sort] %>
<% else %>
<%= f.hidden_field :sort, value: params[:sort] %>
<% end %>
And in my comment controller create action I added:
if params[:sort]
#sort = params[:sort]
elsif params[:comment][:sort]
#sort = params[:comment][:sort]
end
My guess is that after the first form submit the 'sort' hidden field looses it's value so it does not get sent it in next submit.
<%= f.hidden_field :sort, value: params[:sort] %>
maybe it should be
<%= f.hidden_field :sort, value: params[:comment][:sort] %>
because a comment object is being used in the form_for.
Also this is probably not the case but check if it is doing ajax requests and not html requests. If it's doing html requsests then it probably loses the 'sort' hidden value due to page refresh.
EDIT
so when page loads it uses params[:sort] then after create submit it uses params[:comment][:sort], maybe something like this can be used as quick solution:
<%= f.hidden_field :sort, value: params[:sort] || params[:comment][:sort] %>
Other solution would be to add a data attribute to one of tags that is not changed via create ajax request. i.e. form tag. add a data-sort attribute to form tag used to create comments:
<%= form_for [#video, #comment, , :html => {:class => 'form_height ..., :data-sort => params[:sort] do |f| %>
When comment is created don't change the data-attribute, it should only be set when user changes sorting and send it as ajax request parameter every time sort value is needed.

Use key in params

I am trying to build a log in system by this tutorial:
http://www.youtube.com/watch?v=h0k6DFIStFY
My form looks like this:
<!DOCTYPE html>
<div id="content">
<%= flash[:alert1] %>
<%= form_for(:sessions, :url => sessions_path , :html => {:id => "login-form"}) do |f| %>
<fieldset>
<p>
<%= label_tag :name ,"Username:" %>
<%= text_field_tag :name, params[:name] , :class => "round full-width-input", :autofocus=>true %>
</p>
<p>
<%= label_tag :password, "Password:" %>
<%= password_field_tag :password, params[:password], :class => "round full-width-input" %>
</p>
<%= submit_tag "Login", :class=> "button round blue image-right ic-right-arrow" %>
</fieldset>
<% if (flash[:status] == FALSE) %>
<br/><div class="information-box round"><%= flash[:alert] %></div>
<% end %>
<% end %>
</div> <!-- end content -->
and my controller looks like this:
class SessionsController < ApplicationController
def login
end
def create
user = User.authenticated?(params[:sessions][:name], params[:sessions][:password])
flash[:alert1] = "dummy"
if user
redirect_to '/login'
else
flash[:status] = FALSE
flash[:alert] = "Invalid username and password"
redirect_to '/login'
end
end
def new
end
end
when trying to submit, i get this error:
undefined method `[]' for nil:NilClass
in the following line:
user = User.authenticated?(params[:session][:name], params[:session][:password])
Did i use incurrectly in the session key ?
Thanks,
Gal!
I think you have some problems in your form: you are using a form_for and then in fields you are using text_field_tag.
I would correct it in something like :
<% form_for sessions .... do |f| %>
<%= f.text_field :name %>
and so forth.
This will generate the params you want in your controller
params[:sessions][:name]
params[:sessions][:password]
I would suggest you to use some gem instead of building an entire system of authentication, which can be quite tricky in terms of security. Have you taken a look at https://github.com/plataformatec/devise?
Hope it helps
It looks like you're using an external authentication gem, perhaps one of these?
https://www.ruby-toolbox.com/categories/rails_authentication
You need to include a require <gem_name> line at the top.

RubyOnRails sending params with form_remote_tag

I know some will think that i should use form_tag with :remote=>true, but i don't know how to render an entire html output....
My problem is the following:
I have this form that sends a collection through 3 comboxes
<%= form_remote_tag :url => report_client_reports_path, :update => :graphic do%>
<%#= form_tag reporte_client_reports_path%>
<p><%= label_tag :supermercados %>
<%=select_tag "supermercados[]", options_from_collection_for_select(#supermercados, "id", "name"),{:multiple=>true, :id => "supermarkets"}%>
</p>
<p><%= label_tag :cortes %>
<%=select_tag "cortes[]", options_from_collection_for_select(#cortes, "corte_real","cuts"),{:multiple=>true, :id => "cortes"}%>
</p>
<p><%= label_tag :productos %>
<%= select_tag "productos[]", options_from_collection_for_select(#productos, "id", "name"),{:multiple=>true, :id => "products"}%>
</p>
<p><%= submit_tag 'Send' %></p>
<%end%>
If i uncommented this line:
<%#= form_tag reporte_client_reports_path%>
It work good and present me the graph, but not the way i expect to work.
I have detected that using form_remote_tag, it sends all vars with their values, bu i dont know why my controller only see one value of each variable.
here is the controller:
#super = params[:supermarkets]
#superm = []
#super.each do |s|
#superm << Company.find(s).abbr
end
#cuts = params[:cuts]
#prods = params[:products]
#cortesGraph = []
#cortess.to_a.each do |c|
#cortesGraph << "#{RawData.find_by_real_cut(c).cuts}"
end
The objects #superm,#cuts and #products arent receving more than 1 value in the array, is a routing problem or a option i have missed in the form_remote_tag?
And update to simplyfy, what actually is still happening is this:
This Works:
<%= form_tag reporte_client_reports_path do%>
This doesn't:
<%= form_tag reporte_client_reports_path,:remote=>true do%>
The problem is that :remote is not sending my params as array it send all data as 1 var :s
I resolved in functional way, but there might be something very weird with this in my controller:
if request.xhr?
#super = params[:supermercados].to_s.split(",")
#cortess = params[:cortes].to_s.split(",")
#prods = params[:productos].to_s.split(",")
end

Multiple links on a Rails view that each perform different actions - how best to handle this?

One of the things I'm doing includes several links on the show view. For instance, I have a link (or button) for "Accepting", and another one for "Rejecting". Click on Accept, and the model updates the is_accepted field as true, click on Reject, and the is_accepted field is false.
Now, how best do I handle this? In ASP.NET, I would have simply created a LinkButton and written a handler, but Rails doesn't work that way, so I'm trying to figure out how to essentially replicate what a LinkButton would do.
Right now, I'm coding two forms on the same view, nearly identical, that look like this:
<%= form_for #thing do |f| %>
<%= hidden_field_tag 'thing[is_accepted]', '1' %>
<%= f.submit "Accept" %>
<% end %>
<%= form_for #thing do |f| %>
<%= hidden_field_tag 'thing[is_accepted]', '0' %>
<%= f.submit "Reject" %>
<% end %>
This feels weird to me, but I can't seem to find anything that says this is the wrong way to do it.
I could, I assume, dry things up by using a partial and/or a helper method, but I wanted to make sure I'm on the right track and not doing something totally wrongly.
You can give your submit tag a name.. ie
<%= form_for #thing do |f| %>
<%= hidden_field_tag 'thing[is_accepted]' %>
<%= f.submit "Accept", :name => 'accept' %>
<%= f.submit "Reject", :name => 'reject' %>
<% end %>
Then you can detect the name in params[] and skip the '1'/'0' value.
I think you're going about it the right way. One way to clean up your forms is by using the model form helpers all the way through, so you'd end up with something like
<%= form_for #thing do |f| %>
<%= f.hidden_field :accepted, :value => true %>
<%= f.submit "Accept" %>
<% end %>
<%= form_for #thing do |f| %>
<%= f.hidden_field :accepted, :value => false %>
<%= f.submit "Reject" %>
<% end %>
But other than that, it looks like the right way to go about it. I would suggest against creating new methods to do this, because you're not doing anything outside of normal web requests (updating a model in this instance).
Using the submit tag as the switch and detecting it in params[] is also a good way, but I usually prefer to keep my controllers as vanilla as possible. In the end, both of these ways would end up with the same amount of 'stuff' in the UI, so whichever style you'd rather use should be fine.
Depending on how you want your UI to work you might consider link_to_remote (part of the prototype helper) - you can specify an action, params etc, and have it return some JS that gets run.
If you're using map.resources in your routes.rb you should be able to do something like this:
map.resources :things, :member => {:accept => :get, :reject => :get}
Then in your controller:
def accept
#thing = Thing.find(params[:id])
#thing.is_accepted = true
#thing.save
end
def reject
#thing = Thing.find(params[:id])
#thing.is_accepted = false
#thing.save
end
And finally in your view:
<%= link_to 'Accept', accept_thing_url(#thing) %>
<%= link_to 'Reject', reject_thing_url(#thing) %>
Or if you are using Ajax:
<%= link_to_remote 'Accept', :url => accept_thing_url(#thing) %>
<%= link_to_remote 'Reject', :url => reject_thing_url(#thing) %>

Resources