Dynamic update nested fields_for - javascript

I've been struggling with this issue for weeks and haven't been able to find or figure out a solution. Here is what I'm trying to accomplish: a form that is automatically populated with a list of questions based on user input. Specifically, I have a list of all possible questions (PossibleQuestion model) that I want to use to populate the form based on the site a user selects.
I know I'm not rendering a proper response to the ajax request. I'm currently getting an undefined local variable or method f error. I found several SO questions regarding passing the form builder (here and here), however none of them have helped.
I'm still new to ruby and rails, so any help or direction is greatly appreciated!
Versions (ruby 2.3, rails 4.2.2). Also using SimpleForm and Cocoon gems.
report.rb
class Report < ActiveRecord::Base
belongs_to :site
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :allow_destroy => true
end
site.rb
class Site < ActiveRecord::Base
has_many :reports
validates :state, presence: true
end
question.rb
class Question < ActiveRecord::Base
belongs_to :report
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :allow_destroy => true
end
possible_question.rb
class PossibleQuestion < ActiveRecord::Base
validates :content, :state, presence: true
end
answer.rb
class Answer < ActiveRecord::Base
belongs_to :question
end
reports_controller.rb
def new
#report = Report.new
#questions = PossibleQuestion.all
#questions.each do |q|
question = #report.questions.build
question.content = q.content
question.answers.build
end
end
def questions_list
#site = Site.find(params[:id])
#state = #site.state
#questions = PossibleQuestion.where(state: #state).to_a
respond_to do |format|
format.js
end
end
report.js
$(document).ready(function() {
$('#report_site_id').change(function() {
site_id = $('#report_site_id').find(':selected').val();
$.ajax({
url: '/questions_list',
data: { id: site_id, },
questions_list.js.erb
$(".questions").html("<%= j render 'question_fields', :locals => { :questions => #questions } %>");
I've tried passing the formbuilder object f here in locals, however it doesn't work. I get an error that f is undefined.
_question_fields.html.erb
<%= f.simple_fields_for :questions do |builder| %>
<%= f.label f.object.content %>
<%= f.hidden_field :content, :value => f.object.content %>
<%= f.simple_fields_for :answers do |builder| %>
<%= render 'answer_fields', :f => builder %>
<% end %>
<% end %>
_form.html.erb
<%= simple_form_for #report do |f| %>
<%= f.association :site %>
<div class="questions"></div>
<% end %>

Related

Jquery Token Input with has_many association and join table.?

I have 3 models
class Store < ActiveRecord::Base
has_many :storedescriptions
has_many :descriptions , through: :storedescriptions
def self.tokens(query)
stores = where("name like ?", "%#{query}%")
if stores.empty?
[{id: "<<<#{query}>>>", name: "New: \"#{query}\"" }]
else
stores
end
end
def self.ids_from_tokens(tokens)
tokens.gsub!(/<<<(.+?)>>>/) { create!(name: $1).id }
tokens.split(',')
end
end
Description Model
class Description < ActiveRecord::Base
has_many :storedescriptions
has_many :stores , through: :storedescriptions
end
and storedescription model
class Storedescription < ActiveRecord::Base
belongs_to :user
belongs_to :store
belongs_to :description
attr_reader :store_tokens
def store_tokens=(tokens)
Store.ids_from_tokens(tokens)
end
end
I have a form for storedescription
<%= simple_form_for(#storedescription) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :store_tokens,:label => "Add Store Name", input_html: { class: 'priceaddtokendesc form-control'} %>
<%= f.input :description_id, :as => "hidden",:input_html => { :value => item.id } %>
<%= f.input :price %>
<%= f.input :user_id , :as => "hidden",:input_html => { :value => current_user.id } %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
In script
<script type="text/javascript">
$(".priceaddtokendesc").tokenInput("/stores.json", {
crossDomain: false,
prePopulate: $(".priceaddtokendesc").data("pre"),
theme: "facebook",
allowFreeTagging: true,
resultsLimit: "10",
zindex: 9999,
propertyToSearch: "name",
allowCreation: true,
creationText: 'Add new element',
preventDuplicates: true
});
On create i would like to store Store_id => store_tokens, also i have tried adding through controller but it won'work.
def create
#storedescription = Storedescription.new(storedescription_params)
#storedescription.store_id = #storedescription.store_tokens
#storedescription.save
respond_with(#storedescription)
end
But every time i get null result.
What is the best way to implement this process?

NoMethod error fields Nested Attributes

I've been following this railscast: https://www.youtube.com/watch?v=t_FNKR7jahM for learning how to implement nested attributes into my application. When I implement his code in the certain areas necessary I always get the same NoMethod error on the fields.
On the events show page where you can add songs, I am trying to add a 'Add Field' link that will add another group of songs(a field for artist, title, and genre).
Here is my EventController:
class EventsController < ApplicationController
def new
#event = Event.new
#event.songs.build
end
def index
#songs = Song.all
end
def show
#event = Event.find(params[:id])
#songs = #event.songs.paginate(page: params[:page])
end
def create
#event = current_user.events.build(event_params)
if #event.save
flash[:success] = "Event Created!"
redirect_to user_path(#event.user)
else
render 'welcome#index'
end
end
def destroy
end
private
def event_params
params.require(:event).permit(:name, :partycode, song_attributes: [:artist, :title, :genre])
end
end
Here is my Event model:
class Event < ApplicationRecord
belongs_to :user
has_many :songs, dependent: :destroy
accepts_nested_attributes_for :songs, allow_destroy: true
validates :name, presence: true
validates :partycode, presence: true, length: {minimum: 5}
end
Here is the show page in which the songs are added:
<section class="song_form">
<%= render 'shared/song_form' %>
<%= form_for #event do |f| %>
<%= f.fields_for :fields do |builder| %>
<%= render 'events/songs_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Field", f, :fields %>
<% end %>
</section>
Here is the song_fields file:
<fieldset>
<%= f.select :field_type, %w[text_field check_box] %>
<%= f.text_field :artist, placeholder: "Artist" %>
</fieldset>
Here is the ApplicationHelper file :
module ApplicationHelper
def link_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
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
Finally, here is my events coffee script file;
$(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()
Sorry for the lengthy post, an answer to my question would be GREATLY appreciated. Let me know if there is anything else you need from me(note I am using rails 5). Cheers :)
Should be
f.fields_for :songs do |builder|

undefined method `events' for nil:NilClass

I was working with nested attributes, everything seemed to be fine until when I submitted my information and I got this error. It says it is in my EventsController file:
class EventsController < ApplicationController
def new
#event = Event.new
#event.songs.build
end
def index
#songs = Song.all
end
def show
#event = Event.find(params[:id])
#songs = #event.songs.paginate(page: params[:page])
end
def create
#event = current_user.events.build(event_params)
if #event.save
flash[:success] = "Event Created!"
redirect_to user_path(#event.user)
else
render 'welcome#index'
end
end
def destroy
end
private
def event_params
params.require(:event).permit(:name, :partycode, song_attributes: [:event_id, :artist, :title, :genre, :partycode])
end
end
Here is my new.html.erb file in my songs view(handles song submission based on selected event)
<br>
<br>
<div class ="container">
<div class="jumbotron">
<%= form_for Event.new do |f| %>
<h3>Enter a song:</h3>
<%= f.fields_for :songs, Song.new do |song_form| %>
<%= song_form.collection_select(:event_id, Event.all, :id, :name) %>
<%= song_form.text_field :artist, placeholder: "Artist" %>
<%= song_form.text_field :title, placeholder: "Title" %>
<%= song_form.text_field :genre, placeholder: "Genre" %>
<% end %>
<%= link_to_add_fields "Add Song", f, :songs %>
<%= f.text_field :partycode %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
The link_to_add_fields method is defined in my ApplicationHelper.rb file:
module ApplicationHelper
def link_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("songs_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
current_user is defined in Session_helper.rb file:
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
def createEvent(event)
session[:event_id] = event.id
end
# Returns the current logged-in user (if any).
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
def log_out
session.delete(:user_id)
#current_user = nil
end
end
Finally, here is my songs_fields file that generates fields only when a user clicks a link that says 'Add songs'
<fieldset>
<%= f.text_field :artist, placeholder: "Artist" %>
<%= f.text_field :title, placeholder: "Title" %>
<%= f.text_field :genre, placeholder: "Genre" %>
</fieldset>
I feel as though this is the last portion before I get everything in my app to work! So help on this would be tremendous :D
You answered your own question... if you're not logged in, current_user will be nil so you will get this error.
Option 1 (ugly): change your logic so current_user.events doesn't get called if current_user is nil, and just go straight to the render
Option 2 (better): use a before_action statement to force current_user to be set before the action is run. Depends on what you're using to authenticate, but with Devise it would look like this:
class EventsController < ApplicationController
before_action :authenticate_user!
I think maybe:
class EventsController < ApplicationController
before_action :log_in(user)
might do it for you.

Initializing Object with nested form not working

I have a model of a brand that can have many products that can have many categories. I have a nested form to create products that permit nested attributes for creating categories. But I can make it work.
class Brand < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :products, dependent: :destroy
validates :name, presence: true,
length: { maximum: 50 }
end
class Product < ActiveRecord::Base
belongs_to :brand
has_many :categories, dependent: :destroy
accepts_nested_attributes_for :categories
default_scope -> { order(created_at: :desc) }
validates :brand_id, presence: true
validates :name, presence: true,
length: { maximum: 50 }
private
def product_params
params.require(:product).permit(:name,
categories_attributes: [:name, :price])
end
end
class Category < ActiveRecord::Base
belongs_to :product
has_many :units, dependent: :destroy
validates :price, presence: true
validates :product_id, presence: true
validates :name, presence: true,
length: { maximum: 50 }
end
So my product controller is:
class ProductsController < ApplicationController
def new
#product = current_brand.products.new
#product.categories.build
end
def create
#product = current_brand.products.build(product_params)
if #product.save
redirect_to root_url
else
render 'new'
end
end
and my new view is like this:
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#product) do |f| %>
<%= render 'shared/error_messages_products' %>
<%= f.label :name, "Name:" %>
<%= f.text_field :name, class: 'form-control' %>
<%= link_to_add_fields "Add Category", f, :categories %>
<%= f.submit "Add Product", class: "btn btn-primary" %>
<% end %>
</div>
</div>
and my category partial is:
<fieldset>
<%= f.label :name, "Category Name" %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :price, "Price" %>
<%= f.text_field :price, class: 'form-control' %>
<hr>
</fieldset>
I have the link_to_add_fields helper in my application helper:
module ApplicationHelper
def link_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
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
That allows me to use some Javascript to add category fields with:
jQuery ->
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
But when I try to add a product with any number of categories in this example 2, I fail to create the products and the categories. I get the error from my form and object error:
The form contains 1 error:
Categories product can't be blank
The params I get from this submition are:
{"utf8"=>"✓", "authenticity_token"=>"IO8GFcv1auFVh/ZNypONI78XQrY2Ntm07cMrrjmq51ogwppbsb1sNyN/ynKY+Pdb/lyniED9O6jFRkLKsvu2jQ==", "product"=>{"name"=>"Product Example", "categories_attributes"=>{"1467231299616"=>{"name"=>"Category Example 1", "price"=>"1234"}, "1467231300745"=>{"name"=>"Category Example 2", "price"=>"1234"}}}, "commit"=>"Agregar Producto", "controller"=>"products", "action"=>"create"}
I don't understand why the category and the product are not associating correctly.
After a while of experimenting I found that the answer is to remove the validation of product_id from the category model. Like this:
class Category < ActiveRecord::Base
belongs_to :product
has_many :units, dependent: :destroy
validates :price, presence: true
validates :name, presence: true,
length: { maximum: 50 }
end

Change a variable in form based on selection within form

UPDATE 6/21/12
I have a form in rails that is working similar to an e-commerce checkout.
The user selects a start hour, end hour, date, time and price preference(hourly, weekly, daily, etc.) in the form. The product already has a set price in the database that I'm converting to (hourly, weekly, daily, etc.) that I'd like to change based on the price preference and then get submitted with the rest of the form.
I've been following Ryan's screencast on dynamic select menus but I'm wasn't sure how I'd fit his demo into my application since I don't want the user to select the price just the measure (daily/hourly/etc.)
Gear has_many line_items
line_items belongs to Gear and Carts
Below is my code:
Gear Model (where I've created the price variables)
class Gear < ActiveRecord::Base
attr_accessible :title, :size, :price, :sub_category_id, :user_id, :image, :image_a, :remote_image_url, :color, :year, :latefee, :cancellation, :minrental, :policy, :about, :address, :city, :state, :zip
belongs_to :user
belongs_to :sub_category
has_one :category, :through => :sub_category
has_many :comments, :dependent => :destroy
has_many :line_items
has_many :calendars
require 'carrierwave/orm/activerecord'
mount_uploader :image, GearpicUploader
mount_uploader :image_a, GearpicUploader
before_destroy :ensure_not_referenced_by_any_line_item
validates :title, presence: true
validates :size, presence: true
validates :price, presence: true
validates :sub_category_id, presence: true
validates :user_id, presence: true
def hourly_price
price/24
end
def weekly_price
price*7
end
def monthly_price
weekly_price*4
end
end
View
<div class="gearside_date_top">
<%= form_for LineItem.new do |f| %>
<p><%= render :partial => "price" %> /
<%= f.select :day_or_hour, [['day', 1], ['hour', 2]], :id => 'day_or_hour' %>
</p>
</div>
Partial Given to View
<em>from</em>
<span id="gear_daily" style="font-size: 190%; color: #94cbfc;">$<%= #gear.price %></span>
<span id="gear_hourly" style="font-size: 190%; color: #94cbfc; display:none;">$<%= #gear.hourly_price %></span>
Javascript
$('#day_or_hour').change(function() {
var $index = $('#day_or_hour').index(this);
if($('#day_or_hour').val() = '2') {
$('#gear_hourly').show();
}
else {
$('#gear_daily').show();//else it is shown
}
});
I can't seem to get it working.
You can put your price in a partial:
<div class="gearside_date_top">
<%= form_for LineItem.new do |f| %>
<%= render :partial => "price" %>
<%= f.select :day_or_hour, [['day', 1], ['hour', 2]], :id => 'day_or_hour' %>
_price.html.erb
<em>from</em><span style="font-size: 190%; color: #94cbfc;">$<%= #gear.price %></span>
In your line_item.js you put the code to update the price:
line_item.js
$('#day_or_hour').change(function() {
$.get("/cart/update_price", {option: $(this).val()}, function(data) {
}
}
On cart_controller.rb you create the method update_price that makes a call to line_item.rb total_price.
Hope you understand.
edit
It would look like something like this.
def update_price
#gear.price = LineItem.total_price(#gear)
render :partial => 'price'
end

Categories