Form submission help needed - ruby-on-rails

I'm developing a small project using Ruby on Rails where basically student can sign up for a study room. I have one modification that I'm trying to make that I can't figure out yet.
On default when I create new submission, it creates it and goes to the view screen that basically shows all the info from the submission. Here is the controller for the form:
# POST /students
# POST /students.json
def create
#student = Student.new(params[:student])
respond_to do |format|
if #student.save
format.html { redirect_to #student, notice: 'Student was successfully created.' }
format.json { render json: #student, status: :created, location: #student }
else
format.html { render action: "new" }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
What I'm trying to do is when the user clicks the submit button it doesn't automatically save it to the DB, but instead it goes to the next screen where the user can review their submission and confirm the submission by clicking the button. Once they click the confirm button it should save it to the DB and show the 'Student was successfully created.' notice and take them back to the form screen.
Here is the controller for the show (which is the next page that gets displayed):
# GET /students/1
# GET /students/1.json
def show
#student = Student.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #student }
end
end

Some key points:
Don't use #show to display the preview. #show is supposed to display normal persist objects, while your new object is not persist yet. You also need extra element on preview such as "confirm" link/button.
It's better to use a separate #preview action, which accepts form submit from #new, and will send confirmed object to #create.
You need a vehicle to pass object in #preview, both force(to #create) and back(to #new if not satisfied). Though I don't like to use session, it seems there are not too much choices.
The example code:
def new
#student = session[:student].blank? ? Student.new : session[:student]
# ...
render
end
def preview
#student = Student.new(params[:student])
session[:student] = #student
# ....
render
end
def create
#student = session[:student]
session[:student].clear
# ...
render
end
Add
More about #3. In #create you need params to build a new object with full data to save. However in this case the form is not submitted to #create but #preview instead. So how do you get the data? There are three ways:
a. Plain display(just like #show). In #preview, build a form with hidden fields filled with data sent by #new.
b. Plain display. Use session to pass data instead of form. Much simple than #a.
c. Show both plain text and a form in #preview. User can preview the submission and edit it right in #preview.
According to your need, #b and #c are all good IMO.

Related

Rails 5, rendering the same view in a consecutive action doesn't refresh the browser

Quick question, I've been trying for the last couple hours to discern what is causing the following behavior but it's just beyond my grasp.
I have this two actions on my 'UsersController':
def new
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
flash[:success] = 'Wellcome, %s! You have successfully
registered.' % [#user.name]
format.html { redirect_to login_path }
format.json { render :show, status: :created, location: #user }
else
flash.now[:error] = 'Hmm... There seems to be some errors.'
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Basically, we render a clean 'new' view, try to register a new user, and if the creation of a user fails, the application should flash a message for the current action (the create action), and render the 'new' view, updating the previous one with the flash information and the errors of the #user variable.
The problem is that, although the server processes the response just fine, the browser does not update the page, never re-renders the page, it keeps the stale 'new' view. I've looked the response with chrome's web tools and it bears the updated view, but for some reason the browser just won't render it.
I think it has something to do with caching, but really I'm out of my element here. If instead of rendering I just redirect to the new action, the flash works fine (removing the .now(), that is), but this way I lose the #user, which I would like to keep with it's full functionality.
Any ideas why this behaves like this, or at least how to solve it?
If you redirect_to the new action, when the user submits, it will still post to the create action and the user_params would still take effect. Simplying rendering :new on the already new page will not perform a fresh request/response. To initiate a fresh request you will have to use redirect_to.

Rails redirect to another controller's show method

Within the controller of currency, I want to redirect to the show method of prediction. How can I do this?
def update
respond_to do |format|
if #currency.update(currency_params)
prediction = #currency.neural_network.predict
###redirect to prediction's controller, show method
###???
else
format.html { render :edit }
format.json { render json: #currency.errors, status: :unprocessable_entity }
end
end
end
You can just do redirect_to prediction
Rails will automatically resolve path for the model.
simply add redirect_to prediction
Doing a redirect_to a specific object goes to the show page for that object. Rails knows that prediction is an active record object, so it interprets that as knowing you want to go to the show page for the object.
Here's the docs for redirect_to
redirect_to(options = {}, response_status = {}) public
Redirects the browser to the target specified in options.

Control redirect after create in certain circumstances

I have a rails app that has multiple ways of creating tasks
a) A normal task new screen with lots of options
b) A quick task create screen which has the minimum actions and only has the minimum fields to enable quick creation. And a list of the actions
After the create succeeds I want
a) to redirect to the standard show form
b) to redirect back to the quick edit page with a blank quick creation box and the new task in the list.
If create fails on validation I want
a) to redirect to the edit screen with the fields highlighted
b) to redirect to the quick create screen with the fields highlighted and the data still there.
I've tried editing the create respond_to if.save? but that seems to apply to everything in both cases.
There's a slight complication in that I create tasks either generically (no client selected) or as a nested route under client where the client is autoselected, and ideally I'd like to go back to that nested route location.
I'd like to control that respond to by using an if parameter that recognises where the call is coming from
if from quick_create
if #task.save?
redirect
else
reload table and clear
if from new
if task.save?
Any ideas?
Adding current controller code and routes:
routes.rb
get 'tasks/quick_create' => 'tasks#quick_create'
-----
resources :clients do
match 'tasks/quick_create' => 'tasks#quick_create'
----
tasks_controller.rb
def create
#task = Task.new(params[:task])
#task.practice_id = current_user.practice_id
unless #task.recurring.present?
#task.build_recurring
end
#task.create_recurring_tasks
if params[:batch_task] == "Create Task"
#client = Client.find(params[:client_id])
#task.build_batch_task(#client)
end
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render json: #task, status: :created, location: #task }
else
format.html { render action: "new" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
def quick_create
if params[:client_id]
#client = Client.find(params[:client_id]).tasks
#tasks = #client.accessible_by(current_ability, :read).order(:due_date)
else
#tasks = Task.accessible_by(current_ability, :read).order(:due_date)
end
#task = Task.new
#task.status = "Not Complete"
##task.task_files.build
#task.build_recurring
respond_to do |format|
format.html # index.html.erb
format.json { render json: #tasks }
end
end
Ibasically want to control the respond do bit with something like
if request.path.include? "quick_create"
And be able to flag up the errors on quick create. There appears to be two things.
1. The request.path if statement doesn't work
2. The #task when attempting to feed back to the quick_create page hits an error (since it expects both #task and #tasks, I think).
Anyway....
I imagine You may have two options -
specify some custom parameter inside URL and use it to decide where redirect to
or use different actions as already suggested.
If You are having problems with two different create actions, post current controller code and Your routes otherwise we can't help You.

Save referenced resource on update_attributes (create nested resource on edit)

I have something like issue tracking system where there are issues and they have some comments.
Now on one page I want to give user an option to edit some stuff of "issue" as well as add a comment. Editing of and issue is a standard stuff like in /edit but also I want to create a comment and validate if it's not blank.
I've figured out that I can build a comment and make a form for it, but how should I check simultaneously that both issue attributes and comment attributes are valid? Because each update should be followed by a new comment, but I don't want to create a new comment if the issue attributes are no valid.
I would approach this by first adding fails_validation? methods to both your Issues and Comments models to check for problems.
Second, you will have to manually load the #issue form data from params[] and validate it BEFORE you save it (can't use update_attributes(params[:issue]).) Create a new Comment and load it via params[]. Then you can test the validation on both models and go back to the edit action if either fails.
If both pass you can save #issue and then #comment as normal.
def update
#issue = Issue.find(params[:id])
# manually transfer form data to the issue model
#issue.title = params[:issue][:title]
#issue.body = params[:issue][:body]
#...
#comment = #issue.comments.new(params[:comment])
# validate both #issue and #comment
if #issue.fails_validation? || #comment.fails_validation?
flash[:error] = "Your edits or your comment did not pass validation."
render :action => "edit",
end
# validation passed, save #issue then #comment
respond_to do |format|
if #issue.save
#comment.save
format.html { redirect_to #issue, notice: 'Issue successfully updated. Comment created' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #issue.errors, status: :unprocessable_entity }
end
end
end
Not the most elegant solution, but it should work.
You can validate the comment model and the issue model in their respective classes.
It is not clear to me whether you are using 'accepts_nested_attributes_for' in Issue for comments. If you are, then the standard IssueController#update will not save the record if issue is invalid and consequently, it will not create the comment records as well.
Here is the standard IssueController#update:
class IssueController < ApplicationController
def update
#issue = Issue.find(params[:id])
if #issue.update_attributes(params[:issue])
redirect_to issues_path, notice: 'issue updated'
else
render action: 'edit'
end
end

Using .dup and .update_attributes to generate a model instance is causing creation of extra instances upon failing validation

I have a validation for the Activity model and this is the create action in the activities_controller. When I try to save the model without meeting the validations, an extra activity instance is being created. Why is an activity being created when it fails the validations?
def create
#activity_last = Activity.find(params[:activity_id])
#activity = #activity_last.dup
#activity.activity_date = Time.now
respond_to do |format|
if #activity.update_attributes(params[:activity])
format.html { redirect_to #activity.tenant, notice: 'Activity was successfully created.' }
format.json { render json: #activity.tenant, status: :created, location: #activity.tenant }
else
format.html { render action: "new" }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
The reason I am using dup here is because I want to create a duplicate of the latest activity and then update a subset of its attributes from the params. So for explanation sake, say Activity had 10 attributes. The user would go to a form that has field for 3 of these attributes. I then want to create a new activity that has all the same attribute values as the latest activity for the attributes not being updated by the user.
--Update--
I have uncovered why this was happening. I shortened the code above so that there would be less clutter but as part of that shortening I cut out the following lines:
#comment = Comment.new(params[:comment])
#comment.user = current_user
#comment.activity = #activity
#comment.save!
The app works correctly when I move the saving of the comment to after the saving of the activity. My guess is that saving a comment that is linked to an activity that hasn't been saved causes the activity to be saved?

Resources