The Mystery of the Disappearing Checkmarks
Ok, tracked this nasty one down!
The problem lays in your HTML generated. It turns out, that the problematic ones end up performing AJAX calls to... invalid URLs (causing 404
's)!
In your show 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 it problematic? Well, in your JavaScript, you're relying on exact classes of .habit-id
and .level-id
:
habit = $(this).parent().siblings(".habit-id").first().attr("id");
level = $(this).siblings(".level-id").first().attr("id");
While according to HTML from show view, sometimes the proper classes are generated, and sometimes there are classes with appendix of *-two
(habit-id-two
and level-id-two
).
If you try fixing the class names, so all are of the same form expected by your JavaScript (.siblings(".habit-id")
and .siblings(".level-id")
), the problem disappears.
Better solution (yes, it is possible to simplify it a bit ;))
What if we pregenerate urls, and set them in 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 warned, that when generating delete-url, I've used hardcoded value of id
, which is 1
(trying to reproduce your original behaviour), in:
data-delete-url="<%= habit_level_days_missed_path({ habit_id: @habit.id, level_id: level.id, id: 1 }) %>"
which corresponds to:
url: "/habits/" + habit + "/levels/" + level + "/days_missed/1"
in your code. Are you 100% sure this is what you want?
Hope that helps! If you have any questions - I'm more than happy to help/explain!
Good luck!
How to restart current_level for every third missed_day (3 strikes you're out!)?
Here is my solution:
You need to keep track the days you lost at the moment you get 3 missed days (you need to add a field in level):
migration file.rb
class AddDaysLostToLevels < ActiveRecord::Migration
def change
add_column :levels, :days_lost, :integer, default: 0
end
end
Then change controller days_missed to reset when you get 3 missed days and store the days you lost when you start again level (in the variable days_lost):
class DaysMissedController < ApplicationController
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
...remain the same
Now we need to define "calculate_days_lost" and "real_missed_days" methods in habit.rb.
Remember to use self.real_missed_days instead self.missed_days in current_level method: (they are diferent because you can start again a level). New habit.rb will be like this:
class Habit < ActiveRecord::Base
belongs_to :user
has_many :comments, as: :commentable
has_many :levels
serialize :committed, Array
validates :date_started, presence: true
before_save :current_level
acts_as_taggable
scope :private_submit, -> { where(private_submit: true) }
scope :public_submit, -> { where(private_submit: false) }
attr_accessor :missed_one, :missed_two, :missed_three
def save_with_current_level
self.levels.build
self.levels.build
self.levels.build
self.levels.build
self.levels.build
self.save
end
def self.committed_for_today
today_name = Date::DAYNAMES[Date.today.wday].downcase
ids = all.select { |h| h.committed.include? today_name }.map(&:id)
where(id: ids)
end
def current_level_strike
levels[current_level - 1] # remember arrays indexes start at 0
end
def real_missed_days
value = 0
levels.each do |level|
if level.missed_days != nil
value += level.missed_days + level.days_lost
end
end
value
end
def committed_wdays
committed.map do |day|
Date::DAYNAMES.index(day.titleize)
end
end
def n_days
((date_started.to_date)..Date.today).count do |date|
committed_wdays.include? date.wday
end - self.real_missed_days
end
def calculate_days_lost
case n_days
when 0..9
n_days
when 10..24
n_days-10
when 25..44
n_days-25
when 45..69
n_days-45
when 70..99
n_days-70
else
n_days-100
end
end
def current_level
return 0 unless date_started
case n_days
when 0..9
1
when 10..24
2
when 25..44
3
when 45..69
4
when 70..99
5
else
6
end
end
end
How to fix AJAX to always fire when checking box?
This might be the problem with Turbolinks, could you try changing your javascript:
$(document).ready(function()
{
// ...
}
to look like:
$(document).on("ready page:load", function() {
// ..
}
Please, let me know if it helped!
Good luck!
Why does my CheckedTextView icon jump around to different elements in my GridView?
The problems lie in the way you track checked items:
GridView gridView = (GridView) findViewById(R.id.gridview);
View selected =gridView.getChildAt(position);
CheckedTextView selectedCheck = (CheckedTextView)selected.findViewById(R.id.imageTick);
selectedCheck.setChecked(true);
selectedCheck.setVisibility(View.VISIBLE);
1) getChildAt
will not give you the correct view if you've scrolled into content and you're indexing by adapter position. When referencing an active view in a GridView
or ListView
you want to do something like this:
final int index = position - gridView.getFirstVisiblePosition();
View selected = gridView.getChildAt(index);
The reason is that your GridView
only keeps child views for adapter items that it is currently displaying. It may only have child views for elements 4 to 23 if elements 0 to 3 were scrolled off of the top earlier.
This is why you're getting exceptions when you do View selected =gridView.getChildAt(position);
a view for that position does not actually exist when it's off-screen, so getChildAt
returns null
.
2) When a ListView
or GridView
has its content change or it otherwise needs to re-layout its child views, it does so by re-binding existing views using the convertView
parameter to your adapter. Your adapter never adjusts the checked state of your item views, so a checked view can be reused for an item that should be unchecked later.
To solve this you should have your data model that your adapter presents track the checked state of your items rather than relying on the item views to do it. This means that your adapter should always verify/set the checked state of the item in getView
. It may look something like this:
imageView = (ImageView) v.findViewById(R.id.image);
imageView.setImageBitmap(mThumbsIds.get().get(position));
CheckedTextView checkView = (CheckedTextView) v.findViewById(R.id.imageTick);
if (mData.isItemChecked(position)) {
checkView.setChecked(true);
checkView.setVisibility(View.VISIBLE);
} else {
checkView.setVisibility(View.GONE);
}
Then when your option is selected that should change the checked state of an item, instead of the code snippet above do something like:
data.setItemChecked(position, true);
adapter.notifyDataSetChanged();
notifyDataSetChanged()
will tell the GridView
that it should re-bind the item views, which will have your getView
method fix up the checked states properly.
I can't get magnific-popup animations to work
I ran into this same problem and after banging my head against all the hard surfaces in my office I discovered that I need to rename the css classes to match the fade example he provided here.
So for example the mfp-zoom-out animation:
.mfp-zoom-out .mfp-with-anim should be .mfp-zoom-out.mfp-bg
.mfp-zoom-out.mfp-bg stays the same
.mfp-zoom-out.mfp-ready .mfp-with-anim should be .mfp-zoom-out.mfp-ready .mfp-content
.mfp-zoom-out.mfp-ready.mfp-bg should be .mfp-zoom-out.mfp-bg.mfp-ready
.mfp-zoom-out.mfp-removing .mfp-with-anim should be .mfp-zoom-out.mfp-removing .mfp-content
.mfp-zoom-out.mfp-removing.mfp-bg should be .mfp-zoom-out.mfp-bg.mfp-removing
Related Topics
How to Write a Script to Edit a JSON File
R Networkd3 Package: Node Coloring in Simplenetwork()
Jquery Clone() Not Cloning Event Bindings, Even with On()
Wkwebkit JavaScript Execution When Not Attached to a View Hierarchy
Elliptic Curve Cryptography with Sjcl in Js and Openssl in Ruby
How to Pass Parameters in Get Requests with Jquery
Angular 2: Two Backend Service Calls on Success of First Service
How to Add/Remove a Class in JavaScript
How to Get All Arguments of a Function as Single Object Inside That Function
Draggable Line Chart in R/Shiny
Mixing Jsf El in a JavaScript File
Why Doesn't Decodeuri("A+B") == "A B"
Drawing a Line with Three.Js Dynamically
In R, How to Display Value on the Links/Paths of Sankey Graph
JavaScript Date.Utc() Function Is Off by a Month
Reactjs - Lifting State Up VS Keeping a Local State