Scan QR " />

Scan QR " />

Scan QR "/>

Ruby on Rails conditional rendering doesn't work

72 Views Asked by At

I'm having unexpected behavior for displaying this modal.

<% 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>

the instance variable @show_modal is generated from this method in attendances_controller.rb and everything works as expected apart from where if 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

This is my bootsrap modal:

<!-- 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>

changed the condition rendering to render when false, the modal displayed on page load.

<% 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>

enter image description here

This is how I trigger my mark attendance

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);
});
2

There are 2 best solutions below

1
Bahubali Ak On

Try applying one of the solution from below

Soution-1

<% 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>

<%= javascript_include_tag 'https://unpkg.com/html5-qrcode' %>
<%= javascript_include_tag 'html5-qrcode.min' %>
<%= javascript_include_tag 'qr-scanner' %>

Solution-2

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

<%= javascript_include_tag 'https://unpkg.com/html5-qrcode' %>
<%= javascript_include_tag 'html5-qrcode.min' %>
<%= javascript_include_tag 'qr-scanner' %>
0
8bithero On

If changing the condition to if !@show_modal is causing the modal to display, it suggests that @show_modal might be false or nil at the time of rendering the view. This could be due to the controller action not setting @show_modal as expected or the view being rendered in a different request cycle where @show_modal is not set.

So if your condition is being set correctly and you're still having issues then the problem might be that the JS to show the modal is not conditional on @show_modal, meaning it will execute every time the page is loaded so you'd need to tie it to @show_modal. To do this you could try something like this:

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