I have a fairly standard controller with a create method and some validations.
def create
#type = Type.new(params[:type])
respond_to do |format|
if #type.save
format.html { redirect_to types_path, notice: 'Type was successfully created.' }
format.json { render json: #type, status: :created, location: #type }
else
format.html { render action: "new" }
format.json { render json: #type.errors, status: :unprocessable_entity }
end
end
end
The problem is that when a validation fails, I get the errorMissing template ontology/types/create, as if the render action: "new" weren't there. If I replace it with a redirect_to then it works as expected, but then it seems I can't pass the form errors along.
I know that there is a #type instance (with #type.errors) from the original call of new, and throwing it just before the render call confirms this.
The same thing is happening when a validation fails on update It seems like the render call is just being ignored!
NOTE: my routing structure is a little unconventional, but I see not reason why this should be related.
This looks very similar: Path defined in controller and action is getting ignored, Ruby on Rails
Based on the answer to that question, I'm guessing that something is missing that is needed for rendering the new view, and as a result rails is just skipping the render call altogether and rendering create.
Can you show the new controller action and view?
Related
I've just started to play with Rails applications and I'm trying to use Ajax on a form, but I don't know how to catch the status code inside a js.erb file. I'm following this tutorial: http://guides.rubyonrails.org/working_with_javascript_in_rails.html
On my Users controller I have a code for my update method:
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.js {}
format.json { render json: #user, status: :created, location: #user}
else
format.html { render action: 'edit' }
format.js {}
format.json { render json: #user, status: :unprocessable_entity }
logger.debug #user.response_errors
end
end
I've created a update.js.erb file inside my views/users/ folder and is very easy to debug the #user var, but I don't know how to get the status code setted on my method.
Sorry if it's a stupid question, but I'm new with rails and I'm trying to follow all the frameworks concepts to the letter and I don't know the best pratices to create responses to Ajax requests.
What I'm trying to achieve is something like this:
#on my update.js.erb
if( status == 'created' ) {
alert( 'Ok, created' )
} else {
alert( 'Something wrong happened' )
}
I appreciate any help.
Option 1: Check Validity Inside update.js.erb
This is the option that I recommend in most cases.
update.js.erb is an ERB template whose result is a JavaScript code to evaluate on the client. In this case, you can make it look like:
<% if #user.valid? %>
alert('Ok, created');
<% else %>
alert('Something wrong happened');
<% end %>
The decision which alert to displays happens server-side. The client receives either:
alert('Ok, create');
or
alert('Something wrong happened');
depending on the status of #user.
Option 2: Two separate js.erb files
You can split your update.js.erb into two files. update.js.erb should contain the happy path code:
alert('Ok, create');
update-error.js.erb should contain some error handling:
alert('Something wrong happened');
Then you decide which one to display in your controller:
respond_to do |format|
if #user.save
# ...
format.js {}
# ...
else
# ...
format.js { render 'update-error' }
# ...
end
end
I would try to do:
format.js {render json: {#user, status: :created, location: #user}}
I am trying to use the standard create method created for Ruby/Rails projects and simply pass in an additional form field that tells the method how many objects to create (vs just creating one object). The standard create method looks like so:
def create
#micropost = Micropost.new(micropost_params)
respond_to do |format|
if #micropost.save
format.html { redirect_to #micropost, notice: 'Micropost was successfully created.' }
format.json { render :show, status: :created, location: #micropost }
else
format.html { render :new }
format.json { render json: #micropost.errors, status: :unprocessable_entity }
end
end
end
I want to pass in an additional data (form field called number_to_create) which tells the method how many of the microposts to create. I just added a new form field like this, in addition to the other micropost form field params:
<%= text_field_tag :number_to_create %>
My question is how do I modify the create method code such that it creates N number of micropost objects vs. just one. So if I pass in 3 from the form along with the other micropost attributes, the method creates 3 identical micropost objects, not just one as it currently does.
Thanks in advance for your help on this.
You could use the param as times
#microposts = Micropost.transaction do
[].tap do |microposts|
param[:number_to_create].times do
microposts << Micropost.create(micropost_params)
end
end
end
respond_to do |format|
if #microposts.all? &:persisted?
format.html { redirect_to #micropost, notice: 'Micropost was successfully created.' }
format.json { render :show, status: :created, location: #micropost }
else
format.html { render :new }
format.json { render json: #micropost.errors, status: :unprocessable_entity }
end
end
The transaction block is to make sure that either all of them gets saved, or none of them gets saved, this way you can fix your errors and recreate them without worrying of getting any stray saved objects
I took over someone else's Rails project and I have a question about HTTP requests.
It SEEMS that I should be able to pass parameters through HTTP requests, I'm just unsure how. For example: rake routes shows
PUT /auction2s/:id(.:format) auction2s#update
Which seems to correspond to this function
# PUT /auction2s/1
# PUT /auction2s/1.json
def update
#auction2 = Auction2.find(params[:id])
print "Hello World"
respond_to do |format|
if #auction2.update_attributes(params[:auction2])
format.html { redirect_to #auction2, notice: 'Auction2 was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #auction2.errors, status: :unprocessable_entity }
end
end
end
But I can't figure out the URL I would need to pass to, for instance, change
id=18445&done=true
into that function.
Any thoughts? Is the function structured right? Do I just need to pass the request in a Ruby format, not through the browser or AJAX (which is what I'm trying)?
You should have a form for this action. Most likely in this location -> app/views/auction1s/edit.html.erb. It will be edit.html.haml if you are using haml template engine. The form will be rendered in the view and user input will be sent as parameters to this action on submit of the form.
I have a form that is adding rows to the DB via remote => true. I then want to append the new data to a table, but cannot get the correct view to render.
As of now, it is rendering the entire show.html.erb page for the new entry, but I want to layout a minimal version to be added as a . Is there a quick way to tell my controller what view to render after inserting into the db? I want to render my partial named _newly_added.html.erb
My Controller
def new
#task = Task.new
render :partial => "/tasks/newly_added", :locals => { :t => #task }
end
Thanks!!
EDIT
I think what I need is just an alternative "show" view.
I found that the method I needed to change was actually this:
def create
#task = Task.new(params[:task])
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
I just need to make an alternative show view, and then tell this to redirect_to that view.
Edited per the changes in your question. However, nothing really changes. You're thinking about things wrong, and need to adjust how you're thinking. You don't need an alternative show, you need to handle the format.js request.
The partial should be rendered within a JavaScript response, not the controller. The controller looks more like this:
def create
#task = Task.new(params[:task])
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 }
format.js
else
format.html { render action: "new" }
format.json { render json: #task.errors, status: :unprocessable_entity }
format.js
end
end
end
Then, in views/tasks/create.js.coffee
($ '#mytable').append("<%= j render(partial: 'tasks/newly_added', locals: { t: #task }) %>")
What's going on here is that the browser makes a call to create.js. The controller responds with the create.js template, because of the respond_to block's format.js. The j escapes the contents of the _newly_added.html.erb file, and the contents of it are appended to the table. The controller doesn't interact with the existing view, instead, JavaScript is sent to the browser, and it interacts with the view.
This all changes somewhat if you're using a client-side MVC framework like Backbone or Ember, but you didn't specify that so I'm assuming you're going with stock Rails.
I am using almost the code from the regular scaffold. The only change is the 4.times block where I make 4 answer objects on the question. The reason is that I have the corresponding input fields in the view. Now, if the validation fails it renders the new.html.erb again, however after what I have been reading it does not invoke the "new" action again. However I am depending on the 4.times block because otherwise the loop in the view have no answers to loop through. How do I fix this? I tried redirecting but then the error messages disappered.
New action
def new
#question = Question.new
4.times do
#question.answers.build
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: #question }
end
end
Create action
def create
#question = Question.new(params[:question])
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render json: #question, status: :created, location: #question }
else
format.html { render action: "new" }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
Exactly as your title suggests: render does just render (the template belonging to) the action and not perform the action itself.
To get your prebuilt answers rendered again in a failed create call, I suggest removing the :reject_if condition from the nested attributes setup. This way, empty submitted answers are preserved. To prevent them from being written to the database, just add regular validations to the Question model...
What you need to look at is the #question in your create action. In theory it should contain the 4 newly built answers so redisplaying the form would also contain these.
If they are not written you may have to look at the accepts_nested_attributes_for to make sure it gets deserialized correctly from the request.