使用KnockoutJS按日期分组事件

时间:2014-11-27 05:17:15

标签: jquery ajax json mvvm knockout.js

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

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

我做了什么
在日历更新(例如新月)中,我能够设置基本ViewModel以使用可观察数组填充新的事件列表。缺少的唯一部分是group by,例如this示例。由于我希望按事件对象的开始日期进行分组,然后按开始日期对每个组进行排序,我不确定应采取的最佳实践,例如是否有必要将我的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;
    });
}

查看

<div class="calendar-module-mobile">
    <div class="day-wrapper">
        <p class="day-title">Monday, April 16, 2014</p>   <%--the event startDate--%>
        <div class="mobile-block">
            <ul data-bind="foreach: events">
                <li>
                    <a data-bind="attr: { href: eventUrl }">
                        <p data-bind="text: title"></p>
                        <span class="icon-chevron-right turquoise-icon"></span>
                    </a>
                </li>
            </ul>
        </div>
    </div>
</div>

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

1 个答案:

答案 0 :(得分:0)

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

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和对象

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;
    });
}

查看

<div class="calendar-module-mobile">
    <div data-bind="foreach: groupedEvents">
        <div class="day-wrapper">
            <p data-bind="text: dayTitle" class="day-title"></p>
            <%--the event startDate--%>
            <div class="mobile-block">
                <ul data-bind="foreach: events">
                    <li>
                        <span data-bind="text: startHour"></span>
                        <a data-bind="attr: { href: eventUrl }">
                            <p data-bind="text: eventTitle"></p>
                            <span class="icon-chevron-right turquoise-icon"></span>
                        </a>
                    </li>
                </ul>
            </div>
        </div>
    </div>
</div>