Techioz Blog

Ruby on Rails の条件付きレンダリングが機能しない

概要

このモーダルを表示すると予期しない動作が発生します。

<% if @show_modal %>
  <%= render 'modal' %>
<% end %>
<div id="scanQrCodeModal">
  <h1>Scan QR Codes</h1>
  <div class="section">
    <div id="my-qr-reader"></div>
  </div>
</div>

インスタンス変数 @show_modal は、attences_controller.rb のこのメソッドから生成され、my_student_course.size > 1 の場合を除いて、すべてが期待どおりに動作します。

def mark_attendance
    token = params[:encoded_token]

    # Ensure the token is provided
    if token.blank?
      render json: { error: "Token is missing" }, status: :unprocessable_entity
      return
    end

    begin
      #decode jwt token
      secret_key = Rails.application.credentials.secret_key_base
      payload = JWT.decode(token, secret_key, true, algorithm: "HS256")[0]
      lecturer_id = payload["lecturer_id"]
      lecturer = Lecturer.find_by(id: lecturer_id)
      if lecturer
        my_student_course = lecturer.lecturer_units.flat_map(&:students_courses).uniq.select { |course| course.student_id == current_student.id }
        if my_student_course.size > 1
          @show_modal = true
          @students_attendance_courses = my_student_course
          puts @students_attendance_courses
          # Render a pop-up page with a list of results and radio buttons for selection
        else
          # If there is only one result or none, proceed with the first result
          @student_course = my_student_course
          my_student_course_id = @student_course.first&.id
        end
        create_attendance(my_student_course_id)
      else
        render json: { error: "Lecturer not found for the given lecturer_id" }, status: :unprocessable_entity
      end
    rescue JWT::DecodeError => e
      render json: { error: "Invalid token format" }, status: :unprocessable_entity
    end
  end

これは私のブートストラップモーダルです:

<!-- Button trigger modal -->
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <% if @show_modal %>
          <div id="modal">
            <!-- Use @students_attendance_courses to render the list -->
            <% @students_attendance_courses.each do |course| %>
              <!-- Rendering course details as needed -->
              <%= course.id %>
            <% end %>
          </div>
        <% else %>
          <%= "No modal to show" %>
        <% end %>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

<script>
  document.addEventListener('DOMContentLoaded', function() {
    var myModal = new bootstrap.Modal(document.getElementById('exampleModal'));
    myModal.show();
  });
</script>

ページ読み込み時に表示されるモーダルが false の場合にレンダリングするように条件レンダリングを変更しました。

<% if !@show_modal %>
  <%= render 'modal' %>
<% end %>
<div id="scanQrCodeModal">
  <h1>Scan QR Codes</h1>
  <div class="section">
    <div id="my-qr-reader"></div>
  </div>
</div>

これがマーク出席をトリガーする方法です

function domReady(fn) { 
  if (
    document.readyState === "complete" || 
    document.readyState === "interactive"
  ) { 
    setTimeout(fn, 1000); 
  } else { 
    document.addEventListener("DOMContentLoaded", fn); 
  } 
}

const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

function sendPostRequest(encodedToken, csrfToken) {
const currentUrl = window.location.href;

// Replace "/scan" with "/mark_attendance" in the URL
const newUrl = currentUrl.replace('/scan', '/mark_attendance');
  fetch(newUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': csrfToken,
    },
    body: JSON.stringify({ encoded_token: encodedToken }),
  })
  .then(response => {
    if (response.redirected) {
      // Handling redirect manually
      window.location.href = response.url;
    } else {
      return response.json();
    }
  })
    .catch(error => {
      console.error('Error:', error);
    });
}

domReady(function () { 
  // If found your QR code 
  function onScanSuccess(decodeText, decodeResult) { 
    alert("Your QR code is: " + decodeText, decodeResult); 
    // Fetch CSRF token from the meta tag
    const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
    
    // Send a POST request to create attendance
    sendPostRequest(decodeText, csrfToken);
  } 

  let htmlscanner = new Html5QrcodeScanner( 
    "my-qr-reader", 
    { fps: 10,
      qrbox: { width: 250, height: 250 },
      supportedScanTypes: [Html5QrcodeScanType.SCAN_TYPE_CAMERA, Html5QrcodeScanType.SCAN_TYPE_FILE] } 
  ); 
  htmlscanner.render(onScanSuccess);
});

解決策

条件を if !@show_modal によってモーダルが表示されるように変更すると、ビューのレンダリング時に @show_modal が false または nil になる可能性があることが示唆されます。これは、コントローラー アクションが @show_modal を期待どおりに設定していないこと、または @show_modal が設定されていない別の要求サイクルでビューがレンダリングされていることが原因である可能性があります。

したがって、条件が正しく設定されていても問題が解決しない場合は、モーダルを表示するための JS が @show_modal で条件付けされていないことが問題である可能性があります。つまり、ページが読み込まれるたびに実行されるため、次のようにする必要があります。 @show_modal に関連付けます。 これを行うには、次のようなことを試すことができます。

<% if @show_modal %>
  <%= render 'modal' %>
  <script>
    document.addEventListener('DOMContentLoaded', function() {
      var myModal = new bootstrap.Modal(document.getElementById('exampleModal'));
      myModal.show();
    });
  </script>
<% end %>