Customizing multi-model nested form behavior - ruby-on-rails

I am following the Rails guide for doing multi-model nested form. I have 2 models, Page and Picture. Page has_many Pictures. I'm putting the Picture file field within the Edit Page form using fields_for.
Each time I upload an image, the form will add an additional file field to allow for a new Picture to be uploaded.
The behavior I want is for the Page form to only always have one file field which will create a new Picture. I don't need previous pictures to be editable.
The questions thus are 1) how can I do the above? 2) should I even be using nested form? Because I'm not editing other parts of the Page when creating a picture.

You can just use:
#routes.rb
resources pages do
resources pictures
end
#PicturesController.new
#picture = Picture.new
#views/pictures/new.haml
= form_for #picture
#form code here
Or you can place form wherever you want and send it to pictures controller:
#views/pages/show.haml
= form_for [#page, Picture.new] do |f|
= f.hidden_field :page_id, :value => #page.id
= f.file_field :file #change to your own
= f.button :submit
#PicturesController
def create
#picture = Picture.new(params[:picture])
if #picture.save
redirect_to :back, :notice => "success"
else
#some code
end
end

Related

rails different form attributes based on category

I have a rails app!
I'd like to create a form for a product model, where users can choose a product category first and then can fill the form out.
This would be easy, but I'd like to show them different attributes based on the chosen category. Something like if they choose book category, then they will have fields like title, author, published_at, but if they choose shoes category then they can fill out the size, color and type fields.
I saw afew tuts about dynamic forms, but as far as I understand it, I don't need that since the form fields will be predefined and users won't be able to add extra fields.
What is the good approach in this case? Should I create more different models like (shoes,books, etc.) or something else?
Should I create more different models
No, I don't think that's necessary.
What you'd be best doing is using ajax to populate the form on category change. This would require some configuration, but will make it the most efficient and secure:
#config/routes.rb
resources :products do
put :new, on: :new #-> url.com/products/new
end
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def new
if request.get?
#product = Product.new
#categories = Category.all
elsif request.put?
#category = params[:product][:category_id]
#attributes = ...
end
respond_to do |format|
format.js
format.html
end
end
end
#app/views/products/new.html.erb
<%= form_for #product do |f| %>
<%= f.collection_select :category_id, #categories, :id, :name, {}, { data: { remote: true, url: new_product_path, method: :put }} %>
<div class="attributes"></div>
<%= f.submit %>
<% end %>
#app/views/products/new.js.erb
$attributes = $(...); // need a way to create form elements from #attributes
$("form#new_product .attributes").html( $attributes );
Something important to note is that Rails select & check elements allow you to use the data-remote attribute to send an ajax call to your controller on change.
Not much documentation about it, playing around with the above code should get it to work.

Form_for is giving me a No Method Error when trying to create a new object

So as it stands I have a form partial which starts off as:
<%= form_for(#merchandise) do |f| %>
It works perfectly for editing the data that I have already assigned in the rails console. When I try to use it for a "new" form that creates new merchandise (in this case the singular form of merchandise, I don't have nested resources, multiple models etc.), I get a no Method error that states
"undefined method 'merchandises_path' for #<#<Class:0x64eaef0>:0x95d2370>".
When I explicitly state the URL in the form_for method:
<%= form_for(#merchandise url:new_merchandise_path) do |f| %>
I get it to open and I finally have access to the form, however in this case I get a routing error that states
"No route matches [POST] "merchandise/new"".
I decided to write out that route in my routes file which previously just had:
root "merchandise#index" resources :merchandise
After I add the route it literally does nothing. I click submit and it takes me to the form but there is no new data in the database. I am at a complete loss and have been at this for hours googling and stack overflowing and I just don't know what to do anymore. All help is seriously appreciated. I'm adding a pastebin with all my code in the following url:
http://pastebin.com/HDJdTMDt
I have two options for you to fix it:
Option 1:
Please try to do this for best practice in Rails:
routes.rb
change your routes use plural
resources :merchandises
merchandises_controller.rb
Rename your file controller and class into MerchandisesController
class MerchandisesController < ApplicationController
def index
#merchandise = Merchandise.all
end
def new
#merchandise = Merchandise.new
end
def create
#merchandise = Merchandise.new(merchandise_params)
if #merchandise.save
redirect_to merchandises_path
else
render :new
end
end
def show
#merchandise = Merchandise.find(params[:id])
end
def edit
#merchandise = Merchandise.find(params[:id])
end
def update
#merchandise = Merchandise.find(params[:id])
if #merchandise.update(merchandise_params)
redirect_to #merchandise, notice: "The movie was updated"
else
render :edit
end
end
def merchandise_params
params.require(:merchandise).permit(:shipper, :cosignee, :country_arrival_date, :warehouse_arrival_date, :carrier, :tracking_number, :pieces, :palets, :width, :height, :length, :weight, :description, :cargo_location, :tally_number, :customs_ref_number, :released_date, :updated_by, :country_shipped_to, :country_shipped_from)
end
end
Option 2:
If you want to not change many code
/merchandise/_form.html.erb
in partial file
/merchandise/new.html.erb
<%= render 'form', url: merchandise_path, method: 'post' %>
/merchandise/edit.html.erb
<%= render 'form', url: category_path(#merchendise.id), method: 'put' %>

Error_Messages count increasing across all model instances

I am working on a rails 3.2.9 project with a Mongoid back end. I am trying to create posts and if the post is missing a title and content, the model should fail to save which it does properly. When an object is fixed and passes validation, I save it and now create a new post. If I try to save this one with the missing items, the error count just seems to append the previous one even though these are two different objects.
I am using the error_messages helper from dynamic_form to display my errors. Any Ideas?
Here is a sample error message:
152 errors prohibited this post from being saved
There were problems with the following fields:
Title can't be blank
Title can't be blank
Title can't be blank
Title can't be blank
Title can't be blank
The list continues on quite a way. Code is just basic form code:
= form_for #post,:as => :post, :url => post_path(:id=>#post.id), :method => :put do |f|
=f.hidden_field :is_question
#content
.title-page
%h1
New Post
= f.error_messages
In the model I have:
validates_presence_of :title
validates_presence_of :content
and my controller method:
def publish
#post = Post.first(conditions:{_id:params[:post_id]})
#post.assign_attributes(params[:post])
#post.published=true
if #post.save
redirect_to "/"
else
#video = Video.new
render action: "new"
end
end

What to render on error in a Rails controller

In my rails application, I've got a partial view with an entry form on it. The form gets included on multiple pages across my app. The form in the partial posts to a RidesController to save with a create method like this:
RidesController.rb
def create
#ride = current_user.rides.build(params[:ride])
if #ride.save
flash[:success] = "Ride created!"
redirect_to root_path
else
#rides = current_user.rides.paginate(:page => params[:page])
render 'pages/home' # <---- WHAT GOES HERE?
end
end
I've commented the line where my question is. When we have an error, I need to present the same view that the user is presently on. But because this controller is being invoked from a partial instead of a full view, I don't know how to tell what context it's coming from.
Right now if there's an error on /rides/new, the user ends up redirected to the homepage which also has the form.
One way you could do this is pass the template path in with the form.
Add this to each main view that includes the form partial (e.g. pages/home, rides/new, etc):
<% #current_page_template = __FILE__ %>
In your form partial:
<%= form_for ... do |f| %>
<%= hidden_field_tag 'current_page_template',
#current_page_template.sub(File.join(Rails.root, 'app', 'views'), '') %>
In your controller:
def create
...
if #ride.save
...
else
...
render params[:current_page_template]
end
end

Keep form fields filled after an error (RoR)

After validation, I got an error and I got returned back to :action => :new.
Some field on form already filled, so I want to keep them filled even after error message too.
How it can be done?
Your View (new.html.erb) something like following
<%= error_message_for :user %>
<% form_for :user, :action=>"create" do|f|%>
<%= f.text_field :login %>
<% end %>
Controller Code (create method)
def create
#user=User.new(params[:user])
if #user.save
redirect_to :action=>'index'
else
render :action=>'new' #you should render to fill fields after error message
end
end
Since in my case the form was in the view of another controller I did use flash to store my data and then check if there is data in flash present. If yes take this for default values for your input fields, if not just show whatever you want to show then.
So snippets from my code
flash[:date] = start_date
# in the view where to form resides
start_day = flash[:date].nil? nil : flash[:date].day
# ...
<%= select day start_day ... %>
Hope that helps some of you ;-).

Resources