The mystery of disappearing control points

If the user clicks the checkbox, the code below appears, but the checkmark will disappear sometimes, so the checkbox is not checked when it should be checked.

$(document).on("page:change", function() {
  $(".habit-check").change(function()
  {
    habit = $(this).parent().siblings(".habit-id").first().attr("id");
    level = $(this).siblings(".level-id").first().attr("id");
    if($(this).is(":checked"))
    {
       $.ajax(
       {
         url: "/habits/" + habit + "/levels/" + level + "/days_missed",
         method: "POST"
       });
    }
    else
    {
       $.ajax(
       {
         url: "/habits/" + habit + "/levels/" + level + "/days_missed/1",
         method: "DELETE"
       });
    }
  });
});

# I ALSO TRIED USING LOCAL STORAGE, BUT IT HAD THE SAME PROBLEM.
# VERSION 2

$(document).on("page:change", function() {
{
  $(".habit-check").change(function()
  {
    habit = $(this).parent().siblings(".habit-id").first().attr("id");
    level = $(this).siblings(".level-id").first().attr("id");
    if ($(this).is(":checked")) {
          $.ajax({
            url: "/habits/" + habit + "/levels/" + level + "/days_missed",
            method: "POST"
          });
          localStorage.setItem("habit_"+habit+"_"+level, true);
        } else {
          $.ajax({
            url: "/habits/" + habit + "/levels/" + level + "/days_missed/1",
            method: "DELETE"
          });
          localStorage.setItem("habit_"+habit+"_"+level, false);
        }
  });
});

      

Page show calls AJAX

<div class="strikes">
  <% if @habit.current_level_strike %> 
    <div class="btn" id="red"> <label id="<%= @habit.id %>" class="habit-id">Strikes:</label>
  <% else %> 
    <div class="btn" id="gold"> <label id="<%= @habit.id %>" class="habit-id-two">Strikes:</label>
  <% end %>
    <% @habit.levels.each_with_index do |level, index| %>
      <% if @habit.current_level >= (index + 1) %>
        <p>
          <% if @habit.current_level_strike %> 
            <label id="<%= level.id %>" class="level-id">Level <%= index + 1 %>:</label> 
          <% else %> 
            <label id="<%= level.id %>" class="level-id-two">Level <%= index + 1 %>:</label> 
          <% end %>
          <%= check_box_tag nil, true, level.missed_days > 0, {class: "habit-check"} %>
          <%= check_box_tag nil, true, level.missed_days > 1, {class: "habit-check"} %>
          <%= check_box_tag nil, true, level.missed_days > 2, {class: "habit-check"} %>
       </p>
      <% end %>
    <% end %>
  </div>
</div>

      

This is what AJAX is shooting for, days_missed_controller.rb .

class DaysMissedController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]

  def create
    habit = Habit.find(params[:habit_id])
    habit.missed_days = habit.missed_days + 1
    @habit.save!
    level = habit.levels.find(params[:level_id])
    level.missed_days = level.missed_days + 1
    if level.missed_days == 3
      level.missed_days = 0
      level.days_lost += habit.calculate_days_lost + 1
    end
    level.save!
    head :ok # this returns an empty response with a 200 success status code
  end

  def destroy
    habit = Habit.find(params[:habit_id])
    habit.missed_days = habit.missed_days - 1
    habit.save!
    level = habit.levels.find(params[:level_id])
    level.missed_days = level.missed_days - 1
    level.save!
    head :ok # this returns an empty response with a 200 success status code
  end
end

      

Here's the gist . Please feel free to ask for further code or clarification:]

I also ran into this problem when I removed turbolinks thinking it might be the problem, but it is not.

+1


source to share


1 answer


Okay, tracked this nasty one!

The problem is embedded in your HTML code. It turns out the problematic ones end up making AJAX calls for ... invalid urls (calling 404

)!

In your view , you have code like:

<% if @habit.current_level_strike %> 
  <div class="btn" id="red"> <label id="<%= @habit.id %>" class="habit-id">Strikes:</label>
<% else %> 
  <div class="btn" id="gold"> <label id="<%= @habit.id %>" class="habit-id-two">Strikes:</label>
<% end %>

<!-- [...] -->

<% if @habit.current_level_strike %> 
  <label id="<%= level.id %>" class="level-id">Level <%= index + 1 %>:</label> 
<% else %> 
  <label id="<%= level.id %>" class="level-id-two">Level <%= index + 1 %>:</label> 
<% end %>

      

Why is this problematic? Well, in your JavaScript, you rely on exact classes .habit-id

and .level-id

:

habit = $(this).parent().siblings(".habit-id").first().attr("id");
level = $(this).siblings(".level-id").first().attr("id");

      

While in accordance with the HTML of the show For view , are sometimes appropriate classes, and sometimes there are classes with the application *-two

( habit-id-two

and level-id-two

).

If you try to correct the names of classes, so they are all the same for your JavaScript ( .siblings(".habit-id")

and .siblings(".level-id")

), the problem disappears.

Better solution (yes, it can be simplified a little;))

What if we pre-generate URLs and set them to HTML like so:



<div class="strikes">
  <!-- [...] -->
    <% @habit.levels.each_with_index do |level, index| %>
      <% if @habit.current_level >= (index + 1) %>
        <p data-submit-url="<%= habit_level_days_missed_index_path({ habit_id: @habit.id, level_id: level.id }) %>"
           data-delete-url="<%= habit_level_days_missed_path({ habit_id: @habit.id, level_id: level.id, id: 1 }) %>">
          <!-- [...] -->
        </p>
      <% end %>
    <% end %>
  </div>
</div>

      

Then your JavaScript can be simplified to:

$(document).on("page:change", function() {
  $(".habit-check").change(function()
  {
    var submitUrl = $(this).parents("p").data("submit-url");
    var deleteUrl = $(this).parents("p").data("delete-url");

    if($(this).is(":checked"))
    {
       $.ajax(
       {
         url: submitUrl,
         method: "POST"
       });
    }
    else
    {
       $.ajax(
       {
         url: deleteUrl,
         method: "DELETE"
       });
    }
  });
});

      

Please be careful that when generating the delete-url I used a hardcoded value id

which 1

(tries to reproduce your original behavior):

data-delete-url="<%= habit_level_days_missed_path({ habit_id: @habit.id, level_id: level.id, id: 1 }) %>"

      

which matches:

url: "/habits/" + habit + "/levels/" + level + "/days_missed/1"

      

in your code. Are you 100% sure this is what you want?

Hope it helps! If you have any questions - I'm more than happy to help / explain!

Good luck!

+2


source







All Articles