使用KnockoutJS按日期分组事件

更新 :我为我的问题提供了最佳答案。 随意提高可读性和/或效率。


问题 :在我的requireJS设置中使用fullcalendar.io,在每个next / prev / view更改的日历上填充Event对象(从对.ashx处理程序的AJAX调用接收)。 我想在回调上使用该JSON对象来创建由startDate属性分组的事件列表。

我做了什么
在日历更新(例如新月)中,我能够设置基本ViewModel以使用可观察数组填充新的事件列表。 唯一缺少的部分是group by,例如这个例子。 由于我希望按事件对象的开始日期进行分组,然后按开始日期对每个组进行排序,我不确定应采取的最佳实践,例如是否有必要将我的JSON数据转换为对象。 随着我的了解,我会继续更新这篇文章。 我没有使用链接示例的原因是因为我不理解处理程序背后的完整术语,但是在这一点上确实理解了一些像$ root这样的关键字。

AJAX调用和requireJS模块加载 :fullcalendar

var fullCalendar = ".calendar-module", viewModel; function initFullCalendar() { $(fullCalendar).fullCalendar({ events: { url: '/Presentation/DotGov/test/fullcalendar/GetEvents.ashx', type: 'POST', success: function (data) { // bind events to KO ViewModel object viewModel.events(data); } }, }); } // requireJS module load return { load: function () { viewModel = new ViewModel(); ko.applyBindings(viewModel); initFullCalendar(); } }; 

视图模型

 // Day constructor that holds the day title and an array of event objects var Day = function() { dayTitle = null, events = [] } function ViewModel() { var self = this; self.dates = ko.observableArray(); self.events = ko.observableArray(); self.uniqueDates = ko.computed(function () { var allDates = ko.utils.arrayMap(self.events(), function (event) { return new XDate(event.start).toDateString(); }); return ko.utils.arrayGetDistinctValues(allDates); }, ViewModel); // TODO: groupedEvents { date: /.., events: [..]} self.groupedEvents = ko.computed(function () { var groupedEvents = []; var uniqueDates = self.uniqueDates(); for (var i = 0; i < uniqueDates.length; i++) { // create new day object var day = new Day(); day.dayTitle = uniqueDates[i]; day.events = []; // get all events within that day ko.utils.arrayForEach(self.events(), function (event) { var eventDate = new XDate(event.start).toDateString(); if (eventDate == uniqueDates[i]) { day.events[day.events.length] = event; } }); groupedEvents[groupedEvents.length] = day; } return groupedEvents; }); } 

视图

 

Monday, April 16, 2014

更新我已经使用一些计算的可观察函数更新了ViewModel。 self.groupedEvents遍历唯一日期列表并返回一个Day对象数组,其中每个Day对象包含唯一日期标题和一个Event对象数组(未从JSON转换)。 我现在必须更新视图以查看它是否有效。

在这里回答我自己的问题,而不是继续更新问题。 随意留下答案,如果我认为这是一个更好的解决方案,我会给出最好的答案。

FullCalendar:AJAX调用和事件

 $(fullCalendar).fullCalendar({ header: { left: '', center: 'month,agendaWeek,agendaDay', right: 'prev,title,next' }, titleFormat: { agendaWeek: "(M/DD/YYYY)", agendaDay: "dddd MMMM D, YYYY" }, minTime: "07:00:00", maxTime: "19:00:00", fixedWeekCount: false, allDaySlot: false, editable: false, eventLimit: { 'month': 3, 'default': true // gives default value to other views }, events: { url: '/Presentation/test/fullcalendar/GetEvents.ashx', type: 'POST', success: function (data) { // don't render event if not in month (sometimes events at end or beggining of prev/next month are rendered) for (var i = 0; i < data.length; i++) { if ((new Date(data[i].start).getUTCMonth()) != (new Date($(fullCalendar).fullCalendar('getView').start).getUTCMonth())) { data.splice(i, 1); } } //var parsed = JSON.parse(data); viewModel.events(data); // bind events to KO ViewModel } }, /* event handlers */ eventClick: function(calEvent, jsEvent, view) { // hide all overlays initially $('.fc-event').find('.overlay').fadeOut(); var $overlay = $(jsEvent.currentTarget).find('.overlay'); $overlay.show(); jsEvent.stopPropagation(); return false; }, eventRender: function (event, element, view) { // also fired on loading the eventLimit popup.. so check event // if initially loading event on view if (event.startDisplayDate == undefined) { // format start and end dates event.startDisplayDate = new XDate(event.start._i).toString("ddd, MMMM d, h(:mm)tt"); event.endDisplayDate = new XDate(event.end._i).toString("ddd, MMMM d, h(:mm)tt"); } else { // rendering event again, but inside limitclick popover (TODO: find better way) event.overlayClasses = "position-left"; } element.append(popupTemplate(event)); }, viewRender: function (view, element) { $(fullCalendar).fullCalendar('refetchEvents'); // refetch events on view change } }); 

ViewModel和Objects

 var Event = function (event) { this.eventID = event.id; this.eventTitle = event.title; this.startDate = event.start; this.startHour = new XDate(event.start).toString("(h:mm)t"); this.endDate = event.end; this.eventUrl = event.eventUrl; } // Day constructor that holds the day title and an array of event objects var Day = function() { dayTitle = null, events = [] } function ViewModel() { var self = this; self.dates = ko.observableArray(); self.events = ko.observableArray(); // TODO: sort by date self.uniqueDates = ko.computed(function () { var allDates = ko.utils.arrayMap(self.events(), function (event) { return new XDate(event.start).toDateString(); }); return ko.utils.arrayGetDistinctValues(allDates); }, ViewModel); // groupedEvents { date: /.., events: [..]} self.groupedEvents = ko.computed(function () { var groupedEvents = []; var uniqueDates = self.uniqueDates(); for (var i = 0; i < uniqueDates.length; i++) { // create new day object var day = new Day(); day.dayTitle = uniqueDates[i]; day.events = []; // get all events within that day ko.utils.arrayForEach(self.events(), function (event) { var eventDate = new XDate(event.start).toDateString(); if (eventDate == uniqueDates[i]) { day.events[day.events.length] = new Event(event); } }); groupedEvents[groupedEvents.length] = day; } return groupedEvents; }); } 

视图

 

<%--the event startDate--%>