AnthonyGalli.com AnthonyGalli.com - 1 month ago 7
Ruby Question

Conditional for Nested Attribute?

How can I create a conditional where if the dueler is the

current_user
then it'll list the current_user's challenges and if the dueler is
@user
then it'll list the @user's challenges?

duels_controller

def new
@user = User.find(params[:challenge_daddy])
@duel = Duel.new
@duel.duelers << Dueler.new(user_id: current_user.id, user_name: current_user.name, user_last_name: current_user.last_name)
@duel.duelers << Dueler.new(user_id: @user.id, user_name: @user.name, user_last_name: @user.last_name)
@current_user_challenges = current_user.challenges.order(:created_at)
@challenged_user_challenges = @user.challenges.order(:created_at)
respond_with(@duel)
end


duels/_form.html.erb

<%= simple_form_for(@duel) do |f| %>
<%= f.fields_for :duelers do |dueler| %>
<%= render 'dueler_fields', :f => dueler %>
<% end %>
The loser will <%= f.text_field :consequence %>.
<%= f.submit %>
<% end %>


duels/_dueler_fields.html.erb

<%= f.select :user_id, User.order(:name).map { |user| [user.full_name, user.id] }, include_blank: true, id: "change-challenge-options" %> will
<%= collection_select(:dueler, :challenge_id, # @challenged_user_challenges or @current_user_challenges
, :id, :full_challenge, include_blank: true) %>


How do I create a conditional so that if the
:user_id
is equal to the
current_user
then
@current_user_challenges
will show in
collection_select
and if
:user_id
is equal to
@user
then
@challenged_user_challenges
will show?

Answer

You could add some java/coffee script to do this, since it means real time changes to the form.

Add current user to data and a partial (or two different collection_selects with ids):

<%= f.select :user_id, User.order(:name).map { |user| [user.full_name, user.id] }, include_blank: true, id: "change-challenge-options", data: {current-user-id: current_user.id } %>

Add app/assets/javascripts/challengers.coffee

$(document).ready ->
  userSelect = $('#change-challenge-options')
  userSelect.on 'valuesChanged', ->
    currentUserId = $('#current-challenge-options').data('current-user-id')
    if(currentUserId == userSelect.val())
      ... show / hide the correct fields
    else
      ... show / hide the correct fields
    $(window).resize            #Not necessary but makes it look nicer if the lists are long.

Don't know if this is the best solution but I've done something similar in my project and it works fine.

EDIT: So firstly you'll need to add collection_select fields for both situations to your view, and supply them with ids:

<%= collection_select(:dueler, :challenge_id, @current_user_challenges, :id, :full_challenge, include_blank: true), id: "current-user-challenges" %>
<%= collection_select(:dueler, :challenge_id, @challenged_user_challenges, :id, :full_challenge, include_blank: true), id: "challenged-user-challenges" %>

Then in the script you need to add:

if(currentUserId == userSelect.val())
   $('#current-user-challenges').show()
   $('#challenged-user-challenges').hide()
else
   $('#challenged-user-challenges').show()
   $('#current-user-challenges').hide()

This will show/hide the correct fields. I'm not exactly sure on how to add ids to collection_selects, but if my proposal doesn't work just place them in a div or other container. Give those containers ids and show/hide them in the same way.

In addition to this you might want to add some script covering first time visits or revisits of the form properly. So the full code would be something like:

#page:load just makes sure the page is fully loaded before running script.
#Usefull if you use turbolinks etc.
$(document).on 'ready page:load' ->
  userSelect = $('#change-challenge-options')
  currentUserId = $('#current-challenge-options').data('current-user-id')
  $(window).on 'page:change', ->
    if(userSelect.val()) #All none zero values are true in js
      if(currentUserId == userSelect.val()) #Show the right field
        $('#current-user-challenges').show()
        $('#challenged-user-challenges').hide()
      else
        $('#challenged-user-challenges').show()
        $('#current-user-challenges').hide()
    else                                    #Hide both fields if no user is chosen.
      $('#challenged-user-challenges').hide()
      $('#current-user-challenges').hide()
    $(window).resize
  userSelect.on 'valuesChanged', ->
    if(currentUserId == userSelect.val())
      $('#current-user-challenges').show()
      $('#challenged-user-challenges').hide()
    else
      $('#challenged-user-challenges').show()
      $('#current-user-challenges').hide()
    $(window).resize

I hope it will work for you!

Here's a working snippet of the concept using javascript instead of coffee and raw html:

var userSelect = $('#change-challenge-options');
var currentUserId = $('#current-user-id').data('user-id');
$('#challenged-user-challenges').hide();
$('#current-user-challenges').hide();
userSelect.on('change', function() {
  if (userSelect.val() == "0"){
     $('#current-user-challenges').hide();
     $('#challenged-user-challenges').hide();
    }
  else if (userSelect.val() == currentUserId) {
    $('#current-user-challenges').show();
    $('#challenged-user-challenges').hide();
  } else {
    $('#challenged-user-challenges').show();
    $('#current-user-challenges').hide();
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<form id="new_challenge" role="form" accept-charset="UTF-8">
  <div class="form-group" id="current-user-id" data-user-id = "1">
    <label class="control-label col-sm-2">User:</label>
    <select class="form-control" name="users" id="change-challenge-options">
      <option selected="selected" value="0">Choose user...</option>
      <option value="1">Me</option>
      <option value="2">Other user</option>
    </select>
  </div>
  <label class="control-label col-sm-2">Challenges:</label>
  <div class="form-group">
    <select class="form-control" name="users" id="current-user-challenges">
      <option selected="selected" value="0">Choose challenge...</option>
      <option value="1">My challenge 1</option>
      <option value="2">My challenge 2</option>
    </select>
  </div>
  <div class="form-group">
    <select class="form-control" name="users" id="challenged-user-challenges">
      <option selected="selected" value="0">Choose challenge...</option>
      <option value="1">Other user challenge 1</option>
      <option value="2">Other user challenge 2</option>
      <option value="3">Other user challenge 3</option>
    </select>
  </div>
  </div>
</form>

Comments