I have a small question regarding rails. I have a search controller which searches for a name in database, if found shows the details about it or else I am redirecting to the new name page. Is there anyway after redirection that the name searched for to automatically appear in the new form page?
Thanks in advance.
You can use the ActionController::Flash to pass data between actions.
def search(searchedName)
# perform search on DB
flash[:searchedName] = searchedName
redirect_to new_name
end
def new_name
end
new_name.html.erb:
<% if flash[:searchedName] %>
<%= text_field_tag flash[:searchedName] %>
<% end %>
Well, let's say you are saving this 'name' entered by user on a variable named #name.
So, on the controller that do the action of search, you did something like:
if #name
...#action if the name was found
else
session[:name] = #name
...#here is the action you did to redirect
On the declaration of the method called (you said it was 'new') :
def new
#name = session[:name] if session[:name] #I don't know exactly if the condition is this one,
#but you have to confirm that the session was instatiated
session.delete(:name) #to don't let garbage
...#continuous the action of show the new page
#and on the page you have access to a variable #name, that have the name searched before.
Related
I would like to have the ability to create an order directly from the listings show page instead of having to be directed to a new orders page.
I have a Listing (listingcontroller show method) which can be purchased by clicking a button to go to an orders page (orderscontroller create method).
In what way can I have the order form directly on the listings show page?
I have tried adding the form, but I get error:
First argument in form cannot contain nil or be empty
<%= form_for([#listing, #order]) do |form| %>
When I take the Orders controller create method and put it in the Listings Controller Show method i get this error:
Couldn't find Listing without an ID
Here's the form_for I want within the Listings Show Page:
<%= form_for([#listing, #order]) do |form| %>
....
Orders Controller create:
#order = Order.new(order_params)
#listing = Listing.find(params[:listing_id])
#seller = #listing.user
#order.listing_id = #listing.id
#order.buyer_id = current_user.id
#order.seller_id = #seller.id
...
Routes:
resources :listings do
resources :orders
end
listing model:
has_many :orders
category model:
has_and_belongs_to_many :listings
I tried taking the orders create method and injecting it into the Listings show method with "def create" and without. I put "#listing = Listing.find(params[:listing_id])" ahead of the create method (when using "def create" and i would still get the error it needs an id. Even when I get that error, at the bottom of the webpage the request shows the listing ID is there.
I tried using a hidden field in the form but didn't work for me.
Do I need to do something to the controllers or is there a way to load the :listing_id into the form somehow. This is probably something very quick and simple for some of you but why won't it load on the listings show page, but loads fine in the orders create page?
Easy approach.
Your show action in listing_controller.rb should have the following code:
def show
#listing = Listing.find(params[:listing_id])
#order = #listing.orders.build
.
.
.
end
Your views/listings/show.erb should have the following code
<%= form_for(#order, url: listing_orders_path(#listing)) do |f| %>
.
.
.
<%= end %>
This way you create an order to the listing (in memory) before you submit the form. You can add the listing id as a hidden field.
After submit the order you modify your orders_controller.rb this way:
def create
#listing = Listing.find(params[:listing_id])
#order = #listing.orders.build(params[...]) #select the params you need for the order creation. Since you create the order directly to the listing you don't need to add the listing_id to the order.
if #order.save
#do something
else
#do something
end
end
Keep in mind that using params[] directly you have security problems, please check about mass assignment: https://guides.rubyonrails.org/v3.2.8/security.html
You can achieve that by using AJAX call, where you will pass the url of orders action and other params. There will be no reload of page and you'll get the functionality right on the listings page.
Here is the link to have a look - How AJAX calls work.
In my Rails app I have a users_controller with an action that displays a users profile, but I would like it to only display a users profile if the user has a specific value set in the database called is_rec set to true.
The below is my attempt but it doesn't work. I used a scope for the index and that worked well but I wasn't able to get a scope to work when I was working with the show action
def rec_profile
#user = User.find(params[:id])
#user if #user.is_rec == 'true'
end
I could do something like this in the view but I wanted to see if there was a way to do it in the controller instead?
<% if #user.rec == 'true' %>
<% show page here %>
<% end %>
You can use find_by and pass 1 or more attributes to look for in your database. So you could pass the id and is_rec attributes and get your user.
User.find_by(id: params[:id], is_rec: true)
Or you can use where to get all users where is_rec is true and then filter for the user whose id matches with params[:id]:
User.where(is_rec: true).find(params[:id])
Which eventually can be converted to a scope for easy access:
scope :with_rec, -> { where(is_rec: true) }
For being used then as:
User.with_rec.find(params[:id])
with_rec is just an example name as scope, you could use whatever you want.
I'm trying to create a generic breadcrumbs method in my application controller to assign the breadcrumbs based on the current controller. If I wanted the breadcrumbs for the index of 'Thing', I would need in the view:
<%= breadcrumb :things, things %>
And for edit or show:
<%= breadcrumb :thing, thing %>
Where things is a method in the things controller that returns all things, and thing is a method returning the relevant thing.Both are exposed, and I have in my application layout:
<%= breadcrumb crumb, crumb_resource %>
And in my application controller:
def crumb
return controller_name.singularize.to_sym if edit_or_show_action
controller_name.to_sym
end
def crumb_resource
resource = controller_name
resource = controller_name.singularize if edit_or_show_action
end
def edit_or_show_action
action_name == 'edit' || 'show'
end
This obviously returns a string for crumb_resource, rather than the call to the controller method. From what I can find I believe it has something to do with send, however
controller.send(resource)
obviously doesn't work. How can I convert the string that is returned into a controller method call?
If you're using Gretel, then I think what you might be looking for is this:
def crumb_resource
resource = controller_name
resource = controller_name.singularize if edit_or_show_action
self.instance_variable_get("##{resource}")
end
This is assuming you have stored the relevant resource into #resource_name during the edit/show/index action.
I accepted the answer given as I'm assuming it works for people using instance variables to access models in their view, however in the end this worked for me:
breadcrumb crumb, eval(crumb_resource)
where eval evaluates the string, basically reverse interpolation which sounds pretty cool.
I have a listing page, then a form, then a thank you page. I need to put a link on the thank you page that takes the user back to the page they were on before the form which always varies. I've tried using this:
= link_to "Back", :back
But this only takes them back to the previous page, so the form.
Try this
<%= link_to 'Back', url_for(:back) %>
# if request.env["HTTP_REFERER"] is set to "http://www.example.com"
# => http://www.example.com
here is more details.
Well, you can set a method in the form page to collect that url. The basic idea is to use a custom session variable to store previous url and keep it to next session.
Suppose your form's action is SomeController#new, then
class SomeController < ApplicationController
after_action "save_my_previous_url", only: [:new]
def save_my_previous_url
# session[:previous_url] is a Rails built-in variable to save last url.
session[:my_previous_url] = URI(request.referer || '').path
end
end
Then in the thank you page, you can get this my_previous_url by
session[:my_previous_url]
This should be able to suit your case, the previous url two pages ago.
Disclaimer: This is not verified. Idea only.
Add
Session belongs to controller. It is not a helper you can use directly in view. You need to define an instance variable in controller and then you can use it in view. Like this
# Controller
#back_url = session[:my_previous_url]
# View
<%= link_to "Back", #back_url %>
You can use the example from Rails API:
<%= link_to "Back", :back %>
Rails API Doc for link_to
Using a :back Symbol instead of an options hash will generate a link to the referrer (a JavaScript back link will be used in place of a referrer if none exists).
Since you saying,it might be different page before form, probably request_url can help you. you can save your request_url in a param and redirect to param_url if there is.
here is a source that you can take for reference.
http://programming-tut.blogspot.com/2010/06/ruby-on-rails-request-url.html
If use in Controller, you can direct use like this:
def some_action
# some code
redirect_to :back
end
This works for me:
In controller from previous view:
cookies[:original_referrer] = request.orignal_url
to set a cookie on the browser with the URL of the originating page
In the controller from the current view:
redirect_to cookies[:original_referrer]
Think about a simple Rails scaffold application with a "new" action containing a form to add records to a database with a "save" button. After the "create" action the controller redirects to the "show" action, where the user can use the "edit" link to edit the just inserted record. So far, so simple.
But if the user instead uses the browser's back button after creating a record to get back to the "new" action, the browser shows the form with the values the user just has entered. Now he changes some values and presses "save" again. He thinks that this would change the record, but of course this creates a new record.
What is the preferred way to prevent such duplicate entries? I'm looking for a general solution, maybe based on cookies or JavaScript.
After some investigations I found a suitable solution based on cookies. Here it is:
In the controller's "new" action, a timestamp with the current time is generated and rendered in the form as hidden field. When the user submits the form, this timestamps gets back to the controller's "create" action. After creating the record, this timestamp is stored in the session cookie. If the user goes back to the "new" form via browser's back button, he gets a stale form, which means its timestamp is older than the one stored in the cookie. This is checked before creating the record and results in an error message.
Here is the controller code:
def new
#post = Post.new
#stale_form_check_timestamp = Time.now.to_i
end
def create
#post = Post.new(params[:post])
if session[:last_created_at].to_i > params[:timestamp].to_i
flash[:error] = 'This form is stale!'
render 'new'
else
#post.save!
#stale_form_check_timestamp = Time.now.to_i
session[:last_created_at] = #stale_form_check_timestamp
end
end
And here the form code:
- form_for #post do |f|
= tag :input, :type => 'hidden', :name => 'timestamp', :value => #stale_form_check_timestamp
= f.input :some_field
= .......
When I had that same problem I created this little gem that solves it. When the user hits back, he's redirected to the edit_path of the record, instead of going back to the new_path.
https://github.com/yossi-shasho/redirect_on_back
You can do something like:
def create
#user = User.new(params[:user])
if result = #user.save
redirect_on_back_to edit_user_path(#user) # If user hits 'back' he'll be redirected to edit_user_path
redirect_to #user
end
end
Your model validations will ensure things like email addresses are unique, but I think this is more about usability and experience than anything else.
Say you are talking about an account creation form. First of all, your form submit button should say something like "Create Account", instead of just "Submit". Then depending on whether it was successful or not, show a message like either "Account successfully created" or "There were errors creating your account". If the user sees this message, they will know what happened.
Sure you can't prevent someone from hitting the back button and hitting enter again, but you should design for the majority of use cases. If they happen to hit back, they will see the button that says "Create Account". You should probably have some other text on the page that says "Please sign up for a new account to get started".
Just my $0.02.
Session or cookie may result in sides effects.
I totally agree : if there is a way to validate with your model, it's the safest way to prevent duplicate records.
Still you can do 2 things. Prevent browser caching : fields will appear empty in the form when the user clicks on the back button. And disable the "Create" button when clicked.
= f.submit "Create", :disable_with => "Processing..."
When your user will press the back button the button will be disabled.
You can use validators to make sure that no duplicate values are inserted. In this case validates_uniqueness_of :field
If you for example want to prevent users from having the same email address you could put the following code in your user model.
validates_uniqueness_of :email
This checks the column for any previous entries that are the same as the one your trying to inert.
Good luck
base on #Georg Ledermann answer i make this little snip of code for redirect to edit path if the user hits back and then hits create.
#objects_controller.rb
def new
#object = Object.new
#stale_form_check = Time.now.to_i
end
def create
#object = Object.new(object_params)
#function defined in application_controller.rb
redirect_to_on_back_and_create(#object)
end
#application_controller.rb
private
def redirect_to_on_back_and_create(object)
if session[:last_stale].present? and session[:last_stale_id].present? and session[:last_stale].to_i == params[:stale_form_check].to_i
redirect_to edit_polymorphic_path(object.class.find(session[:last_stale_id].to_i)), alert: "Este #{object.model_name.human} ya ha sido creado, puedes editarlo a continuación"
else
if object.save
session[:last_stale] = params[:stale_form_check].to_i
session[:last_stale_id] = object.id
redirect_to object, notice: "#{object.model_name.human} Creado con éxito"
else
render :new
end
end
end
And finally add the #stale_form_check param to your form
<%= hidden_field_tag :stale_form_check, #stale_form_check %>
You could always abstracts this method where you need it, but in this way you could avoid lots of repetition in your project if you need this behavior in many parts
Hope it helps the next one, i used to use redirect_on_back gem, but it didn't work for me this time, the _usec param that this gem uses, was always been reset, so it can't compare in every time when it was need
Here's something that worked for me.
You will need to do 2 things: Create a method in your controller and add a conditional statement in that same controller under your 'create' method.
1) Your method should return the total count of that object from that user.
EX:
def user
current_user.object.count
end
2) Add conditional statement in your 'create' method.
EXAMPLE:
def create
#object = Object.create(object_params)
#object.save if user == 0
redirect_to x_path
end
I hope this helps!
Add html: { autocomplete: "off" } in your form_for like this:
<%= form_for #object, url: xxx_path, html: { autocomplete: "off" } do |f| %>