Creating Rails 4 Form with Join Table Metadata - javascript

Very new Rails 4 developer here. I've got a form where a user is creating Exercises. Exercises can have many Equipment, and Equipment can be optional( think push-up stands for doing push-ups ). I store this "optional" field on the join table exercise_equipment.
I cannot get the parameters to actually send through the values of the collection element that I pick. See below for the model, view, controller, and parameters.
Here are the attributes/relationships of my models:
# id :integer
# name :string
# is_public :boolean
Exercise
has_many :exercise_equipment
has_many :equipment, :through => :exercise_equipment
accepts_nested_attributes_for :exercise_equipment
# id :integer
# exercise_id :integer
# equipment_id :integer
# optional :boolean
ExerciseEquipment
belongs_to :exercise
belongs_to :equipment
accepts_nested_attributes_for :equipment
# id :integer
# name :string
Equipment
has_many :exercise_equipment
has_many :exercises, :through => :exercise_equipment
Here are some (maybe) relevant controller methods:
def new
#exercise = Exercise.new
#exercise.exercise_equipment.build
end
def create
#exercise = Exercise.new( exercise_params )
if #exercise.save
redirect_to #exercises
else
render 'new'
end
end
def edit
#exercise = Exercise.find( params[:id] )
end
def update
#exercise = Exercise.find( params[:id] )
if #exercise.update_attributes( exercise_params )
redirect_to #exercises
else
render 'edit'
end
end
def exercise_params
params.require( :exercise ).permit(
:name,
:is_public,
exercise_equipment_attributes: [
:id,
:optional,
equipment_attributes: [
:id,
:name
],
]
)
end
This is my shot at creating a view to do what I want:
exercises/new.html.erb
<%= form_for #exercise do |f| %>
<%= render 'form', f: f %>
<%= f.submit "New Exercise" %>
<% end %>
exercises/_form.html.erb
<%= f.label :name %><br />
<%= f.text_field :name %>
<%= f.check_box :is_public %> Public
<%= f.fields_for( :exercise_equipment ) do |eef|
<%= eef.fields_for( :equipment ) do |ef|
ef.collection_select :id, Equipment.all, :id, :name %>
<% end %>
<%= eef.check_box :is_optional %> Optional
<% end %>
When I put all of this together and submit an update to an already-existing exercise, the values all go through the params hash, but aren't changed to the new values I've selected...
Parameters: {
"utf8"=>"[checkbox]",
"authenticity_token"=>"[token]",
"exercise"=>{
"name"=>"Test",
"is_public"=>"1",
"exercise_equipment_attributes"=>{
"0"=>{
"equipment_attributes"=>{
"id"=>"1"
},
"optional"=>"1",
"id"=>"2"
}
}
},
"commit"=>"Save Exercise",
"id"=>"1"
}
If you can help me out, I'd be super appreciative. Just let me know if you need any more information and I can provide it.
EDIT
Here is the state of the database before updating:
postgres#=>db=# select id, name, is_public from exercises;
id | name | is_public
----+------+-----------
2 | Test | t
(1 row)
Time: 61.279 ms
postgres#=>db=# select id, exercise_id, equipment_id, optional from exercise_equipment;
id | exercise_id | equipment_id | optional
----+-------------+--------------+----------
2 | 2 | 1 | t
(1 row)
Time: 58.819 ms
postgres#=>db=# select id, name from equipment where id = 1;
id | name
----+-------------
1 | Freeweights
(1 row)
I then go to the update route for that exercise, select a different equipment from the collection, and submit the form. I get the following Rails Console results:
Started PATCH "/exercises/system-test" for 127.0.0.1 at 2014-08-12 23:48:18 -0400
Processing by ExercisesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"PsbbUPSCiIew2Fd22Swn+K4PmLjwNDCrDdwXf9YBcm8=", "exercise"=>{"name"=>"Test", "is_public"=>"1", "exercise_equipment_attributes"=>{"0"=>{"equipment_attributes"=>{"id"=>"1"}, "optional"=>"1", "id"=>"2"}}}, "commit"=>"Save Exercise", "id"=>"system-test"}
Exercise Load (60.5ms) SELECT "exercises".* FROM "exercises" WHERE "exercises"."slug" = 'system-test' ORDER BY "exercises"."id" ASC LIMIT 1
(57.3ms) BEGIN
ExerciseEquipment Load (76.2ms) SELECT "exercise_equipment".* FROM "exercise_equipment" WHERE "exercise_equipment"."exercise_id" = $1 AND "exercise_equipment"."id" IN (2) [["exercise_id", 2]]
Equipment Load (59.1ms) SELECT "equipment".* FROM "equipment" WHERE "equipment"."id" = $1 LIMIT 1 [["id", 1]]
User Load (60.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 10]]
Exercise Exists (60.5ms) SELECT 1 AS one FROM "exercises" WHERE ("exercises"."name" = 'Test' AND "exercises"."id" != 2 AND "exercises"."user_id" = 10) LIMIT 1
(64.8ms) COMMIT
Redirected to http://localhost:3000/exercises/system-test
Completed 302 Found in 590ms (ActiveRecord: 580.0ms)
Started GET "/exercises/system-test" for 127.0.0.1 at 2014-08-12 23:48:19 -0400
Processing by ExercisesController#show as HTML
Parameters: {"id"=>"system-test"}
Exercise Load (64.1ms) SELECT "exercises".* FROM "exercises" WHERE "exercises"."slug" = 'system-test' ORDER BY "exercises"."id" ASC LIMIT 1
Equipment Load (58.7ms) SELECT "equipment".* FROM "equipment" INNER JOIN "exercise_equipment" ON "equipment"."id" = "exercise_equipment"."equipment_id" WHERE "exercise_equipment"."exercise_id" = $1 [["exercise_id", 2]]
Rendered exercises/show.html.erb within layouts/application (122.7ms)
User Load (60.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 10 ORDER BY "users"."id" ASC LIMIT 1
Rendered shared/_header.html.erb (61.9ms)
Rendered shared/_alerts.html.erb (0.1ms)
Completed 200 OK in 264ms (Views: 21.3ms | ActiveRecord: 240.8ms)

Firstly, you need to make sure you define your associations correctly.
Any has_many association should be defined with a plural name -
#app/models/exercise.rb
Class Exercise < ActiveRecord::Base
has_many :exercise_equipments
has_many :equipments, :through => :exercise_equipments
accepts_nested_attributes_for :exercise_equipments
end
#app/models/exercise_equipment.rb
Class ExerciseEquipment < ActiveRecord::Base
belongs_to :exercise
belongs_to :equipment
end
#app/models/equipment.rb
Class Equipment < ActiveRecord::Base
has_many :exercise_equipments
has_many :exercises, through: :exercise_equipments
end
If you've already got it working, and are happy with what you've got, then I'd recommend keeping your current setup. However, you may wish to adopt the above for convention's sake
Edit I see from the deleted answer that Beartech investigated this, and turns out Rails treats Equipment / Equipments as the same. Will be worth ignoring the above, but I'll leave it for future reference
Params
I cannot get the parameters to actually send through the values of the
collection element that I pick. See below for the model, view,
controller, and parameters.
I think I get what you mean - you're looking to update the record, but it does not send through the updated parameters to your controller, hence preventing it from being updated.
Although I can't see any glaring problems, I would recommend the issue is that you're trying to populate the exercise_id of an Exercise object. You need to define it for the exercise_equipment object:
<%= f.fields_for :exercise_equipment do |eef| %>
<%= eef.collection_select :equipment_id, Equipment.all, :id, :name %>
<%= eef.check_box :is_optional %>
<% end %>
This will populate your exercise_equipment table as described here:
Time: 61.279 ms
postgres#=>db=# select id, exercise_id, equipment_id, optional from exercise_equipment;
id | exercise_id | equipment_id | optional
----+-------------+--------------+----------
2 | 2 | 1 | t
(1 row)
Currently, you're populating the Equipment model with equipment_id - which won't work. Populating the model in that way will server to create a new record, not update the ones already created
Extra Field
I want to have a link to add an additional equipment field when it is
clicked, similar to how Ryan Bates did it in this RailsCast, but the
helper method he writes( see "Show Notes" tab if you're not subscribed
to see the source ) seems to become substantially more complex when
dealing with the nested views shown in my code below. Any help in
dealing with this?
This a trickier mountain to overcome
Ryan uses quite an outdated method in this process (to pre-populate the link and then just let JS append the field). The "right" way is to build a new object & append the fields_for from ajax. Sounds tough? That's because it is :)
Here's how you do it:
#config/routes.rb
resources :exercises do
collection do
get :ajax_update #-> domain.com/exercises/ajax_update
end
end
#app/models/exercise.rb
Class Exercise < ActiveRecord::Base
def self.build
exercise = self.new
exercise.exercise_equipment.build
end
end
#app/controllers/exercises_controller.rb
Class ExercisesController < ApplicationController
def new
#exercise = Exercise.build
end
def ajax_update
#exercise = Exercise.build
render "add_exercise", layout: false #> renders form with fields_for
end
end
#app/views/exercises/add_exercise.html.erb
<%= form_for #exercise do |f| %>
<%= render partial: "fields_for", locals: { form: f } %>
<% end %>
#app/views/exercises/_fields_for.html.erb
<%= f.fields_for :exercise_equipment, child_index: Time.now.to_i do |eef| %>
<%= eef.collection_select :equipment_id, Equipment.all, :id, :name %>
<%= eef.check_box :is_optional %>
<% end %>
#app/views/exercises/edit.html.erb
<%= form_for #exercise do |f| %>
<%= render partial: "fields_for", locals: { form: f } %>
<%= link_to "Add Field", "#", id: "add_field" %>
<% end %>
#app/assets/javascripts/application.js
$(document).on("click", "#add_field", function() {
$.ajax({
url: "exercises/ajax_update",
success: function(data) {
el_to_add = $(data).html()
$('#your_id').append(el_to_add)
}
});
});

Related

How to link options to orders incl. the quantity of options?

How can I include product options to an order incl. the quantities for the respective options?
--> It is unclear to me how to deal with the many to many relationship between orders and product options and linking them all together in an order form.
Please find below some code on how my tables are linked at the moment and how I currently try to solve it in a form.
models
class Order < ApplicationRecord
has_many :order_options, dependent: :destroy
end
class OrderOption < ApplicationRecord
belongs_to :option
belongs_to :order
end
class Option < ApplicationRecord
belongs_to :product_type
has_many :order_options, dependent: :destroy
end
class ProductType < ApplicationRecord
has_many :options, dependent: :destroy
end
orders_controller
class OrdersController < ApplicationController
def new
#shop = Shop.find(params[:shop_id])
#order = Order.new
#orders = #shop.orders
#order.order_products.build
#order.order_options.build
#product_type_list = #shop.product_types
#order.build_order_contact
#products = []
#options = []
# Display products/options for type
if params[:product_type].present?
#products = ProductType.find(params[:product_type]).products
#options = ProductType.find(params[:product_type]).options
end
if request.xhr?
respond_to do |format|
format.json {
render json: {products: #products, options: #options}
}
end
end
order form
<%= simple_form_for [#shop, #order] do |f|%>
<%= f.simple_fields_for :order_products do |order_product| %>
#select product for which options are shown -->
<%= order_product.simple_fields_for :products do |product| %>
<%= product.input :product_type_id, collection: #product_type_list,
input_html:{
value: #product_type_list.object_id,
id: "product_type"
}
%>
<% end %>
#area to display options belonging to the product
chosen above incl. dropdown field where users
can select a quantity. -->
<h4>Options:</h4>
<div id="render-options">
# Place where Javascript and Ajax can be rendered with possible options and dropdown field for quantity
</div>
<%= f.button :submit%>
Javascript/Ajax
<script >
// Grab selected product_type on which options are based -->
$(document).on("change", "#product_type", function(){
var product_type = $(this).val();
$.ajax({
url: "/shops/<%= #shop.id %>/orders/new",
method: "GET",
dataType: "json",
data: {product_type: product_type},
error: function (xhr, status, error) {
console.error('AJAX Error: ' + status + error);
},
success: function (response) {
console.log(response);
// dynamic rendered options -->
var options = response["options"];
$("#render-options").html("");
$("#render-options").append("");
for(var i=0; i< options.length; i++){
$("#render-options").append('<option value="' + options[i]["id"] + '">' + options[i]["name"] + '</option>');
console.log(options[i].orders)
}
}
});
});
</script>
For this example I'm going to go with a classic order form setup as its less confusing (at least to me).
class Order < ApplicationRecord
has_many :line_items, dependent: :destroy
has_many :products, through: :line_items
accepts_nested_attributes_for :line_items
end
class LineItem
belongs_to :order
belongs_to :product
end
class OrdersController
def new
#order = Order.new
# seed the form with 5 empty lines
5.times { #order.line_options.new }
end
def create
#order = Order.new(order_params)
# ...
end
# ...
private
def order_params
params.require(:order).permit( line_items_attributes: [:product_id, :quantity] )
end
end
<%= simple_form_for [#order] do |f| %>
<%= f.simple_fields_for :line_items do |item| %>
<%= f.association :product %>
<%= f.input :quantity %>
<% end %>
<% end %>
This is basically the setup for a "classic" order form which emulates paper. In reality this is not very useful unless your dealing with poor corporate minions that actually have to fill it out from top to bottom.
For an actual web shop the users are actually clicking on forms from GET /products/id that should actually be sending individual POST /orders/:order_id/line_item requests to a nested route to "add items to the cart". The order itself is often created implicitly.
In general the greatest misstake is thinking that you can treat placing an order as one single http call which creates the whole order and processes it. Instead you need to treat it as a set of atomic operations that lead the user to eventually checking out.

Save two models (which belong_to a third model) with one submit?

When the user clicks submit how can the info from two different models/DB tables be passed?
The user should be able to create a note in the missed_dates form and then that note should be saved to the respective #challenge the missed date is referring to.
missed_dates/form.html.erb
<%= simple_form_for(#missed_date, url: challenge_missed_dates_path({ routine_id: #challenge }), remote: request.xhr?, html: { data: { modal: true } }) do |a| %>
<%= form_for [#notable, #note] do |b| %>
<%= a.text_field :one %>
<%= b.text_field :two %>
<%= button_tag(type: 'submit') do %>
Save
<% end %>
<% end %>
<% end %>
missed_date.rb
class MissedDate < ActiveRecord::Base
belongs_to :user
belongs_to :challenge
end
missed_date_controller
def new
#challenge = current_user.challenges.find(params[:challenge_id])
#missed_date = current_user.missed_dates.build
#notable = #challenge
#note = Note.new
end
def create
challenge = current_user.challenges.find(params[:challenge_id])
challenge.missed_days = challenge.missed_days + 1
challenge.save
#missed_date = challenge.missed_dates.build(missed_date_params)
#missed_date.user = self.current_user
#missed_date.save
respond_modal_with #missed_date, location: root_path
flash[:alert] = 'Strike added'
end
Short: use "belongs_to" and "has_many :through" association between Note and MissedDates. Then you can use nested attributes.
Long version: This in probably an issue of an improper or incomplete structure of your models. Usually, you can use nested attributes (see http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html) to achieve this.
But, this implies that the models have a direct relation. You should consider if you can do a belongs_to/has_many relation between the note and the missed_date model. This could be done e.g. by "has_many :through..." (http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association) without changing your current db scheme.

Passing nested parameters for instance variables

strong textI'm trying to make dynamic select boxes via "data-remote attribute for select boxes". In console it seems that i'm getting right parameters(the id of selected make), but I can't figure it out how to pass it to controller to get models with matching make_id:s.
Heres the attached_vehicles form part from _form.html.erb
<div class="vehicle_field">
<%= f.fields_for :attached_vehicles do |av| %>
<p>Select make</p>
<%= av.select :make, (#makes.collect { |m| [m.make_name, m.id] }), { include_blank: "Select make" }, { data: { remote: true, url: "update_make_models", name: "make", update: "#diy_attached_vehicles_attributes_0_model"} } %><br>
<p>Select model</p>
<%= av.collection_select :model, #models, (render "make_models/make_model"), { prompt: "Select model" } %><br>
...
<% end %>
</div>
../views/diys/update_make_models.coffee
$.empty()
.append("<%= escape_javascript(render "make_models/make_model") %>")
../diys_controller.rb
...
def update_make_models
#models = MakeModel.where("make_id = ?", params[:make])
end
def new
#diy = Diy.new
#step = #diy.steps.new
#attached_vehicle = #diy.attached_vehicles.new
#step.add_images_to_steps.new
#makes = Make.all
#models = MakeModel.where("make_id = ?", params[:make_id])
end
...
../views/make_models/_make_model.html.erb
<% #models.collect do |models| %>
<option value="<%= models.id %>"><%= models.make_model_name %></option>
<% end %>
And here's what i'm getting in console after selecting make in makes select box
Started GET "/diys/update_make_models?diy%5Battached_vehicles_attributes%5D%5B0%5D%5Bmake%5D=12" for ::1 at 2016-02-18 20:22:35 +0200 Processing by DiysController#update_make_models as JS
Parameters: {"diy"=>{"attached_vehicles_attributes"=>{"0"=>{"make"=>"12"}}}}
MakeModel Load (1.0ms) SELECT "make_models".* FROM "make_models" WHERE (make_id = NULL)
Rendered make_models/_make_model.html.erb (3.0ms)
Rendered diys/update_make_models.coffee (491.0ms)
Completed 200 OK in 628ms (Views: 626.5ms | ActiveRecord: 1.0ms | Solr: 0.0ms)
------------------------------------------------------------------------------------------------------------------------------------
Edit
NameError (undefined local variable or method `attached_vehicles_attributes' for #<DiysController:0x5757648>):
app/controllers/diys_controller.rb:28:in `update_make_models'
Your params hash is(according to logs): {"diy"=>{"attached_vehicles_attributes"=>{"0"=>{"make"=>"12"}}}}. So, if you want to get :make_id from it, you should write:
def update_make_models
#models = MakeModel.where(make_id: params["diy"]["attached_vehicles_attributes"]["0"]["make"])
end

Ajax form not rendering partial

I'm trying to implement a follow/unfollow form to create/destroy a relationship between a user, and a company on a site. I'm having problems rendering a new partial on form submit, to change the relationship button from "follow" to "unfollow"
Currently, I render a different partial depending on whether or not a relationship already exists:
`<% unless current_user.id == #company.user_id %>
<div id="#relationship">
<% if current_user.following?(#company) %>
<%= render :partial => 'unfollow' %>
<% else %>
<%= render :partial => 'follow' %>
<% end %>
</div>
<% end %>`
The follow partial looks like:
<%= form_for(current_user.relationships.build(followed_id: #company.id), remote: true) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow",class: "btn-transparent btn-hollow btn-huge" %>
<% end %>
While the unfollow partial looks like:
<%= form_for(current_user.relationships.find_by(followed_id: #company.id), html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Unfollow", class: "btn-transparent btn-hollow btn-huge" %>
<% end %>
These use Create and Destroy methods in my Relationships_Controller:
class RelationshipsController < ApplicationController
before_filter :authenticate_user!
def create
#company = Company.find(params[:relationship][:followed_id])
current_user.follow!(#company)
respond_to do |format|
format.html { redirect_to #company }
format.js {render layout: false}
end
end
def destroy
#company = Relationship.find(params[:id]).followed
current_user.unfollow!(#company)
respond_to do |format|
format.html { redirect_to #company }
format.js{render layout: false}
end
end
end
If I set remote: false, the form works as expected, creating and destroying relationships, and the button changes on page reload. When I try to use AJAX by setting remote: true, and use the code below for relationships/create.js.erb and relationships/destroy.js.erb
$("#relationship").html("<%= j render('companies/unfollow') %>");
$("#relationship").html("<%= j render('companies/follow') %>");
However, now when I reload my page - I can click on the button once to create/destroy a relationship object. If I click again, I get a 500 error. The new partial is never loaded.
Although I'm a bit of a noob, this error in seems to point me to this line in the jquery source in chrome dev tools:
xhr.send( ( options.hasContent && options.data ) || null );
I'm using Rails 4, Jquery-Turbolinks and Devise - if any of them bare any relevance to the problem.
Incredibly frustrated now, if anyone could help that would be greatly appreciated!
Update
The log output is below. The first DELETE says that it has rendered my partial, however it has not. The second DELETE is what is happening on clicking unfollow a second time - it rightly points out that the Relationship with that id number no longer exists, as it was deleted on the first action.
Started DELETE "/relationships/429" for 127.0.0.1 at 2014-10-28 14:34:59 +0000
Processing by RelationshipsController#destroy as JS
Parameters: {"utf8"=>"✓", "commit"=>"Unfollow", "id"=>"429"}
[1m[36mUser Load (1.0ms)[0m [1mSELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1[0m
[1m[35mRelationship Load (0.4ms)[0m SELECT "relationships".* FROM "relationships" WHERE "relationships"."id" = $1 LIMIT 1 [["id", 429]]
[1m[36mCompany Load (0.3ms)[0m [1mSELECT "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT 1[0m [["id", 1]]
[1m[35mRelationship Load (0.3ms)[0m SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = $1 AND "relationships"."followed_id" = 1 LIMIT 1 [["follower_id", 1]]
[1m[36m (0.2ms)[0m [1mBEGIN[0m
[1m[35mSQL (3.6ms)[0m DELETE FROM "relationships" WHERE "relationships"."id" = $1 [["id", 429]]
[1m[36m (0.5ms)[0m [1mCOMMIT[0m
Rendered companies/_follow.html.erb (2.5ms)
Rendered relationships/destroy.js.erb (5.3ms)
Completed 200 OK in 19ms (Views: 7.0ms | ActiveRecord: 6.3ms)
Started DELETE "/relationships/429" for 127.0.0.1 at 2014-10-28 14:35:04 +0000
Processing by RelationshipsController#destroy as JS
Parameters: {"utf8"=>"✓", "commit"=>"Unfollow", "id"=>"429"}
[1m[35mUser Load (0.9ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
[1m[36mRelationship Load (0.3ms)[0m [1mSELECT "relationships".* FROM "relationships" WHERE "relationships"."id" = $1 LIMIT 1[0m [["id", 429]]
Completed 404 Not Found in 4ms
ActiveRecord::RecordNotFound - Couldn't find Relationship with 'id'=429:
You have a hash in the name of your ID.
Try changing
<div id="#relationship">
to
<div id="relationship">
When use remote: true you must change:
format.js{render layout: false}
with
format.js
Otherwise: you never render relationships/create.js.erb and relationships/destroy.js.erb

Adding Forms Dynamically From Dropdown Menu Selection

I have a Ruby on Rails question about adding a dynamic form through a drop down selection rather than having individual buttons for each possible selection.
Right now, through the help of following some Railscasts, my application works where I have three individual buttons that are able to dynamically add three different types of nested forms to the parent, all corresponding to different models with different form partials.
The parent model here is a Workout, allowing traditional_blocks, emon_blocks, and tempo_blocks to be added dynamically using JS.
Workout Model
class Workout < ActiveRecord::Base
has_many :tempos
has_many :traditionals
has_many :emons
accepts_nested_attributes_for :tempos, allow_destroy: true
accepts_nested_attributes_for :emons, allow_destroy: true
accepts_nested_attributes_for :traditionals, allow_destroy: true
end
/app/views/workouts/new.html.erb
<div>
<%= button_to_add_fields "Add EMON Block", f, :emons %>
<%= button_to_add_fields "Add traditional Block", f, :traditionals %>
<%= button_to_add_fields "Add tempo Block", f, :tempos %>
</div>
/apps/helpers/application_helper.rb
module ApplicationHelper
def button_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
button_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
app/assets/javascripts/workouts.js.coffee
$(document).on 'click', 'form .add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
Like I said earlier, everything works as I want it to when I click the buttons. However, I would like to be able to put "emon block, traditional block, tempo block" inside a collection_select with one button next to the collection_select that says "create." When that "create" button is clicked, I would like it to call that same helper(button_to_add_fields) passing along the necessary parameters for it to work the same way it does now with the multiple button implementation but using the currently selected association in the collection select.
Any tips?
You'll want to use Ajax for this
The Railscast you viewed, although helpful, is somewhat limited in the way that it will only allow you to add a single nested form each time
--
Ajax
#config/routes.rb
resources :workouts do
get "ajax_fields/:type", on: :collection
end
#app/models/workout.rb
Class Workout < ActiveRecord::Base
...
def self.build type
workout = self.new
if type
workout.send(type).build
else
workout.tempos.build
workout.traditionals.build
workout.emons.build
end
end
end
#app/controllers/workouts_controller.rb
Class WorkoutsController < ApplicationController
def ajax_update
#workout = Workout.build params[:type]
render "form", layout: !request.xhr?
end
end
#app/views/workouts/_form.html.erb
<%= form_for #workout do |f| %>
<%= render "fields", locals: { f: f }, onject: params[:type] %>
<%= f.submit %>
<% end %>
#app/views/workouts/_fields.html.erb
<%= f.fields_for type.to_sym, child_index: Time.now.to_i do |t| %>
<%= t.text_field :your_field %>
<% end %>
#app/views/workouts/new.html.erb
<%= form_for #workout do |f| %>
<%= render: "fields", locals: { f: f}, collection: ["tempos", "traditionals", "emons"], as: :type %>
<%= ... dropdown code ...%>
<%= f.submit %>
<% end %>
--
This will allow you to send the following ajax request:
#app/assets/javascripts/workouts.js.coffee
$(document).on 'change', 'form .add_fields', (event) ->
type = $(this).val
$.ajax
url: "/workouts/ajax_fields/" + type,
success: function(data) {
$("form").append(data); // will have to work this out properly
}
});
This should give you the ability to append the extra fields you need to the application, which will then come back with the appropriate HTML for you to append to your DOM
Hopefully you can appreciate the sentiment here - it might not work right out of the box!

Categories

Resources