Best way to create preview functionality in Rails - ruby-on-rails

I'm looking to implement preview functionality in my posts scaffold. All I need to do is allow a user to enter information in the new view (/posts/new) and then replace the submit button with a preview button.
Once the preview button is clicked, the user is routed to the preview page (probably /posts/new/preview). If the user wants to make a change they would click 'go back' or if they are happy with the post they can then submit the post.
I found this article (http://eyedeal.team88.org/node/105) but it seems dated. Any ideas on what the best approach for this would be?
Many thanks,
Tony

On submit from create page, in the new action, build the object but do not save it to the database. Then render the object in its show view with a flag set in the new action to display a submit button. In your show view, always have a form with all the attributes of the object to be saved to db in hidden input fields or in display:none's. When the flag is set, you show the submit button. On submit, you go to the new_to_db action which saves the object to the db.

The link you have posted is a way, but I prefer to save object and set a boolean flag, let's say public to false (:default => false defined in migration).
Then what you basically do is actually create the post and redirect to show action, where you have
edit button (render edit action),
post button (custom action to set public flag to true)
and cancel button (which actually deletes the post)
and maybe continue later button, which keeps the post and redirects to any other page, so the user can come back later and finish editing it.
When you need to show all posts, define a named_scope :visible, :conditions => ['posts.public = ?', true] and call Post.visible instead of Post.all in index and similar actions. You could also define a default_scope with conditions ['posts.public = ?', false], but bare in mind that if you want to find posts that are not visible, you will have to use #without_scope.
This way is better than the one in your link, because user can always come back later and finish editing the post and the publish it. However you will store more objects in DB and have to deal with invisible posts (don't show them by default, etc.)

Related

Redirecting from new function in a resource controller

I'm new to Ruby on Rails. I've seen that in a resources_controller file in whenever you call resources#new it lands me to new.html.haml file which contains a form. When I click on Submit button it redirects to me the the create function of the above resources_controller.rb file. Can anyone explain me how? Actually I want to do something like this:
I want to create a new student only if the student with a given roll number doesn't exist.
For that new.html.haml contains a form where there is only one field for roll number, if the student with that roll number does not exist then a new haml should be called where there will be another form where one will enter the student details and only after submission of this latter form create should be called. And when the student already exits the form fields should be filled automatically.
I cannot figure out how to do this, because whenever I click the submit button in the form in new.html.haml it is redirecting me always to create.
you could simply add some code in top of resources#create function to check if the number corresponds to an existing record or not.
In case it corresponds to an existing one, you redirect the user to the corresponding edit page (that would look like /resources/:id/edit) so that he can fill the extended form.
In case it's a new record, you continue with the regular treatment.
So, you will have to add a code that looks like this
resources_controller.rb
def create
if !(resource = Resource.find(params[:id])).blank?
redirect_to edit_resource_url(resource)
end
...

Ruby on Rails - Adding a second (extremely) simple Auth layer in app

Once a user logs into their account, they are presented with a list of 'Employees'.
As of right now, when you click an employee, it takes the user to the 'show' page of that specific employee, however I want to add a 'pin-protected' aspect to that list before it renders the show page.
I want to add a simple layer of authentication that would go like this:
When a user clicks their name on a list, a text-field appears that asks for the selected employee's pin.
The user types in the pin. On submit, it compares the inputted pin against the 'pin' column for that employees' record. If it's correct it grants access to the selected employee's show page.
Is this something that is easily done in RoR? This is the first real app I have worked on, so I am having trouble wrapping my mind around a couple concepts like these.
Thanks so much!
Take a look at devise, it's most definitely your best bet for Ruby on Rails 3 authentication layer.
You're best bet if you just want to add a little functionality to your existing model class would be to add a method along the lines of:
def validate_pin(pin_to_check)
self.pin == pin_to_check
end
And then you just need to modify your employee controller so that show method checks to see if the pin has been provided (ideally via a session variable), otherwise redirect and request the pin with an additional method and route Employee#request_pin in the controller which asks the user to enter the pin, on success redirecting to the Employee#show route.
Session handling in the controller
To write the session variable, you'd need an Employee#check_pin method (as a POST route) and you'd just use the code:
session[:pin_valid] = true
Then you'd check session[:pin_valid] in your Employee#show method

How to trigger different actions based on submit button in rails

I have a form with a list of stuff, and an action already in place to update items.
I want to have another button, which when clicked triggers a different action to remove the selected items.
= form_for #new_item,:url => {:controller => "item_lists",:action => "update_list" } do |f|
- #items.each do |it|
%input{:type=>"hidden",:name=>"item_list[#{it.id}]position",:value=>it.position, :class=>'position'}
%textarea{:name=>"item_list[#{it.id}]field1"}
=it.field1
%textarea{:name=>"item_list[#{it.id}]field2"}
=it.field2
%input{:type=>'checkbox', :name=>'selected_items[]', :value=>it.id}
=(it.valid?) ? "" : it.errors.full_messages
%input{:type=>"submit", :value=>"Save changes", :name=>'save'}
%input{:type=>"submit", :value=>"Remove selected", :name=>'delete'}
This question seems to indicate I should inspect params in my action to figure out what was clicked. But that feels messy, my controller could quickly degenerate into a mass of ifs when I add more actions.
Is there a more elegant way to do this, i.e. get it to just route to the correct method?
Thanks for any help...
This doesn't really gel with REST. In REST and Rails you're typically going to have one action per endpoint, not decide on the endpoint based on some criteria in the request.
That being said, you can filter actions based on the submit button by checking the name of the button pressed. See this SO question.
I'd argue though that this is only appropriate if your form is doing slightly different things, like perhaps a submit button that updates in place versus a submit button that redirects somewhere afterward, e.g. "Update" versus "Update and Continue" (contrived, but you get what I mean).
Addressing your concern in the comments, your method wouldn't have to devolve into a long sequence of ifs. You could just write some code to determine which method to call based on the name of the submit button. A simple implementation might be:
# your form action
def update_list
send update_list_action
end
protected
def update_list_action
# just return the first action name found in the params
action = %w(save delete).detect {|action| params[action] }
"update_list_#{action}"
end
def update_list_save
# handle save
end
def update_list_delete
# handle delete
end
I would suggest you to add a dropdown menue with the option "delete", "update",... and add some jQuery code that observes the selected item and changes the action of your form depending on the value because you shouldnt use one action to update and delete objects! There should be one action for updating and one for deleting!

Rails - User Pressing 'Back' after object creation, Creating Duplicates

I'm having a problem where when a user fills out my evaluation form, click "Create", then click the browser's back button, make some edits, and click "Create" again, it's creating duplicate Evaluations.
What is the best way to prevent something like this happening.
Only ONE evaluation should exist for each survey_criterion on creation. I don't want the user to lose any data they enter after hitting the back button, filling out the form with new stuff, and clicking "Create" again.
UPDATE
routes.rb
resources :survey_criteria do
resources :groups do
resources :evaluations
end
end
survey_criterion.rb
has_many :evaluations
evaluation.rb
belongs_to :survey_criterion
belongs_to :group
There are more complicated associations, but the answer I'm looking for is more, "how does one handle it when users press the 'Back' button, modify the form, then click Create again".
I want it to update the one that was automatically created I think in this instance, and not throw an error to the user. I know I could add a validation that would error out, but I want this to be invisible to the user I think.
Thoughts?
The simplest solution, would be to change the create action, which should work like this pseudocode:
def create
# ...
if evaluation_exists?
update_evaluation(params[:evaluation])
else
create_evaluation(params[:evaluation])
end
# ...
end
As for Your question "how does one handle it when users press the 'Back' button, modify the form, then click Create again", then I use some random token (a short string) placed as a hidden field in the form.
When the create-request comes, I check whether this token is already stored in the session. If it is not, then I create the object, and add that token to the list of used ones. If the token is already present in the session, I know that user has just resubmitted the form, and I can act accordingly. Usually I ask him whether another object should be created. In the session I store usually not more that 3-5 tokens.
It looks like this (yes, that's just an illustration):
def create
token = params[:token]
session[:tokens] ||= []
if session[:tokens].include? token
render_the_form_again( "You have already created the object. Want another?" )
else
create_the_object
session[:tokens] << token
end
# ...
end
In your Evaluation model, add this line :
validates_uniqueness_of :survey_criterion_id
This is assuming that SurveyCriterion holds the foreign key that associates with your Evaluation.
You can also do 2 things :
Prevent the browser cache.
Disable the Create button with :disable_with => "Processing" option.
It is discussed here too: https://stackoverflow.com/a/12112007/553371
A less elegant way but more generic way to do this is to use history.pushState. On the page after create:
$(function(){
if(history.pushState){
window.onpopstate = function(event){
if(window.history.state && window.history.state.previousStep){
window.location = window.history.state.previousStep;
}
}
window.history.replaceState({ previousStep: '#{edit_resource_url(resource)}'}, document.title, window.location);
window.history.pushState({}, document.title, window.location);
}
})
This example uses HTML5's History API. A similar thing can be done with fallback using the history.js project

changing form behavior on submit based on radio button selection

I'm using Rails 3.1.0.rc5. I want to have a form with a pair of radio buttons, enable and disable, and a field to enter an integer (expire_after_days, the number of days until ticket expiration), with a hidden field for the fixed parameter subdomain_name. I'd like to be able to use the same simple form to create, edit, or delete a record, depending on which radio button is checked.
So if enable is checked, with no record found for the subdomain_name, a record would be created on form submission.
If enable is checked, and a record is found, the existing record should be updated on form submission.
And if disable is checked, the record should be deleted on form submission.
Is this a reasonable thing to do? If so, what tips do you have for how to do it?
It's not ideal nor restful to have all 3 actions (create, update, destroy) cramped up in just one controller method, but if you wish to continue on this dirty route here's what you could do:
def my_dirty_method
if params[:enable].present?
if params[:subdomain_name].present?
# Edit subdomain
else
# Create subdomain
end
end
if params[:disable].present?
# Delete subdomain
end

Resources