How to display multi-day events in SimpleCalendar?
Hi there,
Not sure if i've missed something, but i'm not able to get multi-day events to show over the date range using SimpleCalendar.
From what I've read on the Gem homepage, I've passed in the end date attribute option to the calender_helper, but when viewing the results I only see the event showing on the first day, but it doesn't show on the following days.
I've followed some of the videos, which seems to use a "starts_at" value, to compare against day to be displayed, which I presume has something to do with it. I can't see anything obvious about using an "ends_at" value too to check and see if the day falls inside that range.
Has anyone else encountered this, or have I missed something obvious?
Many thanks in advance,
Alan
Sup Alan,
Can you share the code you've got for this?
And did you follow the end_attribute stuff here? https://github.com/excid3/simple_calendar#rendering-events
Hey Chris,
Thanks for replying.
I'm got end_attribute set up in
<%= week_calendar events: @events, attribute: :start_at, end_attribute: :end_at do |date| %>
What's the best way to share the code? snippets or the full code?
You can just paste the relevant snippets here. I'd be interested in seeing the model's columns aside from that piece. Something's not wiring up correctly most likely but there could also be a bug in the gem.
It's quite possible that something's not wired up properly. I've picked the work up from someone else, and having to re-code parts of it.
One thing that I think might be causing problems, is that it's also using IceCube for recurring events.
My events_controller has
def weekly_planner
@all_events = Event.not_hidden
@events = @all_events.order(:all_day).flat_map{ |e| e.calendar_events(params.fetch(:start_date, Time.zone.now).to_date) }
end
In the model I've got
def calendar_events(start)
if recurring.empty?
Event.new(id: id, title: title, start_at: start_at, end_at: end_at, color: news_events_category.color, recurring: false, all_day: all_day)
else
schedule(start_time).occurrences(end_time).map do |date|
Event.new(id: id, title: title, start_at: date, end_at: end_at, color: news_events_category.color, recurring: recurring)
end
end
end
I had some problems with something else in the calender_events method, due to the new Event object created here.
My calender is also showing all day events, so I'm having to group events by all day events, then list out other events on that day ordered by start time.
The weekly_calender partial is doing the grouping and sorting, using
<% @events.select{ |m| m.start_time.to_date == day}.sort_by{|event| [event.all_day ? 0 : 1, event.start_time]}.each do |event| %>
Hopefully that helps to build a bit of a picture for you. Let me know if you need anything more
Hi Chris,
Appreciate you're a busy man. Wanted to check in with you, as to whether you had any thoughts / ideas as to whether I'm not seeing the multi-day events due to something I'd configured wrongly, or it's something else?
Many thanks again,
Alan
Hey Alan,
Yeah sorry, super busy lately. It never gets easier. :P
One thing I just noticed is that you're not actually using the events that simple_calendar has parsed for you. When you pass in the events into the calender method in the view, it will yield to the block the day and the filtered events.
<%= week_calendar events: @events, attribute: :start_at, end_attribute: :end_at do |date, events| %>
<div><%= date %></div>
<div>Events:</div>
<div><%= events.inspect %></div>
<% end %>
Try something like this code to see what it outputs for the events. Note the new events
parameter for the block.
No worries.
Busy is good, victim of your own success : ) All the more reason for appreciating you taking the time.
Putting that in (and disabling my overriding _week_calender
layout) I'm seeing the same results, with the multi-day event only showing on the start day.
As I'm not doing solid rails work all the time, my understanding of how things work just isn't strong enough for me to know how to dip deeper into what's going on and figure it out. Trying to turn that around, by doing more rails work and figure out a bit more how to decipher how it all works, so all help is invaluable!
Yep, it's a good and a bad problem at the same time. :P
In that case, I think you'll want to make sure that your calendar_events method is creating the right virtual events. Just do a single multi-day event and make sure that it generates 3 Event objects if it spans 3 days. The start and end times for those should be for their respective days. My guess is that there's something wrong in the generation of these and that's causing the discrepancy.
If you verify that works correctly, we can then eliminate a lot of things and figure out what else might be causing it. I'm going to make a simple example of multi-day events with simple_calendar as well to make sure those still work as expected. Nothing should have changed with them, but it'll be good for a double check.
Due to time differences and it being way after midnight, I've just done this.
Set up a new app with a simple_calender setup, following the guide on the gem github page, using the multi-day scaffold example.
On my Meeting index view i've used
<%= month_calendar events: @meetings, attribute: :start_time, end_attribute: :end_time do |date, meetings| %>
<%= date %>
<% meetings.each do |meeting| %>
<div>
<%= meeting.name %>
<%= meeting.inspect %>
</div>
<% end %>
<% end %>
and it's still only showing the event on the start day only.
Hmm, well if that didn't work, might be a bug in the gem. I'll spend some time on it today after I get a screencast published for this week.
It definitely works. Just tested a fresh install of simple_calendar and both month_calendar and week_calendar that I tested work as expected. https://cl.ly/kW2w One record in the database but it gets rendered 4 times, one for each day it spans.
That means that your start_time
and end_time
attributes must be the culprit in your last example I guess. What do your records look like?
So, turns out I was using an old version of the gem. Updating it made a multi-day event show on each of the days in the test app I set up.
Updating the gem on the app I need it to work, gets it to work too when using
<% sorted_events.fetch(day, []).each do |event| %>
to loop through the events. However, as my calendar is also showing recurring events (using the ice_cube), the code is checking for recurring events, and re-ordering based on showing all day events first, it's using revised code instead
<% @events.select{ |m| m.start_time.to_date == day}.sort_by{|event| [event.all_day ? 0 : 1, event.start_time]}.each do |event| %>
I've pasted in a previous comment about, what the events_controller and Event model are doing to create @events. I can see that multi-day bit isn't working due to the check for m.start_time.to_date == day.
Is it possible to override the events used in sorted_events to use those from @events?
Ah ha! That would explain it. :) Should have asked about that, but I assume that people are using the latest too quickly sometimes.
You have two options for that:
I would just do like I showed in the episode by creating your virtual array of events for recurring ones. The all-day events you would want to handle a little differently and to actually be passed into simple_calendar with the start time as 12:00am and 11:59pm for the end time. No overriding necessary as these will obviously be sorted to the top automatically. Actually, if the user checks the "all day" box you can just save those values in the database and then you don't have to do any special code for them.
You could override the method. Just redefine the calendar object and override that method. Generally not a great idea as you may run into problems when the gem gets updated in the future.
I know what you mean! In my day job, it's always the first thing I ask others before going further with any investigations, and I fell into the same trap!
Ok, getting somewhere now. It's working in the main app.
I've got an "all day" checkbox, which I was trying to use .sort_by{|event| [event.all_day ? 0 : 1, event.start_time]} to filter to the top, before sorting the other events by date. I'll see if I can figure out how to do that. That and passing in an end time for the event. Turns out that adding end_at to
schedule(start_time).occurrences(end_date).map do |date|
Event.new(id: id, title: title, start_at: date, color: news_events_category.color, recurring: recurring, is_recurring: true)
end
stops all the recurring events from showing. I'm sure there's a reason for that. Hopefully watching the screencast again will fill in some blanks.
Thanks again for your help (and putting up with some basic queries)
Haha! Yeah no worries man. Calendars get tricky at a point when you're doing all day events and so on.
The main thing to understand here is that simple_calendar is just sorting the objects you give it by the time attributes, so they must all have valid times and dates. All-Day events should sort to the top, which means their start time should always be 12:00am. If that is nil or some other value in the database, they will sort nil or those other values and will be incorrectly placed.
Since it does the sorting internally, any pre-sorting you might have done will be ignored. Your all-day sort to the top is going to be ignored since simple calendar can't know ahead of time which day to place the event. As long as you get your times right for each event, simple calendar will take care of the rest.
Hi Chris,
Would you share a code example for that feature when you have some time? I (and probably not me only) would be very interested to see it in action.
Best
Chris, I got everything working from the example in "Recurring events with the ice_cube gem" except that the recurring events are placed on the calendar one day early. For example, if I set it to Monday Weekly the event will appear on Sunday.
Any thoughts?