Recently there was a comment on Power Pages Tip #222 – List Calendar Styles on our YouTube channel – they were asking about adding a click event handler to events being displayed on a list that is using the calendar functionality. In this post, I’ll show you one way of doing that.
You may not have known that one of the capabilities of the List functionality in Power Pages is to render a calendar layout, instead of the traditional table. This is accomplished via the Power Pages Management model-driven app – you can’t do it in the Design Studio yet.
On the List form, go to the Calendar View tab, and then check Enabled. Next, you need to tell Power Pages which attributes on your table correspond to the data necessary to display a calendar, such as start and end date, summary and description. Below that, you’ll find a section that allows you to control some of the calendar settings.
With that setup, you should see something that looks like this:
By default, when you click on an event, an area will slide down that will show event details.
But what if you want something else to happen? For example, maybe you want to go to a details page for that event. Or maybe display a modal popup with event details.
As is unfortunately too often the case, it isn’t quite as straight-forward as you might think. It certainly wasn’t as straight-forward as I thought it would be when the question was asked. But, thanks to some knowledge learned in some previous posts, I was able to come up with a solution.
The calendar functionality wasn’t built by Microsoft – instead they used a plugin called Bootstrap Calendar. I like the fact that they used a plugin that natively supports Bootstrap, to keep the UI consistent.
When I started, I thought it would be as easy as binding a click event handler to the anchor tags that are used to render each of the events.
The first small challenge is that the calendar plugin is loaded in the $(document).ready event. So if we put our code in a $(document).ready handler, we can’t guarantee the calendar will exist yet. So at that point, we can’t target the anchor tags directly.
Also, the calendar plugin has some functionality that causes the entire area to be re-rendered. For example, if you’re in the month view, and you click on the number for a day, you’ll be taken to a new view that shows all of the events for that day. All of the DOM from the month view is gone, as would be any events that we might attached to them when the page loads.
So we can’t use a solution that simply looks for the elements when the page is done loading and attach events to them – we need to handle the fact that the elements for an event might be created much later.
Generally, in this case, I’d add an event to an ancestor element that we know will always be there, and then use filters to target the inner elements that we actually care about. For example, we can target the <div> with a class of “entitylist”, and then add a filter for the anchor tags we care about – in this case, anchors with the class of “event-info”. So something like:
$(document).ready(function () { $('.entitylist').on('click', 'a.event-info', function (e) { alert('event clicked!'); }); });
I thought this would work great because the a.event-info elements don’t need to be there when I add my handler. However, when I tried this, the alert only happened the second time I click on the event, not the first. Or more specifically, it only showed when the event details area that is toggled by default is being hidden, and not when it is shown.
After I bit of digging, I found this line of code in the Bootstrap Calendar plugin when the event details area is being shown:
event.stopPropagation();
This will prevent the event from being bubbled up to ancestor elements. In our case, because technically I’d registered the event on the <div> with a class of “entitylist”, the click event never made it up to that part of the DOM tree because the plugin was stopping it.
I did try some other things to try to get around this, but then I realized that if someone wants to handle the click event themselves, they probably don’t want that area that slides up and down showing the event details. So if I could just figure out how to turn that off, then I wouldn’t have to worry about my event not being fired.
After poking around in the Bootstrap Calendar plugin code, I noticed there is an option to disable the events details area. This is done by settings the views.month.slide_events option to 0. However, we don’t have direct access to change any of the options provided to the Bootstrap Calendar plugin – those options are set by Microsoft code.
Thankfully, as we saw in our post from June, we can hack around that a bit. We do that by overriding the default plugin initialization function with our own version, which then calls the normal version (after making the changes we want):
var oldCalendar = $.fn.calendar; $.fn.calendar = function (params) { params.views = params.views || {}; params.views.month = params.views.month || {}; params.views.month.slide_events = 0; return oldCalendar.call(this, params); };
This code gets a reference to the default calendar initialization function, and then overwrites it with our new one. Our new one is simple – we set the option we care about, then call the original function.
Now that the event details area isn’t being shown, the code that stops the bubbling of our events doesn’t fire, so our original code is called each time the anchor is clicked. The complete code (if you want the click to take you to a details page) looks like:
var oldCalendar = $.fn.calendar; $.fn.calendar = function (params) { params.views = params.views || {}; params.views.month = params.views.month || {}; params.views.month.slide_events = 0; return oldCalendar.call(this, params); }; $(document).ready(function () { $('.entitylist').on('click', 'a.event-item, a.event-info', function (e) { window.location = "{{sitemarkers['Event Details'].Url}}?id=" + $(this).data('event-id'); }); });
A couple of quick notes:
If you want some other action to happen when you click on an event, like opening a modal, that should be easy to do – but you’ll have to do that part yourself. 🙂
Leave a Reply