After validation, I got an error and I got returned back to :action => :new.
Some field on form already filled, so I want to keep them filled even after error message too.
How it can be done?
Your View (new.html.erb) something like following
<%= error_message_for :user %>
<% form_for :user, :action=>"create" do|f|%>
<%= f.text_field :login %>
<% end %>
Controller Code (create method)
def create
#user=User.new(params[:user])
if #user.save
redirect_to :action=>'index'
else
render :action=>'new' #you should render to fill fields after error message
end
end
Since in my case the form was in the view of another controller I did use flash to store my data and then check if there is data in flash present. If yes take this for default values for your input fields, if not just show whatever you want to show then.
So snippets from my code
flash[:date] = start_date
# in the view where to form resides
start_day = flash[:date].nil? nil : flash[:date].day
# ...
<%= select day start_day ... %>
Hope that helps some of you ;-).
Related
I am trying to create a custom form_for where I check if the record exists in the table or not. I've done tons of research but haven't come up with anything useful.
My current approach is to create a simple search form and display all similar records. However, that's not what I am looking for. Ideal scenario would be:
Get record's name from form_for
Check if this record present
If present - redirect to one page. If not - redirect to another page
My controller:
def validate_name
#room = Room.new
name = params[:name]
if name != nil
puts "Redirect to page A"
else
puts "Redirect to page B"
end
end
The problem here is that whenever the user comes to the page it automatically triggers the code above. My goal is to create a form validation that tries to find the exact record and then redirect based on if else condition.
Current form_for:
= form_for(Room.new, url: name_room_path, method: :get, action: :validate_name) do |f|
= f.text_field :name
= f.submit
I am sure that my form is incorrect too because I got lost. I found ways to create custom forms but can't figure out how to trigger database check based on the user's input.
PS: these are not new or update actions.
Thank you for your help and time.
Try this:
class RoomsController < ActionController::Base
def validate_name
if params[:name] && Room.where(name: params[:name]).last.present?
puts "Redirect to page A"
elsif params[:name] && Room.where(name: params[:name]).last.nil?
puts "Redirect to page B"
end
end
end
in routes.rb:
get '/rooms/validate_name', to: 'rooms#validate_name'
in view:
<%= form_tag(rooms_validate_name_path, :method => :get )do %>
<%= text_field_tag :name %>
<%= submit_tag %>
<% end %>
I'm trying to make simple app. I input my first name and last name to simple <%= form_for #data do |f| %> rails form and after submitting it, app should render simple text like this. My first name is <%= data.first_name %> and my last name is <%= data.last_name %>. I don't know why but my app is saying this error:
undefined local variable or method `data' for
It's probably saying it because no params are passed to view.
Here is my code.
routes.rb
resources :data, only: [:new, :create, :index]
data_controller.rb
class DataController < ApplicationController
def new
#data = Data.new
end
def index
end
def create
#data = Data.new(data_params)
if #data.valid?
redirect_to #data
else
render :new
end
end
private
def data_params
params.require(:data).permit(:first_name, :second_name)
end
end
/views/data/new.html.erb
<%= form_for #data do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :second_name %>
<%= f.text_field :second_name %>
<%= f.submit 'Continue', class: 'button' %>
<% end %>
/views/data/index.html.erb
<h2>Coolest app ever :D</h2>
<p>My first name is: <%= data.first_name %>.</p>
<p>And my second name is: <%= data.second_name %>.</p>
/models/data.rb
class Data
include ActiveModel::Model
attr_accessor :first_name, :second_name
validates :first_name, :second_name, presence: true
end
Please help to find out why params are not passing to next page. Thanks anyways :D
Your view should look like this:
<h2>Coolest app ever :D</h2>
<p>My first name is: <%= #data.first_name %>.</p>
<p>And my second name is: <%= #data.second_name %>.</p>
Also, I would suggest that calling a model something generic like Data is not a very Rails-y approach. Generally, domain models correspond to real-world things like User and Article, which are easy to understand and relate to. It'll get confusing quite fast if you use need to make another model and want to call it Data2 or something :)
Edit:
Since you specified that you do not wish to use the database, I would recommend passing in the object params through the redirect:
redirect_to(data_path(data: #data))
and in your controller's index method:
def index
#data = Data.new(params[:data])
end
Now your view should render properly, since you're passing the in-memory #data object attributes as params within the redirect. You then recreate this object in the index page or wherever you wish to redirect to.
To expand on Matt's answer, the reason you're getting NilClass errors is because:
You're redirecting to a data#show action when no show action has been enabled within your routes file. Since you've set your views up for the index, I'm assuming you want to redirect there when the #data object has been verified as valid:
redirect_to data_path
However I would recommend you follow Rails conventions and specify the data#show route within your routes.rb:
resources :data, only: [:index, :new, :create, :show]
and in your data_controller.rb:
def show
#data = Data.find(params[:id])
end
Another problem is that you're not actually saving the #data object upon creating it. The new method populates the attributes, and valid? runs all the validations within the specified context of your defined model and returns true if no errors are found, false otherwise. You want to do something like:
def create
#data = Data.new(data_params)
if #data.save
redirect_to data_path
else
render :new
end
end
Using save attempts to save the record to the database, and runs a validation check anyways - if validation fails the save command will return false, the record will not be saved, and the new template will be re-rendered. If it is saved properly, the controller will redirect to the index page, where you can call upon the particular data object you want and display it within your view.
As an admin, when I view the users index page, I can 'make' or 'unmake' a user as a resident manager.
When I make a user a residence manager, it works and the link changes to 'unmake...' but when I click to 'unmake...', it doesn't work (the link doesn't change back to 'make')
def update
user = User.find(params[:id])
if params[:resident_manager]
user.update(resident_manager: true)
else
user.update(resident_manager: false)
end
redirect_to admin_users_path
end
<% #users.each do |user| %>
<% if user != current_user %>
<p><%= user.email %>
<% if !user.resident_manager? %>
<%= link_to "Make Resident Manager", admin_user_path(user, resident_manager: true), method: :put %>
<% else %>
<%= link_to "Unmake Resident Manager", admin_user_path(user, resident_manager: false), method: :put %>
<% end %>
<% end %>
<% end %>
Your error is caused by this part of the code
if params[:resident_manager]
user.update(resident_manager: true)
else
user.update(resident_manager: false)
end
If you look at the parameters passed to your controller, you'll probably see that params[:resident_manager] is always passed. Change this chunk of code to
user.update(resident_manager: params[:resident_manager])
And that should solve your issue.
Your issue is that parameters from a form are always strings: an html form doesn't have any type information.
As a result your if statement is testing whether the string "true" or the string "false" are truthy. In ruby all objects other than nil and false are truthy, so in particular all strings are truthy and your code will always set the column in your model to true.
However you can assign that parameter straight to your model, i.e
user.update(resident_manager: params[:resident_manager])
Because activerecord has typecast code to convert strings into booleans, since this is so common. In particular it will convert '0', 'f' and 'false' to the boolean false
Perhaps you'd be better using the toggle method:
def update
user = User.find params[:id]
user.toggle :resident_manager if params[:resident_manager]
redirect_to admin_users_path
end
Considering boolean values can either be true or false, the toggle functionality will update the :resident_manager attribute to the opposite of what it is stored as - achieving the functionality you require
So after playing with the answers you guys provided, all of them worked. But I used the toggle because I have a custom validation and 'user.update ...' doesn't work well for it. Using the toggle allows me to call save which automatically checks the user against my custom validation and either proceed or shows the error while rendering the index page.
def update
user = User.find(params[:id])
user.toggle :resident_manager if params[:resident_manager]
user.toggle :resident_assistant if params[:resident_assistant]
if user.save
redirect_to admin_users_path
else
#users = User.all
render "index"
end
end
THANKS AGAIN EVERYONE!!!
In my rails application, I've got a partial view with an entry form on it. The form gets included on multiple pages across my app. The form in the partial posts to a RidesController to save with a create method like this:
RidesController.rb
def create
#ride = current_user.rides.build(params[:ride])
if #ride.save
flash[:success] = "Ride created!"
redirect_to root_path
else
#rides = current_user.rides.paginate(:page => params[:page])
render 'pages/home' # <---- WHAT GOES HERE?
end
end
I've commented the line where my question is. When we have an error, I need to present the same view that the user is presently on. But because this controller is being invoked from a partial instead of a full view, I don't know how to tell what context it's coming from.
Right now if there's an error on /rides/new, the user ends up redirected to the homepage which also has the form.
One way you could do this is pass the template path in with the form.
Add this to each main view that includes the form partial (e.g. pages/home, rides/new, etc):
<% #current_page_template = __FILE__ %>
In your form partial:
<%= form_for ... do |f| %>
<%= hidden_field_tag 'current_page_template',
#current_page_template.sub(File.join(Rails.root, 'app', 'views'), '') %>
In your controller:
def create
...
if #ride.save
...
else
...
render params[:current_page_template]
end
end
One thing you can do on my rap lyric explanation site is "like" explanations (once you're logged in):
http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1645.png
I'd like to show the "Like" links to users who aren't logged in, and then, when a non-logged in user clicks "Like", show him a lightbox with a "Login or Register" form (like Digg / Reddit)
http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1650.png
What's the best way to accomplish this?
Currently I'm using this approach:
Clicking "Like" POSTs to /annotations/:id/vote (the POST body indicates whether the user is liking or "unliking").
The vote Annotation controller action has a require_user before_filter that looks like this:
def require_user
unless current_user
store_desired_location
flash[:notice] = "You'll need to login or register to do that"
redirect_to login_path # map.login '/login', :controller => 'user_sessions', :action => 'new'
return false
end
end
user_sessions#new looks like this:
def new
#user_session = UserSession.new
respond_to do |format|
format.html {}
format.js {
render :layout => false
}
end
end
The problem is that the redirect doesn't seem to work correctly over javascript:
http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1700.png
How do I get this to redirect correctly?
Also, is this the right general approach? Another thought I had was to attach a different handler to the "Like" links in javascript when there was no logged in user (but I don't think this method scales well to other actions that I'd like to handle the same way)
There's a few problems to overcome here.
Browsers in general do not allow redirecting to a POST request.
redirect_to doesn't preserve format without additional input.
Store location does not preserve form data.
All these problems can be solved by eliminating redirects.
Here is how I've handed it in the past:
Instead of redirecting in required_user, render. If a before filter redirects or renders the pending action is cancelled. (No need to return false either). Unfortunately going this route blurs controller boundaries. But allows for simple html fallback, and lends its self to DRYness.
The high level view of the new work flow will be:
Request to annotations#vote (POST)
required_user filter fails
render new session
submit login information and original POST data back to annotations#vote (POST)
new filter in vote captures session information and logs in. vote proceeds as expected. If login fails return to 3.
annotations#vote redirects/renders as it should
Start by reworking the require_user to render the user_sessions#new template.
def require_user
unless current_user
flash[:notice] = "You'll need to login or register to do that"
#user_session ||= UserSession.new
respond_to do |format|
format.html {render :template => 'user_sessions/new'}
format.js {
render :template => 'user_sessions/new', :layout => false
}
end
end
end
The #user_session ||= UserSession.new ensures we can return validation errors to the form.
Now we've got to beef up your user_session#new template so that it can remember the action. Also if you plan on using lightboxes, this should be a partial rendered rendered by relevant RJS or the new.html.erb.
First we create a partial to create hidden fields preserving the POST data that would have been lost in a redirect:
<% if params[:controller] == "annotations" %>
<% content_for :old_form do %>
<%= hidden_field_tag "annotation[song_id]", params[:annotation][:song_id] %>
<%= hidden_field_tag "annotation[vote]", params[:annotation][:vote] %>
<% end %>
<% end %>
Then render that partial in the login partial that will occupy your lightbox:
<%= render :partial => vote_form_replica %>
<% url = params[:controller] == "user_sessions ? user_sessions_url : {} %>
<% form_tag #user_session, :url => url do |f| %>
<%= yield :old_form %>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= submit_tag %>
<%end%>
The empty hash for url in the form_tag looks like an error, but isn't. It ensures that the form data is posted to the url that rendered the form. Which at this point should be annotations/:id/vote
Now for the new filter to login. Essentially it will be doing what ever UserSessionsController#create does without the render/redirect. The following is copied from the RESTful authentication plugin.
def authenticate
self.current_user = User.authenticate(params[:login], params[:password])
if logged_in?
if params[:remember_me] == "1"
current_user.remember_me unless current_user.remember_token?
cookies[:auth_token] = { :value => self.current_user.remember_token,
:expires => self.current_user.remember_token_expires_at }
end
end
end
All that's left is to make sure the filter order is right.
before_filter :authenticate, :require_user, :only => :vote
N.B.: You're probably not going to use this version of require_user without this version of authenticate so it makes sense to combine them into a single filter.
And that's it. The way this has been set up allows for robust DRY easily reuseable code. By placing the new filters into ApplicationController they're available in any controller. From this point, adding this functionality to any other controllers/actions takes only 3 simple steps:
Create a new partial modelled after the vote_form_replica partial.
Add the corresponding render statement to the new session template.
Apply the filters to your actions.
I would approach this in the way you describe at the bottom of your question. Before displaying the page initially, check if the user is logged in. If they are, the "Like" links should use their normal behavior. If not, bind a click event to show the register/login panel. There's nothing about this that can't be reused. In fact, we use this exact method at my job. Any user action that requires authentication either follows its normal behavior or pops up a generic login panel depending on login state at the time the page loads.