SitePoint Sponsor

User Tag List

Results 1 to 16 of 16

Thread: Drop Down Menu

  1. #1
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Drop Down Menu

    Hey guys -

    I'm trying to use a drop-down menu to allow a user select a value for submission along with some text-input fields. I have a submit_tag handling the submission.

    I can't figure out what's wrong with this syntax:

    Code:
    <td width = 275px, align = "center">
    		<select name= "status" >
    			<option value="option1">Option 1</option>
    			<option value="option2">Option 2</option>
    			<option value="option3">Option 3</option>
    		</select>
    		
    	</td>

    I have "validates_presence_of :status" in my project model, which seems to be doing its job because the page just refreshes when I submit. Without the validation, it claims status is nil.

    Thanks for your time!

  2. #2
    SitePoint Evangelist
    Join Date
    Feb 2006
    Location
    Worcs. UK
    Posts
    404
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The important bit that you appear to be missing is the controller code. You need to catch the value returned by the page in the controller code via params and then pass it to an instance of the model. So this would work:
    Code:
    def edit
      thing = Thing.new
      thing.status = params[:status]
      thing.save
    end
    That would work but it is not using Rails to its best. If you'd prefer working with rails you're better sticking to the conventions. This is an easier way to go about it:

    Controller
    Code:
    def edit
      @status_options = ['option_1', 'option_2', option_3']
      thing = Thing.new
      if request.post? and params[:thing]
        thing.attributes = params[:thing]
        thing.save
      end
    end
    View
    Code:
    <&#37;=  error_messages_for(:thing) %>
    <% form_tag do %>
    <%= select (:thing, :status, @status_options.collect{|o| [o.humanize, o]}) %>
    <%= commit_tag %>
    <% end %>
    If you look at the rails api, you should be able to work out how I used @status_options to generate the select list using the select helper method.

    By default, rails methods return items from forms in the format:
    Code:
    params[:object_name][:method_name]
    And you should be able to see how I used that format to catch the form fields. I used thing.attributes as that allow me to catch all the form fields in one hit. I could have done this instead, but then I'd have to enter a line of code for each form element:
    Code:
      if request.post? and params[:thing]
        thing.status = params[:thing][:status]
        thing.save
      end
    The error_messages_for method will display any problems caught by your validation in the model.

  3. #3
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the generosity. I'm having a little trouble with the "humanize" bit...any brief explanation of the iteration would be appreciated.

    I actually have a global array "@status_values" in my application controller. I was trying to use this as the "choices" attribute for the select function, but I couldn't get it to work.

    Thank you again for your time

  4. #4
    SitePoint Evangelist
    Join Date
    Feb 2006
    Location
    Worcs. UK
    Posts
    404
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Code:
    <&#37;= select (:thing, :status, @status_options.collect{|o| [o.humanize, o]}) %>
    The select method needs the object name &#040;:thing), and the method &#040;:status) which it uses to create the field name and ids. It then also needs an array in this form:
    Code:
    [  ["Label 1", "label_1"], ["Label 2", "label_2"], ["Label 3", "label_3"] ]
    That is an array of arrays where each sub array consists of the label that will appear in the drop down and the data that will be returned by the form where that option is selected.

    Select will convert that array of arrays into:
    Code:
    <option value="label_1">Label 1</option>
    <option value="label_2">Label 2</option>
    <option value="label_3">Label 3</option>
    The array method 'collect' iterates over the array replacing each element with the code generated in the call out. So:
    Code:
    test_array = ['an_entry']
    result_array_1 = test_array.collect{|e| [e, e]}
    result_array_2 = test_array.collect{|e| [e.humanize, e]}
    The result arrays will contain an array with a single sub-array whose content will be:
    Code:
    result_array_1  ---->    [ ["an_entry", "an_entry"] ]
    result_array_2  ---->    [ ["An entry", "an_entry"] ]
    So in the original, the collect call converts the array into an array of pairs with the first element in each pair being the humanized version of the original text and the second being a copy of the original text. Select then users those pair to generate the options list.

  5. #5
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    :hug:

    Thank you VERY much. hopefully this helps some future form-doers.

    I did make a slight change; I defined the array of sub arrays explicitly in my application controller and I didn't use "commit_tag" - ruby didn't recognize it. It's working though:

    Code:
    	<% form_tag do %>
    		<%= select (:project, :status, @status_options) %>
    	<% end %>
    Thanks again for helping .

  6. #6
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Here's another related question - thank you for looking.

    My objective is to have a select menu with submittable options (currently working) that dynamically generates another select menu according to what's been selected. I won't need to access anything from a database.

    The mother select-menu would have Status 1, Status 2, and Status 3.
    The child select-menu would have Level 1, Level 2, Level 3. The values of Level change according to each Option. Eg Option 1's Level 1 would be 20, wheras Option 2's Level 1 would be 40.

    I think it would be something like this:

    Code:
    <&#37; form_tag do %>
    	<%= select (:project, :status, @status_options, {onchange => ( select (:project, :level, @%status%_options}) %>
    <% end %>
    Where %status% is the value of the selected mother select-menu's Status.

    How exactly would I get the %status% working?

  7. #7
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Just posting my progress...

    I decided to generate each Option's form in separate <div>s, and have them hidden until the actual Option is selected. The <div> is then supposed to appear. I currently have each div set to

    Code:
     style = "display:none"
    But I would like to "toggle" this hidden attribute according to the selected Option. I'm assuming I'll need to use JavaScript...any pointers would be helpful.

    I'll post up my solution if I figure it out .

  8. #8
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I feel like I'm so close! Not quite working..

    Code:
    <&#37; form_tag do %>
    <%= select (:project, :status, @status_options, {:include_blank => nil},{:onchange => "i=this.options[this.selectedIndex].text; if(i=='option1'){ show('option1'); hide('option2'); hide('option3');};"}) %>
    					
    <% end %>
    Where the hide/show methods are

    Code:
    function hide(s) {
    s.style.display = "none";
    }
    
    function show(s) {
    s.style.display = "block";
    }
    </script>

  9. #9
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I currently have the interface working, but not the submission.

    Code:
    <&#37; form_tag do %>
    					<%= select (:project, :status, @status_options, {:include_blank => nil},{:onchange => " if(this.value.match(/option1/i)){
    $('option1').show(); } else {
    $('option1').hide(); } 
    if(this.value.match(/option2/i)){
    $('option2').show(); } else {
    $('option2').hide(); }  "})%>
    My DIVs:

    Code:
    <div id = "option1" style = "display: none">
    	<% form_tag do %>
    		<%= select (:project, :probability, @status_option1) %>
    	<% end %>
    </div>
    				
    <div id = "option2" style = "display: none">
    	<% form_tag do %>
    		<%= select (:project, :probability, @status_option2) %>
    	<% end %>
    </div>
    And my global "choices" from ApplicationContoller:

    Code:
    @status_option1 = [["Probability",nil],["High (70%)",0.7],["Medium (50%)",0.5],["Low (30%)",0.3]]
    @status_option2 = [["Probability",nil],["High (50%)",0.5],["Medium (20%)",0.2],["Low (10%)",0.1]]
    I couldn't get "else-if" to work, so I was rather crude. If anyone sees an obvious reason why the probability variable isn't being submitted, let me know. Thanks

  10. #10
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    After looking at the generated HTML, I now know it's because the "probability" is being passed as a string, as opposed to integer. Trying to figure out how to get it passed as an int...

  11. #11
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So - now I have each div split up into a separate partial, and I'm trying to call the partial like this:

    Code:
    <&#37;= select (:project, :status, @status_options, {:include_blank => nil},{:onchange => render :partial => ("this.value" + "probs")})%>
    I know this can't work, but what's a professional way to go about getting this child-menu rendered?

  12. #12
    SitePoint Evangelist
    Join Date
    Feb 2006
    Location
    Worcs. UK
    Posts
    404
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by deliveryguy View Post
    I didn't use "commit_tag" - ruby didn't recognize it.
    Sorry. stupidity on my part. The method is "submit_tag"

    http://api.rubyonrails.org/classes/A...r.html#M002024

  13. #13
    SitePoint Evangelist
    Join Date
    Feb 2006
    Location
    Worcs. UK
    Posts
    404
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think you are trying to run before you can walk, and I'd suggest you start with a simpler design.

    However, if you really want to get the sort of behaviour you describe, you need to use browser side functionality and that means JavaScript. You can use AJAX to simplify the coding, and you can also use Rails AJAX helpers to generate the AJAX code.

    The reason your first version works and the second doesn't is because the former onchange calls a bit of JavaScript, and the latter calls a bit of Rails code. The onchange triggers an event client side - that is in the browser - and therefore the code that is executed has to be understood by the browser. Browsers don't understand Rails. You have to use Rails methods that generate the JavaScript/AJAX that will be run via the onchange trigger. Otherwise you can code the JavaScript/AJAX yourself.

    I think my best advice would be to suggest that you invest in the excellent "Ajax on Rails" book by Scott Raymond. This is by far the best reference I've read on using AJAX in the Rails environment.

  14. #14
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the reference Reggie. I'll read up on Ajax and have at it.

  15. #15
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've skimmed through Scott's book, and it's definitely something I'll be studying. I also have "Agile Web Development with Rails" (3rd edition, Rails 2.0). Great examples in both. I wish Ajax on Rails had a bit more on select menus though...

    After wrapping my head around some of the more general concepts, I went at this again with a different approach. I'm not sure if it's a "good" solution per se, but it's sort of working.

    I went entirely with javascript to contol the actions within a div. This div contains the "optional" sub-menu that may or may not be displayed.

    Code:
     <select onchange= "getprob(this.value)"
    This accomplishes what I want, with the following definition:

    Code:
    function getprob(id){
    	
    	switch (id){
    	
    	case "lead":
    		show("probability_select");
    		document.projectform.project_probability.options[0] = new Option("Probability", null, true);
    		document.projectform.project_probability.options[1] = new Option("High", 25, true);
    		document.projectform.project_probability.options[2] = new Option("Medium", 10, true);
    		document.projectform.project_probability.options[3] = new Option("Low", 5, true);
    		break;
    		
    	case "prospect":
    		show("probability_select");
    		document.projectform.project_probability.options[0] = new Option("Probability", null, true);
    		document.projectform.project_probability.options[1] = new Option("High", 50, true);
    		document.projectform.project_probability.options[2] = new Option("Medium", 20, true);
    		document.projectform.project_probability.options[3] = new Option("Low", 10, true);
    		break;
    		
    	case "proposal":
    		show("probability_select");
    		document.projectform.project_probability.options[0] = new Option("Probability", null, true);
    		document.projectform.project_probability.options[1] = new Option("High", 70, true);
    		document.projectform.project_probability.options[2] = new Option("Medium", 50, true);
    		document.projectform.project_probability.options[3] = new Option("Low", 30, true);
    		break;
    	
    	default: hide("probability_select");
    		}
    		}
    		
    function show(layer_ref) {
    var state = 'visible';
    
    show = document.getElementById(layer_ref);
    show.style.visibility = state;
    }
    
    function hide(layer_ref) {
    var state = 'hidden';
    
    show = document.getElementById(layer_ref);
    show.style.visibility = state
    But, there's one little problem. This works if the user selects the right "status" the first time, but the function only seems to run once. That is, if I picked a status that didn't pull up the probabilities, then I have to refresh the page to select another option with new sub-menu values. Hmmm...

  16. #16
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The one-time-only issue was fiixed by nesting the show and hide functions within the getprob() function.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •