How to "Create" after second step _form? - ruby-on-rails

So in step one the user creates his challenge.
<%= form_for(#challenge) do |f| %>
<%= f.text_field :action %>
<%= f.submit %>
<% end %>
Then he is directed to another _form to finish adding details about that challenge.
<%= form_for(#challenge) do |f| %>
<%= f.text_field :action %>
<%= f.date_select :deadline %>
<%= f.check_box :conceal %>
<%= f.submit %>
<% end %>
Once he adds those details and clicks "Save" I want the challenge to be created via the Create action of the challenges_controller.
def step_one
??
end
def create
#challenge = Challenge.new(challenge_params)
#challenge.save
redirect_to challenging_url(#challenge)
end

If you want to create a record across two requests, you need to persist data from the first request, and re-submit it along with the second request.
The easiest and most "Rails"y way of accomplishing this is to accept the incoming "name" attribute from your first request and render the second stage form with the name persisted as a hidden field.
app/controllers/challenge_controller
# Show "step 1" form
def new
#challege = Challenge.new
end
# Show "step 2" form, OR, attempt to save the record
def create
#challenge = Challenge.new(params[:challenge])
if params[:step] == '2'
if #challenge.save
redirect_to #challenge, notice: "Challenge saved!"
end
end
# Fall through to render "create.html.erb"
end
app/views/challenges/new.html.erb
<%= form_for #challenge do |f| %>
<%= f.input_field :name %>
<%= f.submit %>
<% end %>
app/views/challenges/create.html.erb
<%= form_for #challenge do |f| %>
<%= f.hidden_field :name %>
<%= hidden_field_tag :step, 2 %>
<%= f.text_field :action %>
<%= f.date_select :deadline %>
<%= f.check_box :conceal %>
<%= f.submit %>
<% end %>
There are a few things to note here:
create renders a different form than new. This is atypical for a Rails application
The "step 2" form rendered by create uses hidden_field_tag to attach an extra value to the submission, outside of the params[:challenge] attributes
Validation is unhandled - it's up to you to display errors if somebody submits an empty name in step 1, or other invalid attributes in step 2

The answer is more likely "it depends what you are trying to do", but the simplest solution is to redirect (in create after save) to the edit action/view which contains all or the other fields, not just the limited fields you provided in the new action/view.
def create
#challenge = Challenge.new(challenge_params)
if #challenge.save
redirect_to edit_challenge_url(#challenge), notice: "Saved!"
else
render :new
end
end

Related

Submit values with link_to in Rails 5.1

So currently i have a link_to, where signed in users can click on:
<%= link_to "Enroll", [#task.project, #task] %>
The user has an association with the project, through subscription. To create a new subscription for a user with a project, i wrote some simple form for it.
<%= form_for([#project, #subzz]) do |f| %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Which works fine and creates the association. However, i want that the user is able to create the subscription whenever he clicks on 'enroll' instead of a second, extra submit button.
Any ideas how to approach this? I thought about using jQuery, but not sure how to inject the ids with it and if its the 'right' way to do it.
Thanks in advance everyone!
EDIT:
When using the method posted as answer, i get:
param is missing or the value is empty: sub
My updatet form:
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
<%= hidden_field_tag :project_id, :value => #project.id %>
<%= hidden_field_tag :user_id, :value => current_user.id %>
<%= link_to "Enroll", [#task.project, #task], :onclick => "$('#project_form').submit() "%>
<% end %>
subs_controller.rb
class SubsController < ApplicationController
def create
#subz = Sub.create(sub_params)
project = #subz.project
redirect_to root_path
end
private
def sub_params
params.require(:sub).permit(:project_id, :user_id)
end
end
You can be using the existing form and link_to, just edit some like edit the dorm_tag like this
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
and remove the button into form like this one
<div class="actions">
<%= f.submit %>
</div>
and edit the link_to like this
<%= link_to 'Enroll', "", :onclick => "$('#project_form').submit()" %>
it will work
Update
You can achieve this without a form, comment out this form and edit the link like below
<%= link_to 'Enroll', subs_path(project_id: #project.id, user_id: current_user.id), method: :post %>
and the create method update like below
def create
#subz = Sub.new(sub_params)
if #subz.save
flash[:success] = 'Sub was successfully submited.'
redirect_to root_path
else
flash[:danger] = 'Sub not submited'
redirect_to request.referer
end
end
that is easier
Or if you keep before one with form then the link out from the form and the create method edit like the following
def create
#subz = Sub.new(sub_params)
if #subz.save
flash[:success] = 'Sub was successfully submited.'
redirect_to root_path
else
flash[:danger] = 'Sub not submited'
redirect_to request.referer
end
end
and the form will look like this
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
<%= hidden_field_tag :project_id, :value => #project.id %>
<%= hidden_field_tag :user_id, :value => current_user.id %>
<% end %>
<%= link_to "Enroll", [#task.project, #task], :onclick => "$('#project_form').submit() "%>
if you confused this [#task.project, #task] on link tag then use direct link
So i came up with the following solution:
I've added the sub handling to the application_controller, so that its availiable for the project_controller. I also added the project, tasks as a reference, so that i am able to redirect to a task via the sub_controller, instead of the project_controller.
application_controller.rb
def create
#subs = Sub.new(sub_params)
project = #subs.project
taskz = project.tasks.first
if #subs.save
redirect_to [taskz.project, taskz]
end
end
private
def sub_params
params.require(:sub).permit(:project_id, :user_id)
end
Inside the show.html.erb from the project_controller, i use the old form:
<%= form_for([#project, #subz] do |f| %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.submit "Submitted" %>
<% end %>
which works fine. Thanks for any previous help!

When I try to load my create.html.erb page, I get this error "param is missing or the value is empty"

I got this error on loading the page and I would guess that it has somethinng to do with my create method in the controller
My controller looks like this
class StoryController < ApplicationController
def index
#story = Story.all
end
def new
#story = Story.new
end
def create
#story = Story.new(story_params)
if #story.save
flash[:notice] = "Story created successfully"
flash[:color]= "valid"
else
flash[:notice] = "Story is invalid, man"
flash[:color]= "invalid"
end
end
def show
#story = Story.find(params[:id])
end
private
def story_params
params.require(:story).permit(:story_title, :story_body)
end
end
My create.html.erb looks like
<%= form_for #story ,url: story_path do |f| %>
<%= label :story, :title %><br />
<%= text_field :story, :story_title %>
<%= label :story, :body %><br />
<%= text_field :story, :story_body %>
<%= submit_tag 'Create story' %>
<% end %>
My create.html.erb didnt look like this before, I changed it to that after I read some questions about how form_for would work instead of form_tag for the story_params.
But either way, I still get the error anyways and I would like to know why and if there is a fix for it.
very first, you don't need to specify a path if you are using form_for and if you don't want to submit a form on the custom route.
If you are using new object then it will submit form on create method and for existing object it will submit form on update method.
So your form will be,
<%= form_for #story do |f| %>
<%= f.label :title %><br />
<%= f.text_field :story_title %>
<%= f.label :body %><br />
<%= f.text_field :story_body %>
<%= submit_tag 'Create story' %>
<% end %>
And this form needs to be in new.html.erb file.
This form will submit your form to create action with post method and from there you need to do render or redirection depending upon condition. So your controller will be,
class StoryController < ApplicationController
def index
#story = Story.all
end
def new
#story = Story.new
end
def create
#story = Story.new(story_params)
if #story.save
flash[:notice] = "Story created successfully"
flash[:color]= "valid"
redirect_to story_path(#story)
else
flash[:notice] = "Story is invalid, man"
flash[:color]= "invalid"
render :new
end
end
def show
#story = Story.find(params[:id])
end
private
def story_params
params.require(:story).permit(:story_title, :story_body)
end
end
If you do rake routes in the termial, you can see all methods with its expected methods
Also according to rails conventions, if you have story model then you can directly create :title & :body attributes instead of :story_title and :story_body
The create method is a POST... so when he enters story/create he's already expecting those values... that's why he says he can't find the params... i didn't look at the code deeply but it seems fine. Just change the name of the view to new.html.erb. New is the setup for create.
In new you setup the values and then invoke create where the controller actually creates the story.
Just change the name of the view to new.html.erb and change it to this
<%= form_for #story do |f| %>
<%= f.label :title %><br />
<%= f.text_field :story_title %>
<%= f.label :body %><br />
<%= f.text_field :story_body %>
<%= submit_tag 'Create story' %>
<% end %>
as the other user said. you need to say that those inputs belong to form |f| the text_field belongs to f, f.text_field
And of course you access that view through stories/new
Try this make changes in the create.html.erb
<%= form_for #story ,url: story_path do |f| %>
<%= f.label :title %><br />
<%= f.text_field :story_title %>
<%= f.label :body %><br />
<%= f.text_field :story_body %>
<%= submit_tag 'Create story' %>
<% end %>
Are you sure your attributes for the Story object are named story_title and story_body ?
If not, your story_params should be like this :
def story_params
params.require(:story).permit(:title, :body)
end
And also, apply #koshlendra suggestion, he's right.
I suggest you to generate a scaffolding for a fake resource to see how it's supposed to work :
rails g scaffold Fake title:string body:text
Then look at the generated controller and views to understand fully how it works.

Assigning attributes in HTML form or when calling .new(*attrs)

I have worked on numerous projects where I have seen both the setting of attributes for a new object in an HTML form and when calling Model.new(foo: 'bar')
Which is the correct way of doing this in the fashion of "best practices"?
Form:
<%= form_for User.new, remote: true do |f| %>
<%= f.hidden_field :foo, value: "bar" %>
<%= f.text_field :email %>
<% end %>
Instance Variable:
#Obviously this is set in the Controller
#user = User.new(foo: "bar")
<%= form_for #user, remote: true do |f| %>
<%= f.text_field :email %>
<% end %>
At first case, when validation of user is not passed, empty form will be rendered, at second case form with filled fields will be rendered. It's because instance variable #user at controller will keep entered values.
So I recommend you to use second variant.
I prefer the form being in a form partial. This way, you don't duplicate the new and the edit.
_form.html.erb
<%= form_for user, remote: true do |f| %>
<%= f.text_field :email %>
<% end %>
This lets you send in the user that you want to render it by.
So in the new.html.erb
<%= render partial: 'form', locals: {user: #user} %>
But it all comes down to the controller:
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end

Rails: form_for doesn't turn parameters

I'm basically trying to create a Rails form_for but it simply does not turn parameters. I've checked the server log and it receives the parameter but cannot capture on the create function. I'm watching andrewperk's tutorial (http://www.youtube.com/watch?v=unq1yubL6lQ) about create and save methods. What could I possible done wrong?. Here is my Users controller;
def new
#user=User.new
end
def create
#user=User.new(params[:user])
if #user.blank?
render :new
else
#user.save
render :index
end
end
And here is my form;
<h1>Add an available user</h1>
<%= form_for #user do |a| %>
<p>
<%= a.label :name %>
<%= a.text_field :name %>
</p>
<p>
<%= a.label :surname %>
<%= a.text_field :surname %>
</p>
<p>
<%= a.submit "Add new available user"%>
</p>
<% end %>
You should use params[:user] in your controller, because this is how the HTML form fields are named by default (you can check page source).
Also I'd suggest refactoring the create method a bit. It is a convention that you redirect to index page instead of just rendering it after creating a record.
def create
#user = User.new(params[:user])
if #user.save
redirect_to users_path
else
render :new
end
end

rails multiple input record

I'm new to rails. I have some question that's been quite a headache to me, so here it is:
For example i have this controller & view:
Controller:
class OrdersController < ApplicationController
def new
#Order = Order.new
end
def create
#Order = Order.new(params[:order])
if #Order.save
flash[:notice] = "Successfully created order."
redirect_to #Order
else
render :action => 'new'
end
end
View:
<% title "Menu Order" %>
<%= form_for #Order do |f| %>
<%= f.error_messages %>
<div id="form-order">
<p>
<%= f.label :name%><br />
<%= f.text_field :name, %>
</p>
<p>
<%= f.label :menu_order %><br />
<%= f.text_field :menu_order %>
</p>
</div>
<%= f.submit %>
So my question is :
before displaying the form above, I want to have a text_field_tag that specify how many forms (roughly said, duplicate the form div) I want to generate based on count, and then insert the data to the database simultaneously,
the idea is to speed things up, so that the user don't have to input the data only one at a time, but multiple record at single submit
How do I do that?

Resources