My current working environment is Rails 2.3.8 (various reasons why my company hasn't moved to Rails 3).
I'm trying to update elements of a multi-model form via AJAX calls - the idea being to replace certain dropdowns depending on how the user selects or fills in other fields.
I have previously managed to get this working by using non-form based partials - the problem I have now is to reproduce the AJAX updating of the select dropdowns when the partials are based around form_for and fields_for.
Sorry for the following wall of text - i've tried to cut it down as much as possible (the code itself does work on my test site).
How do I generate the form builder elements in the Outbreak controller and then pass this to the category partial to take the place of incident_form?
Any pointers would be great :D
Models
class Outbreak < ActiveRecord::Base
has_many :incidents, :dependent => :destroy
has_many :locations, :through => :incidents
accepts_nested_attributes_for :locations, :allow_destroy => true, :reject_if => :all_blank
accepts_nested_attributes_for :incidents, :allow_destroy => true, :reject_if => :all_blank
end
class Incident < ActiveRecord::Base
belongs_to :outbreak
belongs_to :location
belongs_to :category
belongs_to :subcategory
belongs_to :subtype
end
class Location < ActiveRecord::Base
has_many :incidents, :dependent => :destroy
has_many :outbreaks, :thorugh => incidents
end
Views
_form
<% form_for(#outbreak, :html => {:multipart => true}) do |form| %>
<%= render :partial => 'outbreak_type_select', :locals => {:outbreak_types => #outbreak_types, :f => form } %>
<% form.fields_for :incidents do |incident_form| %>
<%= render :partial => 'category_select', :locals => {:categories => #categories, :incident_form => incident_form} %>
<%= render :partial => 'subcategory_select', :locals => { :subcategories => #subcategories, :incident_form => incident_form } %>
<% end %>
<% end %>
_outbreak_type_select
<% with_str = "'outbreak_type=' + value " %>
<% if #outbreak.id %>
<% with_str << "+ '&id=' + #{outbreak.id}" %>
<% end %>
<%= f.collection_select(:outbreak_type, #outbreak_types, :property_value, :property_value, {}, {:onchange => "#{remote_function(:url => { :action => "update_select_menus"}, :with => with_str)}"} ) %>
_category_select
After calling update_select_menus how to generate the incident_form
<%= incident_form.collection_select( :category_id, #categories, :id, :name, {:prompt => "Select a category"}, {:onchange => "#{remote_function(:url => { :action => "update_subcategory"}, :with => "'category_id='+value")}"}) %>
RJS
begin
page.replace_html 'outbreak_transmission_div', :partial => 'outbreaks/transmission_mode_select', :locals => {:transmission_modes => #transmission_modes }
rescue
page.insert_html :bottom, 'ajax_error', '<p>Error :: transmission modes update select</p>'
page.show 'ajax_error'
end
begin
page.replace_html 'incident_category_select', :partial => 'outbreaks/category_select', :locals => { :categories => #categories }
rescue
page.insert_html :bottom, 'ajax_error', '<p>Error :: incident category update select</p>'
page.show 'ajax_error'
end
Controllers
Outbreak
def new
#outbreak = Outbreak.new
#outbreak.incidents.build
#outbreak.locations.build
#just the contents for the dropdowns
#categories = Category.find(:all, :conditions => {:outbreak_type => "FOODBORNE"}, :order => "outbreak_type ASC")
#subcategories = Subcategory.find(:all, :order => "category_id ASC")
end
def update_select_menus
#outbreak_type = params[:outbreak_type].strip
if params[:id]
#outbreak = Outbreak.find(params[:id])
else
#outbreak = Outbreak.new
#outbreak.incidents.build
#outbreak.locations.build
end
if #outbreak_type == "FOODBORNE"
ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << #outbreak_type
#transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query})
ob_type_query = "INVESTIGATIONS:CATEGORY:" << #outbreak_type
#sample_types = Property.find(:all, :conditions => {:field => ob_type_query})
#categories = Category.find(:all, :conditions => { :outbreak_type => "FOODBORNE"})
#subcategories = Subcategory.find(:all, :conditions => { :category_id => #categories.first.id})
#subtypes = Subtype.find(:all, :conditions => { :subcategory_id => #subcategories.first.id})
elsif #outbreak_type == "NON-FOODBORNE"
ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << #outbreak_type
#transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query})
ob_type_query = "INVESTIGATIONS:CATEGORY:" << #outbreak_type
#sample_types = Property.find(:all, :conditions => {:field => ob_type_query})
#categories = Category.find(:all, :conditions => { :outbreak_type => "NON-FOODBORNE"})
#subcategories = Subcategory.find(:all, :conditions => { :category_id => #categories.first.id})
#subtypes = Subtype.find(:all, :conditions => { :subcategory_id => #subcategories.first.id})
end
respond_to do |format|
format.html
format.js
end
end
Found a work around based on http://www.treibstofff.de/2009/07/12/ruby-on-rails-23-nested-attributes-with-ajax-support/
This should probably go in Outbreak helper (in Outbreak controller atm)
def update_select_menus
#outbreak_type = params[:outbreak_type].strip
#next_child_index will only be used if
#next_child_index ? params[:next_child_index] : 0
if params[:id]
#outbreak = Outbreak.find(params[:id])
else
#outbreak = Outbreak.new
#outbreak.risks.build
#outbreak.incidents.build
#outbreak.locations.build
end
if #outbreak_type == "FOODBORNE"
ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << #outbreak_type
#transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query})
ob_type_query = "INVESTIGATIONS:CATEGORY:" << #outbreak_type
#sample_types = Property.find(:all, :conditions => {:field => ob_type_query})
#categories = Category.find(:all, :conditions => { :outbreak_type => "FOODBORNE"})
#subcategories = Subcategory.find(:all, :conditions => { :category_id => #categories.first.id})
#subtypes = Subtype.find(:all, :conditions => { :subcategory_id => #subcategories.first.id})
elsif #outbreak_type == "NON-FOODBORNE"
ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << #outbreak_type
#transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query})
ob_type_query = "INVESTIGATIONS:CATEGORY:" << #outbreak_type
#sample_types = Property.find(:all, :conditions => {:field => ob_type_query})
#categories = Category.find(:all, :conditions => { :outbreak_type => "NON-FOODBORNE"})
#subcategories = Subcategory.find(:all, :conditions => { :category_id => #categories.first.id})
#subtypes = Subtype.find(:all, :conditions => { :subcategory_id => #subcategories.first.id})
end
#pathogen_types = Property.find(:all, :conditions => {:field => "PATHOGENS:CATEGORY"})
#outbreak_types = Property.find(:all, :conditions => {:field => "OUTBREAKS:OUTBREAK_TYPE"} )
render :update do |page|
page.replace 'outbreak_transmission_div', :partial => 'transmission_mode_select_update'
page.replace 'incident_category_select', :partial => 'incident_category_select_update'
page.replace 'incident_subcategory_select', :partial => 'incident_subcategory_select_update'
page.replace 'incident_subtype_select', :partial => 'incident_subtype_select_update'
end
end
In the Outbreak view (although since this partial is related to Incident it should probably go in that view instead)
<% ActionView::Helpers::FormBuilder.new(:outbreak, #outbreak, #template, {}, proc{}).fields_for :incidents,{:child_index => #next_child_index} do |this_form| %>
<div id="incident_category_select">
<%= render :partial => 'category_select', :locals => {:incident_form => this_form } %>
</div>
<% end %>
The ActionView::Helpers::FormBuilder is used to produce the required fields_for form - The website article goes through this in more detail.
The resulting index is set by the #next_child_index variable which can be passed to the controller by the original AJAX call (for example #next_child_index = 1, then the resulting form element name will be outbreak [incidents_attributes] [1] [category_id] ) - I haven't used this in this example because although in future I want the form to support more than one location per Outbreak for this initial run-through it will just accept a single Location - Incident per Outbreak.
_category_select.erb partial (in Outbreak view atm)
<% with_str = "'category_id=' + value " %>
<% if #outbreak.id %>
<% with_str << "+ '&id=' + #{#outbreak.id}" %>
<% end %>
<%= incident_form.collection_select( :category_id, #categories, :id, :name, {:prompt => "Select a category"}, {:onchange => "#{remote_function(:url => { :action => "update_subcategory"}, :with => with_str)}"}) %>
The with_str is just to optionally pass the Outbreak id if it exists to the controller to find the Outbreak record to produce the form and if not to build a new Outbreak and associated nested attributes like Incidents and Locations.
The must be neater ways of doing this - especially the FormHelper and passing the Outbreak id via the optional with string.
Related
I tried ccavenue but I'am getting this error. paypal is working fine.
undefined methodpayment_service_for'`
This is my controller action
def create
#subscription = Subscription.new(subscription_params)
#programme = Programme.find(subscription_params[:programme_id])
rand_number = rand.to_s[2..11]
#programme.update_attributes(:invoice_id => rand_number)
session[:programme_id]=#programme.id
session[:invoice_id]=#programme.invoice_id
#paypal = PaypalPayment.create(:material_type => 'Programmes',:invoice_id => rand_number,:currency => #programme.currency, :status => 'Processing', :created_at => DateTime.now, :user_id => current_user.specific.id, :email_id => current_user.specific.email, :programme_id => #programme.id,:amount => #programme.price_paisas)
#paypal.save
`session[:paypal_id]=#paypal.id
logger.info #programme.inspect
if subscription_params[:payment_type] == 'paypal'
item_details=[]
if #programme.currency == 'INR'
price = #programme.price.exchange_to('USD')
else
price = #programme.price
end
logger.info price.inspect
item_details << {:name => #programme.title, :quantity => '1', :amount => price.fractional}
response = EXPRESS_GATEWAY.setup_purchase(price.fractional,
:items => item_details,
:currency => "USD",
:order_id => #programme.invoice_id,
:return_url => students_success_url,
:cancel_return_url => students_root_url
)
logger.info response.inspect
session[:programme_price]=price
return redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
elsif subscription_params[:payment_type] == 'ccavenue'
payment_service_for #programme.invoice_id, CCAVENUE_ACCOUNT,
:amount => #programme.price.fractional,
:currency => 'INR',
:service => :ccavenue do |service|
service.customer :name => current_user.name,
:email => current_user.email,
:phone => current_user.mobile
service.redirect :return_url => students_success_url
submit_tag 'Proceed to payment'
end
end
end
end`
I referred this link:
https://github.com/meshbrain/active_merchant_ccavenue
The payment_service_for is the view helper of the Active Merchant gem. You should use this method inside views or you should include view helpers inside your controller.
I'm having an issue with special characters (apostrophe, namely), only when present is a nested association, however.
I have a 'Vendor' model and an 'Event' model, where a Vendor has_many Events. Here are the index files:
vendor_index:
ThinkingSphinx::Index.define :vendor, :with => :active_record do
indexes :name
indexes city
set_property :min_prefix_len => 2
set_property :enable_star => true
end
event_index:
ThinkingSphinx::Index.define :event, :with => :active_record do
indexes title
indexes subtitle
indexes venue_name
indexes vendor.name, :as => :vendor_name
indexes vendor.city, :as => :vendor_city
indexes genre.name, :as => :genre_name
where "workflow_state = 'published'"
set_property :min_prefix_len => 2
set_property :enable_star => true
end
I'm using an ExcerptorPane, like so, in my search#index action :
class SearchController < ApplicationController
helper_method :format_autocomplete
def index
#events = Event.search params[:search], {:star => true , :per_page => 5, :page => params[:events_page]}
#events.context[:panes] << ThinkingSphinx::Panes::ExcerptsPane
#vendors = Vendor.search params[:search], { :star => true , :per_page => 5, :page => params[:vendors_page]}
#vendors.context[:panes] << ThinkingSphinx::Panes::ExcerptsPane
#users = User.search params[:search], { :star => true , :per_page => 5, :page => params[:users_page]}
#users.context[:panes] << ThinkingSphinx::Panes::ExcerptsPane
end
# methods used for ajax-y pagination
def vendor_results
#vendors = Vendor.search params[:search], { :star => true , :per_page => 5, :page => params[:vendors_page]}
#vendors.context[:panes] << ThinkingSphinx::Panes::ExcerptsPane
respond_to do |format|
format.js
end
end
def user_results
#users = User.search params[:search], { :star => true , :per_page => 5, :page => params[:users_page]}
#users.context[:panes] << ThinkingSphinx::Panes::ExcerptsPane
respond_to do |format|
format.js
end
end
def event_results
#events = Event.search params[:search], { :star => true , :per_page => 5, :page => params[:events_page]}
#events.context[:panes] << ThinkingSphinx::Panes::ExcerptsPane
respond_to do |format|
format.js
end
end
def get_terms
results = ThinkingSphinx.search(params[:search], {:star => true})
results.context[:panes] << ThinkingSphinx::Panes::ExcerptsPane
results_json = format_autocomplete(results)
respond_to do |format|
format.js { render :json => results_json }
end
end
private
def format_autocomplete(r)
bucket = [];
r.each do |result|
puts result.class
if result.class.name == "Event"
title = result.excerpts.title
name = result.excerpts.vendor_name
bucket << {
:label => title,
:value => title,
:category => "Events",
:subtitle => result.excerpts.subtitle,
:url => event_url(result),
:vendor_name => name,
:vendor_city => result.excerpts.vendor_city,
:genre_name => result.excerpts.genre_name,
:venue_name => result.excerpts.venue_name
}
elsif result.class.name == "Vendor"
name = result.excerpts.name
bucket << {
:label => name,
:value => name,
:category => "Vendors",
:subtitle => result.excerpts.city,
:url => vendor_url(result)
}
elsif result.class.name == "User"
name = result.excerpts.name
bucket << {
:label => name,
:value => name,
:category => "Users",
:subtitle => result.excerpts.city,
:url => user_url(result)
}
end
end
bucket
end
end
I have also included a charset_table and ignore_chars in my thinking_sphinx.yml file.
Now, when I search for a Vendor with an apostrophe in their name, everything goes fine if the Vendor has no events. If a Vendor has events though, I get an error trying to render the event's vendor_name: (the full vendor name is "VIFF's Vancity Theatre, and the search query is 'viff')
sphinxql: syntax error, unexpected IDENT, expecting ')' near 's Vancity Theatre', 'event_core', '*viff*', '<span class="match">' AS before_match, '</span>' AS after_match, ' … ' AS chunk_separator)'
raised at this line in my view:
<p><%= link_to ( raw event.excerpts.vendor_name ), vendor_path(event.vendor) %></p>
I've been searching for a while, but can't find anything of help...Any ideas as to what might be causing this?
Thanks!
UPDATE:
It gets weirder ... with vendor name "Viff's Vancity Theatre", (and all events and vendors have city = 'Vancouver' ) if I search "van" or "vanc", everything renders fine, with "Vancity" marked as a match. However if I search "vanco" it breaks again. This happens when I am performing a search on specific models. When I perform a global search however (for autocomplete), I get the opposite behaviour - 'vanco' will work, but anything shorter throws back the same error. I've updated the code above with the full search_controller.rb.
I just received a pull request for Riddle that may contain a fix for this. Try adding the following to your Gemfile:
gem 'riddle', '~> 1.5.6',
:git => 'git://github.com/pat/riddle.git',
:branch => 'master',
:ref => '50d410cda6'
I have a form on index.html.erb (User views):
<%= form_tag( '', :method => :get ) do %>
<% #company = Position.all.map { |p| [ p.company, p.company ] } %>
<%= select_tag "company", options_for_select((#company), params[:position_id]), { :include_blank => true, :reject_if => #company.blank? } %>
<% #industry = Position.all.map { |p| [ p.industry, p.industry ] } %>
<%= select_tag "industry", options_for_select((#industry), params[:position_id]), { :include_blank => true, :reject_if => #industry.blank? } %>
<%= submit_tag 'Filter', class: 'btn btn-large btn-primary' %>
<% end %>
and a controller (User controller):
def index
if params[:company] && params[:industry]
#users = User.companies(params[:company]).industries(params[:industry])
elsif params[:company]
#users = User.companies(params[:company])
elsif params[:industry]
#users = User.companies(params[:industry])
else
#users = User.all
end
end
A User has many companies and industries through positions:
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :first_name, :last_name, :position_ids
has_many :positions
has_many :companies, :through => :positions
has_many :industries, :through => :positions
has_one :current_position, :class_name => "Position", :conditions => { :is_current => true }
scope :companies, lambda { |*company| {:include => :positions, :conditions => ["positions.company = ?", company]} }
scope :industries, lambda { |*industry| {:include => :positions, :conditions => ["positions.industry = ?", industry]} }
end
Despite the if statement in my user controller, I cannot get my view to ignore blank entries in either the company or industry field. For instance, a blank company and "Internet" industry filter returns this url:
...users?utf8=%E2%9C%93&company=&industry=Internet&commit=Filter
how do I modify my code to ignore a blank company field so that the url excludes 'company=&' entirely? In this case, I get the returned results that I want:
...users?utf8=%E2%9C%93&industry=Internet&commit=Filter
thanks!
What you need to do is check to see if the parameters are blank.
if !params[:company].blank? && !params[:industry].blank?
#users = User.companies(params[:company]).industries(params[:industry])
elsif !params[:company].blank? && params[:industry].blank?
#users = User.companies(params[:company])
elsif !params[:industry].blank? && params[:company].blank?
#users = User.companies(params[:industry])
else
#users = User.all
end
When you submit the form, the variables are being set. Just that, they have blank values. So you need to check if they are blank instead of whether they exist.
I currently have this:
data = []
products.each do |product|
categories = product.shop_categories.select("shop_categories.id, shop_categories.name").map do |category|
{
:name => category.name,
:category_id => category.id.to_s
}
end
data << {
:name => product.name,
:product_id => product.productid,
:manufacturer => product.manufacturer,
:detail => product.description,
:categories => categories,
:sales_rank => product.sales_rank,
:sale_price => product.sale_price.to_f,
:price => product.price.to_f,
:images => product.images,
:url => product.url,
:is_rated => current_user.voted_for?(product),
:is_liked => current_user.voted_as_when_voted(product),
:is_in_wishlist => current_user.has_product_in_wishlist?(product)
}
end
This part where products are searched for its shop_categories are taking up a huge amount of time to query since every product (100 per run) when it searches for the products' shop_categories.
Is there a way to minimize the number of queries or at least minimize the CPU being used up by this process?
Use includes to eager-load the association:
data = Product.includes(:shop_categories).collect do |product|
{
:name => product.name,
:product_id => product.productid,
:manufacturer => product.manufacturer,
:detail => product.description,
:categories => product.categories.collect { |c| { :name => c.name, :category_id => c.id.to_s } },
:sales_rank => product.sales_rank,
:sale_price => product.sale_price.to_f,
:price => product.price.to_f,
:images => product.images,
:url => product.url,
:is_rated => current_user.voted_for?(product),
:is_liked => current_user.voted_as_when_voted(product),
:is_in_wishlist => current_user.has_product_in_wishlist?(product)
}
end
Manager :has_many :interns, :through => :assigns
Intern :has_many :managers, :through => :assigns
I am trying to create a new assign record. This is the assigns/new view where a given authenticated intern is creating a new association with a manager:
# assigns/new.html.erb
<% form_for #assign, :url => {:action => "create"} do |p| %>
<%= p.label "Select Manager", nil, :class => "label" %>
<%= collection_select :assign, :manager_id, #managers, :id, :manager_name, options ={:prompt => "Select a manager"}, html_options ={:class =>"listBox", :style => "width:25em;"} %>
<%= p.hidden_field :intern_id %>
<% end %>
# assigns controller
def new
#intern = Intern.find(params[:id])
#assign = Assign.new(:intern_id => #intern.id)
render :layout => 'centered'
end
def create
#assign = Assign.new(params[:assign])
#assign.manager_id = params[:manager_id]
if #assign.save
redirect_to :controller => "interns", :action => "home"
else
render :action => :new
end
end
The problem is: manager_id = nil. Why? Thanks very much.
Comment out following or
#assign.manager_id = params[:manager_id]
OR replace with
#assign.manager_id = params[:assign][:manager_id]
You don't need this line
#assign.manager_id = params[:manager_id]
You're already assigning the manager_id in the line before
#assign = Assign.new(params[:assign])