Search code examples
ruby-on-railsformsradio-buttonruby-on-rails-6

How to custom enum with rails radio_buttons?


I want to make a simple rating selection with star icon from enum in the form of a radio_button.

my model:

enum rating: %i(1 2 3 4 5 6 7 8 9 10)

and my view:

= form_with model: @rating  do |f|
    .form-group
label.input-label.text-white = t(".title")
- Rating.ratings.keys.each do |rating|
  = f.radio_button(:rating, rating) do |star_icon|
    = star_icon.label { "i fa fa-star" + star_icon.radio_button  }

= f.submit t("submit")

Honestly it looks scary, so I want to apply a style to make it look like a real rating. The radio buttons on the left and right are just html icons

enter image description here

li
        i.fa.fa-star title=("Rate 10")
      li
        i.fa.fa-star title=("Rate 9")
      li
        i.fa.fa-star title=("Rate 8")
      li
        i.fa.fa-star title=("Rate 7")
      li
        i.fa.fa-star title=("Rate 6")
      li
        i.fa.fa-star title=("Rate 5")
      li
        i.fa.fa-star title=("Rate 4")
      li
        i.fa.fa-star title=("Rate 3")
      li
        i.fa.fa-star title=("Rate 2")
      li
        i.fa.fa-star title=("Rate 1")

Solution

  • Honestly I would take a completely different approach. There isn't really a good reason to use an enum column to save a rating value, it just needs to be an integer.

    To capture the value in the view, a radio button is not the best look, I would use a little javascript (shown with jQuery here) and a little css to render a more typical rating selector (written here in haml syntax):

    =form_for @rating, method: :post, remote: true do |f|
      = f.hidden_field :rating, id: :rating
      - (1..10).each do |i|
        .star{data: {rating: i}}
    
    :css
      .star{
        display: inline-block;
      }
      .star::before{
        content: '\2606'
      }
      .star.selected::before{
        content: '\2605';
      }
    
    :javascript
      let selectRating = (event)=>{
        let target = $(event.target);
        let rating = parseInt(target.data('rating'));
        $('.star').each((i,el)=>{
          if(i<rating){
            $(el).addClass('selected');
          }else{
            $(el).removeClass('selected');
          }
        })
        $('#rating').attr('value',rating);
        $('form').submit();
      }
      $('body').on('click', '.star',selectRating)
    
    

    it will look like this

    rating stars

    Now when you click on one of the stars, the clicked star and all stars left of it become 'solid' and all the stars right of it become 'empty'. Also the value of the hidden field is set to the selected rating, so you can submit the form and save the rating on the backend.