How can I write a form in the view which calls a controller method I've defined? Also, I want to pass a parameter to the method. I want to do something like this, where I have defined the method predict, which takes the parameter days.
<%=form_for #currency, url: url_for(:controller => :currencies, :action => :predict) do |f| %>
<%= f.label :days %><br />
<%= f.text_field :days %>
<%= f.submit "Predict", class: "btn btn-primary" %>
<%end%>
Also, what do I need to add in routes.rb?
The 'rails way' would say not to create custom actions but create a new Predictions controller and use a standard :new or :create action depending on what you want.
That being said, you can use
<%=form_for #currency, url: {action: "predict"} do |f| %>
<%= f.label :days %><br />
<%= f.text_field :days %>
<%= f.submit "Predict", class: "btn btn-primary" %>
You can find it in these docs by searching for "action:" http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for
You'll then have access the params[:days] or however you set up your strong params. Hope this helps!
I generated a scaffold Currency with only one column (days) :
rails generate scaffold Currency days:string
In my Currency controller you have two action (home for the form and the root path and predict) :
class CurrenciesController < ApplicationController
def home
#currency = Currency.new
end
def predict
puts params[:currency][:days]
redirect_to root_path
end
end
The home view with the form looks like this :
<%= form_for #currency, :url => url_for(:controller => 'currencies', :action => 'predict') do |f| %>
<%= f.label :days %><br />
<%= f.text_field :days %>
<%= f.submit "Predict", class: "btn btn-primary" %>
<% end %>
With this result you can see the days field printed in your console with the puts log.
My routes.rb looks like this :
get 'currencies/home'
post 'currencies/predict'
root 'currencies#home'
Related
Routes
resources :users do
get 'following' => 'users#following'
resources :projects
end
Controller
class ProjectsController < ApplicationController
def index
#user = User.find(params[:user_id])
end
end
View
<% #user.projects.each do |project| %>
<div class="hidden">
<%= form_tag({:controller => "projects"}, :method => "put", :id => "form") do %>
<%= text_field_tag :title, nil, class: "form-control" %>
<%= hidden_field_tag "project_id", project.id %>
<%= submit_tag "Submit", class: "btn-inline" %>
<% end %>
</div>
<% end %>
I need to pass project.id to my controller, the project params in the hidden_field_tag keeps saying that it's 1 for every project. If I do <%= project.id %> outside the form_tag, then it displays the correct project id, but inside the form_tag, it shows 1 for every project. Any ideas?
I think the issue is more in that popover that you use than in your code, which I tested and works fine. I would suggest using something like Firebug to look at the generated HTML code on your page and see what value that has your hidden_field_tag.
Good luck!
I have 2 models that are associated with each other via a join table:
class Book < ActiveRecord::Base
has_many :reviews
end
class Reader < ActiveRecord::Base
has_many :reviews
end
class Reviews < ActiveRecord::Base
belongs_to :reader
belongs_to :book
end
Right now, I am able to update a review (which I created manually in the console) on route:
readers/:id/books
The above route was create using rails' member method:
resources :readers
member do
get 'books'
end
end
The update action in reviews controller (reviews#update) is defined like so:
def update
#reader = current_reader
#review = Review.find_by_reader_id(#reader.id)
#book = Book.find(params[:review][:book]
if #reader.books.include?(#book)
#review.update_attributes(review_params)
redirect_to (#reader)
else
flash[:error] = 'You can only edit reviews that belong to you'
end
end
My form_for reviews (reviews#update) looks like this:
Reader Reviews:
<% book.reviews.each do |review| %>
<% if current_reader == (review.reader) %>
<%= review.content %> written by <%= review.reader.name %>
<% if current_reader.reviews.include?(review) %>
<%= form_for ([book, review]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Update", class: "btn btn-large btn-primary" %>
<% else %>
<%= form_for ([book, review]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
<% end %>
<% end %>
The above works for update. But the 2nd form doesn't.
My intent is to check for a review => if there is one - display a form so that reader can update review; if there isn't one then display a form so that reader can create a review. I have a private method in reviews controller that checks to make sure that a reader has a book before either action is carried out (a before_action method I guess).
The first form works well (the update form) but the second does not - the form is not displayed at all. I have tried various things to get the form to display but no luck. Can you please me determine the best way to resolve this issue?
Thank you very much!
There could be several issues:
elsif
The likely reason your other form won't show will be that your elsif logic won't be correct
I had a look at the .include? Ruby function, and it seems to just be for arrays. Why not try using .exists? instead?
<% elseif !current_reader.reviews.exists?(review) %>
You may have to use review.id or similar to get the correct response. Failing that, why don't you just use <% else %>?
form_for
The second issue may be with your second form_for
<%= form_for [:book, review] do |f| %>
You're currently passing a local variable called book to the form_for builder. Although this is probably correct (I can't find your reference to creating book), I've found it best to put a symbol in the nested form (to show Rails which data it needs to use)
Could you try using else instead of elsif !current_reader.reviews.include?(review)?
Also, it's elsif and not elseif. The problem should not because of this - The page would not have loaded in the first place if this is the case.
UPDATE
I fixed my first error by updating my forms:
Reader Reviews:
<% book.reviews.where(reader_id: current_reader.id).each do |review| %>
<li>
<span><%= review.content %> writen by <%= review.reader.name %> </span
<%= form_for ([book, review]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Update", class: "btn btn-large btn-primary" %>
<% end %>
</li>
<% if book.reviews.where(reader_id: current_reader.id).size == 0 %>
<%= form_for ([book, book.reviews.build]) do |f| %>
<div class="field">
<%= f.hidden_field :book, :value => book.id %>
<%= f.text_area :content, placeholder: "compose new review" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
<% end %>
This displays the forms (both post and update). BUT I got this error when I tried to post a new review:
(rdb:35) #review
#<Review id: nil, reader_id: 101, content: "Trial", created_at: nil, updated_at: nil, book_id: nil>
(rdb:35) review_params
Unpermitted parameters: book
{"content"=>"Trial"}
(rdb:35)
So I changed my create action for review to make sure book_id isn't nill:
def create
#reader = current_reader
# #book = Book.find(params[:book_id])
#book = Book.find(params[:review][:book])
if #reader.reviews.where(book_id: #book.id).exists?
flash[:error] = "You already reviewed this book"
else
#review = current_reader.reviews.create(:book_id => params[:book_id.to_i, :content => review_params[:content])
debugger
if #review.save
flash[:success] = "Review created"
redirect_to reader_path(#reader)
else
flash[:error] = "You can only review books that are in your library"
redirect_to reader_path(#reader)
end
end
end
Also changed how I defined review_params:
def review_params
params.require(:review).permit(:content, :book_id)
end
All this changes gave the desired results. My code isn't dry AT ALL but the most important thing to me at this point is getting things to work. Here is to hoping I don't break it again. Thanks for your help #RichPeck
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Passing only two variables between controller and view - best practice?
There is my action:
def list
#codes = Code.order("created_at")
#languages = Language.order('name').collect {|l| [l.name, l.coderay]}
end
There is my view(I removed some lines):
<% #codes.each do |code| %>
<div class="code">
<%= link_to code.title, :action => 'show', :id => code.id %>
<% if code.author %>
#<%= code.author %>
<% end %>
</div>
<% end %>
<%= render :partial => 'shared/error_messages', :locals => {:object => #code} %>
<%= form_for :code, :url => {:action => 'create' }, :html => {:multipart => true} do |f| %>
<%= f.text_field :title %><br />
<%= f.text_area :content %><br>
<%= f.select(:language, #languages, {:selected => 'text'}) %>
<%= f.text_field :author %><br>
<%= f.submit "Submit code" %>
<% end %>
There are 3 variables in it: #codes(list of posts), #code(current post, used in another action) and #languages.
My IDE writes:
At most two instance variables should be shared between controller and
view
This inspection warns if there are more than two instance
variables shared between a controller and a view. A controller should
only manage one instance variable, plus a second one for the
current_user variable.
Usually I share more variables between Controller and View(in PHP), sometimes 10+.
How it's done in Rails?
You can save an instance var by making languages a helper:
def languages
Language.order('name').collect {|l| [l.name, l.coderay]}
end
Its a guideline some developers follow some of the time.
But I would read up on Rails Routing a bit more. Understanding how Rails routing works should give you a better idea on how to structure your code.
http://guides.rubyonrails.org/routing.html
I modified your code a bit, not tested. But hopefully gives you some good ideas.
Controller:
def new
#code = Code.new
#codes = Code.order("created_at")
end
def create
#code = Code.new(params[:code])
if #code.save?
# Do your thing.
else
# render your :new action passing your #code variable
end
end
View:
<% #codes.each do |code| %>
<div class="code">
# Use Rails Routing - In console, type rake routes to get list of routes.
<%= link_to code.title, code_path(code.id) %> # example.
<% if code.author %>
<%= code.author %>
<% end %>
</div>
<% end %>
<%= render 'shared/error_messages', :object => #code %>
<%= form_for #code, :html => {:multipart => true} do |f| %>
<%= f.text_field :title %><br />
<%= f.text_area :content %><br>
# language_list = helper method.
<%= f.select(:language, language_list, {:selected => 'text'}) %>
<%= f.text_field :author %><br>
<%= f.submit "Submit code" %>
<% end %>
What is the difference between using form_for the following way:
<% form_for #user do |f| %>
<%= f.label :name %>:
<%= f.text_field :name, :size => 40 %>
...
<% end %>
and:
<% form_for :user, :url => {:action => 'create'} do |f| %>
<%= f.label :name %>:
<%= f.text_field :name, :size => 40 %>
...
<% end %>
Does using #user just automatically use CRUD methods for the URL actions?
If you just give a model instance like #user without specifying an action (as in your first example), Rails automatically uses the appropriate CRUD action for your form:
If #user is a new, unsaved User object, the form will point to your create action.
If #user is an existing User loaded from the database, the update action will be used instead.
This has the advantage that you can reuse the same form for both your edit and new views without changing the :url parameter for your forms.
As usual, the API docs provide more information.
If you give form_for a symbol without an instance variable it looks for an instance variable with the same name.
The documentation says:
For example, if #post is an existing
record you want to edit
<% form_for #post do |f| %>
...
<% end %>
is equivalent to something like:
<% form_for :post, #post, :url => post_path(#post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
...
<% end %>
I am attempting to use form_for to implement a search form that works with a table-less Search model I created. The search form keeps triggering the 'index' action. I assume I should use 'new' to create the form and 'create' the process the search query. Looking at the log, my POST is getting changed into a GET. Here's my code:
/searches/new.html.erb:
<% form_for :searches, #search, :url => searches_path, :html => {:method => :post} do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :keywords %><br />
<%= f.text_field :keywords %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>
What's the standard way for triggering the 'create' action with form_for?
Are you using the RESTful map.resources :searches ?
If so, shouldn't your :url be set to new_search_path ?
form_for is used with models. For a simple search form, I reccommend doing something like this:
<% form_tag posts_path, :method => :get do |f| %>
<%= f.text_field :query %>
<% end %>
You'll get /posts?query=wtf.