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

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.

Related

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.

How to update two _form from one submit?

How can the current_user update his email, if he chooses, without having to have a separate submit button?
current code
<%= form_for(#challenge) do |challenge| %>
<%= challenge.action %>
<%= challenge.send_email %>
<% end %>
<%= form_for(current_user) do |user| %>
<%= user.email %>
<%= user.submit %>
<% end %>
<%= form_for(#challenge) do |challenge| %>
<%= challenge.submit %>
<% end %>
overall idea
<%= form_for(#challenge) do |challenge| %>
<%= challenge.action %>
<%= challenge.send_email %>
<%= form_for(current_user) do |user| %> # Or somehow remove form_for(current_user) altogether while still being able to update current_user.email within the form_for(#challenge)
<%= user.email %>
<% end %>
<%= challenge.submit %>
<% end %>
image of _form
Offering controllers code to see if we can make this work via fields_for
challenges_controller
class ChallengesController < ApplicationController
before_action :set_challenge, only: [:show, :edit, :update, :destroy, :challenging, :mark_accomplished, :mark_completed, :create_freebie, :like]
respond_to :html, :json
def show
#challenge_to_deadline = current_user.challenges.group_by {|i| i.deadline} if current_user
#notable = #challenge
#notes = #notable.notes
#note = Note.new
#commentable = #challenge
#comments = #commentable.comments
#comment = Comment.new
#correct_user = current_user.challenges.find_by(id: params[:id])
end
def new
#challenge = Challenge.new
respond_modal_with #challenge, location: root_path
end
def edit
end
def create
#challenge = Challenge.new(challenge_params)
if params[:step] == '2'
if current_user == nil
# If there is no user, store the lifetime values to the session.
session[:challenge_action] = challenge_params[:action]
session[:challenge_committed] = challenge_params[:committed]
session[:challenge_deadline] = [params["challenge"]["deadline(3i)"], params["challenge"]["deadline(2i)"], params["challenge"]["deadline(1i)"]].join('/')
session[:challenge_date_started] = [params["challenge"]["date_started(3i)"], params["challenge"]["date_started(2i)"], params["challenge"]["date_started(1i)"]].join('/')
session[:challenge_order] = challenge_params[:order]
session[:challenge_days_challenged] = challenge_params[:days_challenged]
session[:challenge_why] = challenge_params[:why]
session[:challenge_conceal] = challenge_params[:conceal]
redirect_to signup_path
else
#challenge = current_user.challenges.build(challenge_params)
if #challenge.conceal == true
#challenge.save
redirect_to root_path
if #challenge.date_started.present?
flash[:info] = 'habit Challenge secretly saved! Click "Strike 1" upon incompleting a day.'
else
flash[:info] = 'goal Challenge secretly saved! Click checkmark upon completing it.'
end
elsif
#challenge.save
track_activity #challenge
redirect_to root_path
if #challenge.date_started.present?
flash[:info] = 'habit Challenge saved! Click "Strike 1" upon incompleting a day.'
else
flash[:info] = 'goal Challenge saved! Click checkmark upon completing it.'
end
else
respond_modal_with #challenge
end
end
end
end
def update
#challenge.update(challenge_params)
flash[:info] = 'Challenge updated'
respond_modal_with #challenge, location: root_path
end
private
def set_challenge
#challenge = Challenge.find(params[:id])
end
def challenge_params
params.require(:challenge).permit(
:action,
:why,
:like,
:deadline,
:accomplished,
:tag_list,
:conceal,
:archive,
:trigger,
:missed_days,
:target,
:reward,
:order,
:date_started,
:date_ended,
:days_challenged,
:completed_at,
:freebie,
:freebie_date,
:send_email => [],
:committed => [])
end
end
users_controller
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
before_action :correct_user, only: [:edit, :update]
def show
#user = User.find(params[:id])
#past_challenges = #user.challenges.publish.order("deadline ASC").select{ |challenge| challenge.deadline < Date.current if challenge.deadline.present? }
#past_challenges_by_years = #past_challenges.group_by { |t| t.deadline.beginning_of_year }
#present_oneshot_challenges = #user.challenges.unaccomplished.publish.order("deadline ASC").select{ |challenge| challenge.deadline == Date.current if challenge.deadline.present? }
#present_habit_challenges = #user.challenges.unaccomplished.publish.order("date_started DESC").select{ |challenge| challenge.date_started <= Date.tomorrow if challenge.date_started.present? }
#future_challenges = #user.challenges.unaccomplished.publish.order("deadline ASC").select{ |challenge| challenge.deadline > Date.current if challenge.deadline.present? }
#future_challenges_by_years = #future_challenges.group_by { |t| t.deadline.beginning_of_year }
#inspirations = #user.inspirations.publish
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
action = session.delete(:challenge_action)
deadline = session.delete(:challenge_deadline)
committed = session.delete(:challenge_committed)
date_started = session.delete(:challenge_date_started)
order = session.delete(:challenge_order)
days_challenged = session.delete(:challenge_days_challenged)
why = session.delete(:challenge_why)
conceal = session.delete(:challenge_conceal)
# Create
if deadline.present?
#user.challenges.create(action: action, deadline: deadline, why: why, conceal: conceal, date_started: date_started, committed: committed, days_challenged: days_challenged)
end
#user.send_welcome_email
log_in #user
redirect_to tutorial_url
flash[:info] = 'Let the games begin! Add another challenge with + Challenge'
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to root_url
flash[:success] = "Settings updated"
else
render 'edit'
end
end
private
def user_params
if params[:conceal] = true
params.require(:user).permit(:time_zone, :name, :email, :tag_list, :password, :conceal, inspirations_attributes: [:name, :tag_list, :conceal], activities_attributes: [:conceal, :action, :trackable_id, :trackable_type])
else
params[:user][:conceal] = false
params.require(:user).permit(:time_zone, :name, :image, :tag_list, :email, :password, inspirations_attributes: [:name, :tag_list], activities_attributes: [:action, :trackable_id, :trackable_type])
end
end
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please sign in first"
redirect_to root_url
end
end
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
end
Embedding form within another form is not valid html. There can be multiple forms with in a page, but they cannot be embedded within each other.
If you want to have fields for different models with in the same form, depending upon the association between those models, you could make use of fields_for within the form_for to render the form fields from those models. And then when the form is submitted, again depending upon the associations between the models, you could persist the data.
You could also make use of javascript and submit/update parts of the form. For example: when the contents of the text_field have changed, you could trigger an AJAX request and persist the data.
Refer to FormHelper#fields_for for more info.
Update:
Based on your response in the comments, as you have the following models:
class User < ActiveRecord::Base
has_many :challenges
end
class Challenge < ActiveRecord::Base
belongs_to :user
end
You have two approaches.
Approach 1: Through controller action filters. Intercept the user's email from the params in the controller and update it. Something like:
class ChallengesController < ApplicationController
before_action :update_user_email, if: proc {|c| c.current_user.present? && c.params[:email].present? }
private
def update_user_email
email = params[:email]
current_user.update_attribute(:email, email)
end
end
And update your form code as follows:
<%= form_for(#challenge) do |challenge| %>
# include fields for challenge
<%= text_field_tag :email, current_user.email %>
<%= challenge.submit %>
<% end %>
Approach 2: Through model callbacks. Using a combination of attr_accessor and the callbacks in the model.
class Challenge < ActiveRecord::Base
belongs_to :user
attr_accessor :user_email
before_save :update_user_email, if: :user_email_present?
protected
def user_email
self.user.email if self.user.present?
end
def user_email_present?
self.user_email.present?
end
def update_user_email
# based on your logic in controller, looks like the
# `current_user` and the `user` of the `challenge`
# are one and the same. So you could directly update
# `email` on the `user` object.
self.user.update_attribute(:email, self.user_email)
end
end
And update your form code as follows:
<%= form_for(#challenge) do |challenge| %>
# include fields for challenge
<%= challenge.text_field :user_email, current_user.email %>
<%= challenge.submit %>
<% end %>

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!

Creating Rails 4 Form with Join Table Metadata

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)
}
});
});

