I've been trying to implement what should be a pretty simple feature, but I'm not having much luck. I have a drop-down menu on my ROR app which prompts the user to Select a Country.
NOTE: The list of Countries is contained in the Constants.rb file (but basically the two available options are: Interantional and United States.)
What I would like to see happen: When a user selects "United States" the div id state_nav (a div containing a drop-down menu with a list of States) would show up... if the user selects International, the state_nav menu would remain hidden.
Here is what I had thus far in terms of code (I'm using HAML)
The javascript is located in the header of the html
:javascript
function showDiv(obj)
{
if(obj[obj.selectedIndex].value == 'United States')
{
document.getElementById('state_div').style.display = 'block';
}
else
{
document.getElementById('state_div').style.display = 'none';
}
}
this is the code located in the body of the html.HAML
.section
%div{:class => "label_leftalign field"}
%p.information
please list the country, state (if applicable), and city where you're located
= f.label :country, "Location"
= f.select :country, COUNTRIES, :onChange => "showDiv(this)", :prompt => true
%div{:class => "label_leftalign field"}
#state_div{:style => "display:none;"}
= f.label :state, " ".html_safe
= f.select :state, STATES, :prompt => true
At present... when I load the page it the State drop-down (#state_div) is hidden, and it remains hidden regardless of what Country is selected.
First I would change :onChange to :onchange. Then throw an alert into your js to see if the JS is at least being called:
:javascript
function showDiv(obj)
{
alert(obj[obj.selectedIndex].value);
if(obj[obj.selectedIndex].value == 'United States')
{
document.getElementById('state_div').style.display = 'block';
}
else
{
document.getElementById('state_div').style.display = 'none';
}
}
UPDATED:
You can change your select to include missing hash param:
.section
%div{:class => "label_leftalign field"}
%p.information
please list the country, state (if applicable), and city where you're located
= f.label :country, "Location"
= f.select :country, COUNTRIES, {}, :onChange => "showDiv(this)", :prompt => true
%div{:class => "label_leftalign field"}
#state_div{:style => "display:none;"}
= f.label :state, " ".html_safe
= f.select :state, STATES, :prompt => true
I tried that on my local and it worked. Make sure that the value of your option field reflects that value that you are asserting in obj[obj.selectedIndex].value.
Related
I am creating a cinema application in Ruby on Rails and am currently working on the bookings.
What I am trying to do is implement a search so that a user can search for a free seat, select that seat, and then book that seat for a film's showing.
To do this, I want to implement 4 drop down menus: films, showings, screens and seats. But the difficulty is that what I want is the drop down menus to be dynamic and update themselves based on the previous selection, so when a user selects a film, the showing drop down only shows showings for that film, when the showing is selected the screen drop down only shows the screen that showing is in, and then the screen drop down menu is used so that the seats drop down only shows seats that are in that screen.
I have so far enabled the user to search for a screen and then when they click search the seats available are shown on the seats/index.html.erb.
This was done using the partial _booking_lookup.html.erb:
<%= form_tag my_path, :method=>'post', :multipart => true do %>
<%= select_tag ('screen_id'),
options_from_collection_for_select(#screens, :id, :screens_info, 0 ),
:prompt => "Screen" %>
<%= submit_tag 'Search' %>
<% end %>
Which is displayed on the layouts/application.html.erb:
<%= render(:partial => '/booking_lookup', :locals=> {:film => #films = Film.all, :showings => #showings = Showing.all, :screens => #screens = Screen.all, :seats => #seats = Seat.all, :my_path => '/seats/display_seats_by_screen' }) %>
And links to the display_seats_by_screen in the seats_controller:
def display_seats_by_screen
#seats = Seat.screen_search(params[:screen_id])
if #seats.empty?
# assign a warning message to the flash hash to be displayed in
# the div "feedback-top"
flash.now[:alert] = "There are no films of that genre."
# return all products, in alphabetical order
#seats = Seat.all
end
render :action => "index"
end
Which uses the screen_search in the seat.rb model:
def self.screen_search(search_string)
self.where("screen_id = ?", search_string)
end
So essentially for the above before the user selects the screen they should have selected a showing and before that a film so that only screens for that showing are shown so they can select an appropriate seat.
The models associations: film:
has_many :showings
belongs_to :certificate
belongs_to :category
showing:
belongs_to :film
has_many :bookings
belongs_to :screen
screen:
has_many :seats
seat:
belongs_to :screen
has_many :bookings
Can anyone help?
Here's an example jsfiddle:
It works on the first one, It's not dynamic though.
html:
<p>films</p>
<div data-film1='["showing1_value","showing2_value"]' data-film2='["showing3_value","showing4_value"]'>
<select name="user[films]" id="films" class="select"><option value=""></option>
<option value="film1">transformers</option>
<option value="film2">the hangover</option>
</select>
</div>
<p>showing</p>
<div data-showing1='["screen1_value","screen2_value"]' data-showing2='["screen3_value","screen4_value"]'>
<select name="user[showing]" id="showing" class="select">
<option value=""></option>
<option value="showing1_value">showing1</option>
<option value="showing2_value">showing2</option>
<option value="showing3_value">showing3</option>
<option value="showing4_value">showing4</option>
</select>
</div>
js:
$('#films').change(function(){
if ($("#films").val() == "film1"){
//you can get this films shows:
alert ( $("#films").parent().data("film1") );
//now you need to ajust the showing dropdown according: something like this:
$("#showing").html('<option value=""></option>');
$.each( $("#films").parent().data("film1"), function( index, show ){
$("#showing").append('<option value="' + show + '">' + show + '</option>');
});
}
});
Since the js only apply to this current page, I wouldn't put it in application.js (where it would be includes on every page), I would put it on the relevant page- since it only apply that page.
On the server side you would need to set the data attributes of course.
What I did was put each film's showings, as data attributes, and then when the user, selected that film, I used that film's attributes- which contains that film's relevant showings, to create the next dropdown, as seen here
Now for the rails part:
In this page controller action,
You will have something like this:
#now it doesn't matter how you do it, you can choose your own way
#but eventually you need like seen here, only that it includes all the films,
#and for each film all his showings.
#films = Film.all
#film_data_attributes_string = ""
#films.each do |film|
film_str = "data-#{film.title}=" #this should give: data-tranformers=
part_two_1 = "'[\"" #this should give '[\"
film_showings_array = film.showings #this will give the film's showins. since film has_many :showings,
showings_array_names = film_showings_array.map(&:name) #this will give an array of the showing's titles
part_two_2 = showings_array_names.join('","') #this should give a string: showing3_value","showing4_value
part_two_3 = "\"]'" #this should give "]'
#film_data_attributes_string << film_str + part_two_1 + part_two_2 + part_two_3 + " "
#all together: data-tranformers='["showing1_value","showing2_value"]'
#notice there is a space in the end
end
this will give you (more or less) the data attributes of the films. It's not tested, but that's the main idea.
assuming both your films and showings have an attributes called "name".
(of course it would be better to move all that functionality to the film model and not use that in controller)
Then in the view, you will use #film_data_attributes_string of course, maybe you will need to use html escape or something, not sure.
Or you can use this answer
and use this instead:
#data_hash = {}
#films.each do |film|
#data_hash["data-#{film.title}"] = film.showings.map(&:name).to_json
end
You will need to do something similar for the java script.
I have a Rails 3.2.18 app in which I'm doing a simple create/update action on a model.
There's a field called sms which I used ActionMailer to send a message to a person's phone in the form of 2812222222#vtext.com. Currently I manually type this in which is fine, but I want other's to be able to enter a phone number, select a carrier and have these two items merge into the proper format in the sms field again being 2812222222#vtext.com. Makes it a lot more user-friendly than having to know the full sms-to-email address by heart.
I assume I can do most of this via JS/JQuery, but I'm not sure on how to get started.
I'd like them to be able to enter the phone number in a 281-555-4444 format and have the carrier select as a drop-down of Tmobile, Verizon, etc and substitute #tmomail.net, #vtext.com, etc, strip (regex) the phone number and merge it with the carrier (#vtext.com) into the sms field.
If anyone can point me in the right direct, it would be appreciated.
Update:
I figured I could use a helper method to define the carriers:
def phone_carriers
{
"All Tell" => "#message.alltel.com",
"AT&T" => "#txt.att.net",
"Boost" => "#myboostmobile.com",
"Cellular South" => "#csouth1.com",
"Centennial Wireless" => "#cwemail.com",
"Cincinnati Bell" => "#gocbw.com",
"Cricket Wireless" => "#sms.mycricket.com",
"Metro PCS" => "#mymetropcs.com",
"Powertel" => "#ptel.net",
"Qwest" => "#qwestmp.com",
"Rogers" => "#pcs.rogers.com",
"Sprint" => "#messaging.sprintpcs.com",
"Suncom" => "#tms.suncom.com",
"T-Mobile" => "#tmomail.net",
"Telus" => "#msg.telus.com",
"U.S. Cellular" => "#email.uscc.net",
"Verizon" => "#vtext.com",
"Virgin Mobile USA" => "#vmobl.com"
}
end
Add attr_accessor :carrier to the Person model
Then in the form do something like this:
<%= f.input :phone, placeholder: "555-555-5555", required: true, input_html: {required: true} %>
<%= f.select :carrier, phone_carriers.map{ |k, v| [k, v] }, {include_blank: true}, {placeholder: "Select your phone carrier", class: "select2"} %>
But I'm still having problems figuring out how to strip the phone number to 5555555555 and merging the :phone field with the :carrier object into the :sms field automatically (preferably client side)
After doing a lot of research and breaking things, I've come up with the following to make this work. The model and form only show what changes I've made, not the full form or model.
Form
<%= f.label 'Cell_Phone'%>
<%= f.text_field :phone, placeholder: "xxx-xxx-xxxx", required: true %>
<%= f.label :carrier, "SMS Notifications", class: "control-label" %>
<%= f.select :carrier, phone_carriers.map{ |k, v| [k, v] }, {include_blank: true}, {placeholder: "Select your phone carrier", class: "select2"} %>
<%= f.hidden_field :sms %>
Model
attr_accessible :carrier
attr_accessor :carrier
CoffeeScript
$ ->
$("#person_carrier").on "change", update_sms_address
$("#person_phone").on "keyup", update_sms_address
update_sms_address
update_sms_address = ->
digits = $("#person_phone").val().match(/\d+/g)
phone = digits.join("") if digits
carrier = $("#person_carrier").val()
if phone? && phone.length == 10 && carrier
sms = phone + carrier
$("#person_sms").val(sms)
$("#person-sms-number").html(sms)
else if !carrier
$("#person_sms").val("")
$("#person-sms-number").html("Choose a carrier to receive SMS notifications")
else
$("#person_sms").val("")
$("#person-sms-number").html("Invalid phone number")
This seems to work when entering a phone number in the format of 281-444-4444 as it strips the number down and joins it with the value from the carrier array and sets the hidden :sms field appropriately.
The only problem I have now is since I'm using a virtual attribute for the carrier, if I go to edit the person again it doesn't retain the carrier select and leaves it blank. If I submit the form without changing any info it will retain the :sms field, but it could be confusing since the :carrier is not an actual field that is remembered by the database but instead a virtual attribute.
Is there a better way to go about doing this?
If you used the standard javascript string library, wouldn't something like
function prepare(str){
str2 = str.replace("(","").replace(")","").replace("-","")
carrier = getCarrier //assuming this implemented by you somewhere
return (st2.concat(carrier))
}
I would start by creating a carriers table:
rails generate model Carrier name:string email_domain:string
And referencing it in the Person model by adding the following to your migration:
change_table :people do |t|
t.references :carrier
end
add_index :people, :carrier_id
And the following to your People model:
belongs_to :carrier
validates_presence_of :carrier
def sms
carrier ? "#{phone}##{carrier.email_domain}"
end
def phone_with_dashes
"#{phone[0..2]}-#{phone[3..5]}-#{phone[6..9]}"
end
def phone_with_dashes=(_phone_with_dashes)
self.phone = _phone_with_dashes.gsub(/-/ ,'')
end
Now, your view (I'll leave the formatting to you):
Phone: <%= f.phone_field :phone_with_dashes %>
Carrier: <%= f.collection_select :carrier_id, Carrier.all, :id, :name %>
<div id='person-sms-number'>
<%= f.object.sms %>
</div>
Lastly, the Coffeescript: The only thing that has to change here is you no longer need the '#person_sms' input since you are no longer persisting the value directly--you are generating it dynamically from other fields.
I have an application in which I'm using the gems rails3-jquery-autocomplete and nested_form to suggest existing users to be tagged as contributors to a project entry.
The form view:
<%= f.fields_for :contributors do |contributor|%>
<%= contributor.autocomplete_field :name, autocomplete_user_profile_last_name_projects_path, :update_elements => { :user_id => "#user_id" } %><br>
<%= contributor.hidden_field :user_id %>
<%= contributor.radio_button :contributor_type, 'Colleague' %>
<%= contributor.radio_button :contributor_type, 'Supervisor' %>
<%= contributor.radio_button :contributor_type, 'Client' %>
<%= contributor.link_to_remove "Remove this contributor" %>
<% end %>
<%= f.link_to_add "Add a contributor", :contributors %><br>
I have also overwritten the get_autocomplete_function(parameters) function in order to allow more than one column in my autocomplete, namely the columns first_name and last_name
def get_autocomplete_items(parameters)
items = UserProfile.select("DISTINCT CONCAT_WS(' ', first_name, last_name ) AS full_name, first_name, last_name, id, user_id").where(["CONCAT_WS(' ', first_name, last_name) LIKE ?", "%#{parameters[:term]}%"])
end
I also have the following line in my Projects controller:
autocomplete :user_profile, :last_name, :full => true, :extra_data => [:first_name, :last_name, :user_id ], :display_value => :fullname
With the `fullname' function simply being:
def fullname
"#{first_name} #{last_name}"
end
What I want to do is to get the value of user_id from the autocomplete items and put its value inside the hidden field, but when I check the server the it outputs something like:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"DYQ7iJeqdwMQrprSGn0WNHXY5iXDZLA5pUd3OlYJ2so=", "project"=>{"creator"=>"1", "title"=>"lesdodis", "contributors_attributes"=>{"0"=>{"name"=>"Test User the 1st", "user_id"=>"", "contributor_type"=>"Client", "_destroy"=>"false", "id"=>"21"}, "1"=>{"name"=>"Test User the 2nd", "user_id"=>"", "contributor_type"=>"Supervisor", "_destroy"=>"false", "id"=>"22"}, "1393677266696"=>{"name"=>"", "user_id"=>"", "_destroy"=>"1"}, "1393677267518"=>{"name"=>"", "user_id"=>"", "_destroy"=>"1"}}, "description"=>"asdad", "tag_list"=>"sdads", "link"=>"asdasd", "gallery_attributes"=>{"screenshots_attributes"=>{"0"=>{"description"=>"", "_destroy"=>"false", "id"=>"10"}}, "id"=>"16"}}, "commit"=>"Update", "id"=>"16"}
where the user_id field inside contributors is left blank. I have already tried manually assigning ids to the fields by using contributor.object.id to assign a unique number to the field name with limited success, as the new dynamically added fields do not have object ids. I am using this form for both creating and updating a project entry, so I would like to ask for help in how to make this work regardless of editing the field or adding a new one.
Update:
After a while of searching I finally got it to work by adding this code
<% field_name = "#{contributor.object_name}[user_id]".gsub(/(\])?\[/, "_").chop %>
<%= contributor.autocomplete_field :name, autocomplete_user_profile_last_name_projects_path, :update_elements => { :user_id => "##{field_name}" } %><br>
I hope this helps anyone who might encounter the same problem.
I've been wrestling with a similar problem as well. In your case, my feeling is that the nested_form helpers are generating unique identifiers which are defeating the "#user_id" selector passed to :update_elements.
You might be able to workaround it by hooking into the event streams for each gem and then manually updating the hidden :user_id for the specific contributor in the form. For the dynamically added contributors, possibly something like:
$(document).on('nested:fieldAdded', function(nf_event) {
var ac_input = nf_event.field.find('.ui-autocomplete-input');
ac_input.bind('railsAutocomplete.select', function(ac_event, data) {
$(this).next('input[type=hidden]').val(data.item.id);
});
});
For your contributors generated through existing associations (ie. editing), you can bind to their autocomplete inputs on $(document).ready(). This might at least get you pointed in the right direction.
After a while of searching I finally got it to work by adding this code
<% field_name = "#{contributor.object_name}[user_id]".gsub(/(\])?\[/, "_").chop %>
<%= contributor.autocomplete_field :name, autocomplete_user_profile_last_name_projects_path, :update_elements => { :user_id => "##{field_name}" } %>
I hope this helps anyone who might encounter the same problem.
I have a form where I need the user to select (with radio buttons) an option that will determine the next form field beneath it. In this case, their post can be a post or a revision of a post. Thus, selecting 'post' will show a title field (to name the post) and selecting 'revision' will show a select list of existing posts to choose from (the title will be inherited).
_form.html.erb
<!--Form inputs-->
<%= f.collection_radio_buttons :is_revision, [[false, 'Post'] ,[true, 'Revision']], :first, :last %>
<%= f.input :revision_id, collection: #originals, :input_html => {:id => 'revision'} %>
<%= f.input :title, :input_html => { :maxlength => '50', :placeholder => 'Title', :id => 'title'} %>
<!--Javascript-->
<script type="text/javascript">
$('input[name="post[is_revision]"]').on("change", function(){
if ( $("#post_is_revision_true").attr("checked")){
$("#title").hide();
$("#revision").show();
}
if ( $("#post_is_revision_false").attr("checked")){
$("#revision").hide();
$("#title").show();
}
});
</script>
I should mention that this works fine when hiding and showing one field, yet when selecting the other radio button nothing changes. I feel the logic is correct (although I'm not the strongest with JS) but I can't seem to get this.
EDIT: I made a few jQuery errors. Also, here is a jsFiddle example
Try this.
$('input[name="post[is_revision"]').change(function() {
var selected = $('input:checked[name="post[is_revision]"]').val();
if(selected == 'revision') {
$('#title').hide();
$('#revision').show();
} else if(selected == 'new_post') {
$('#revision').hide();
$('#title').show();
}
});
Another way to do it, smaller but more confusing:
$('input[name="post[is_revision"]').change(function() {
var is_revision = $('input:checked[name="post[is_revision]"]').val() == "revision";
$('#title').toggle(!is_revision);
$('#revision').toggle(is_revision);
});
If one will always be selected first, you could condense it to this:
$('input[name="post[is_revision"]').change(function() {
$('#title').toggle();
$('#revision').toggle();
});
Or for kicks and giggles and slides:
$('input[name="post[is_revision"]').change(function() {
$('#title').slideToggle();
$('#revision').slideToggle();
});
Comment if you have problems with any.
I have a hash stored as a constant defined in application.rb
It looks a bit like this:
ITEMS = { "Item 1" => ['1 - sdfsdf', '2 - sdlfksdf'], "Item 2" => ['1 - lkfsdf', 2- dkfdjk']}
What I would like from this is one combo box with the options:
Item 1
Item 2
and a second combo box with the items in the array in the hash depending on the selection of the first.
Is there an easy way to do this with rails 3 (I am using JQuery)
Thanks
You could create the first box with the following:
<%= f.select :var_name, ITEMS.collect { |key, value| [key, key] } %>
Then, add an observer
<%= observe_field 'element_var_name',
:url => { :action => "another_action_here" },
:update => "div_tag_to_update",
:with => "'selected='+ escape($('element_var_name').value)" %>
Be sure to adjust element_var_name and the action to your situation. The another_action_here action should render a view like this:
def call_ids_by_type
#element_list = ITEMS[param[:selected].collect { |value| [value, value] }
render :layout => false
end
The associated view should only contain the select field you want to add. Haven't tried it exactly, but I think this should work.