Do I need Ajax or something to put different erb content in my tabs?

I want to have some tabs in an index page.
Each of the tabs should render a different category for the Food Model.
The tabs are working fine, but with static content.
I have 2 tables:
Foods, Categories
And I have a column :category_id in my Foods table.
My associations are: Food has_one category, and Categories has_many foods.
So, when clicking on one of the tabs, a category_id must be passed so that it knows what content to render.
I don't know how do I need to make my view (and maybe a scope?)...It's the first time I do this.
This is the code for one of the tabs:
<ul class="accordion-tabs">
<li class="tab-header-and-content">
Entrantes
<div class="tab-content row">
<% #foods.by_category(params[1]).each do |food| %>
<%= render "food_card" %>
<% end %>
</div>
</li>
I'm trying to pass params manually in each of the tabs, but I don't know if I need AJAX or something. Please help!
Update
My Foods Controller
def index
#addictfood = Addictfood.last
#food = Food.last
#foods = Food.all
#category = Category.find params[:id]
respond_to do |format|
format.html
format.json { render json: #category.foods.to_json }
end
end
My index view (Foods)
<ul class="accordion-tabs">
<div class="categories">
<li class="tab-header-and-content">
<% #categories.each do |tab| %>
<%= link_to tab.name, category_show_path(tab), class: "tab-link" %>
<% end %>
</li>
</div>
</ul>
</article>
<script>
$(".categories").on("click", "a", function() {
$.ajax({
url: $(this).attr("href"),
dataType: "json",
success: function(data) {
$(".category").html(data)
}
});
});
</script>
Error
Started GET "/foods" for 127.0.0.1 at 2014-07-02 20:41:53 +0200
Processing by FoodsController#index as HTML
Addictfood Load (0.3ms) SELECT "addictfoods".* FROM "addictfoods" ORDER BY "addictfoods"."id" DESC LIMIT 1
Food Load (0.3ms) SELECT "foods".* FROM "foods" ORDER BY "foods"."id" DESC LIMIT 1
Completed 404 Not Found in 4ms
ActiveRecord::RecordNotFound (Couldn't find Category without an ID):
app/controllers/foods_controller.rb:7:in `index'
If each food has one category only you should use the belongs_to category association.
In Category, you can leave it with has_many foods.
In the show view of the category controller you can pull the food like this:
<% #category.foods.each do |food| %>
<%= food.title %>
<% end %>
Params
You won't be able to pass params to your tabs if they are within a single request. Params are sent via HTTP in the URL, or behind the scenes, to give you variables in the backend of your app:
Params are there to give you the ability to manipulate the response after a request is made to the controller. In your case, with your files on the same page, you will need to use them in an ajax request to pull the relevant category content
--
Ajax
As mentioned by #mavis, you'll want to set up your models as such:
#app/models/food.rb
Class Food < ActiveRecord::Base
belongs_to :category
end
#app/models/category.rb
Class Category < ActiveRecord::Base
has_many :foods
end
This will give you the ability to list your tabs like this:
<div class="categories">
<% #categories.each do |tab| %>
<%= link_to tab.name, category_show_path(tab) %>
<% end %>
</div>
The problem you'll need to use ajax to overcome is to use it to populate your tabs, like this:
#app/assets/javascripts/application.js
$(".categories").on("click", "a", function() {
$.ajax({
url: $(this).attr("href"),
dataType: "json",
success: function(data) {
$(".category").html(data)
}
});
});
You'll then have to do this:
#app/controllers/categories_controller.rb
Class CategoriesController < ApplicationController
def show
#category = Category.find params[:id]
respond_to do |format|
format.html
format.json { render json: #category.foods.to_json }
end
end
end

Categories

Resources