Hi,
I've been trying to get a scheduler widget working with a signalr datasource, following the example project "scheduler-signalr-server-filtering".
The scheduler doesn't show any data, and the read method in my Hub never gets hit. The javascript console in Chrome shows the following error: "kendo.all.js:13206 Uncaught Error: The "promise" option must be set."
This option is set in the widget, which looks like this:
<script src="@Url.Content("~/signalr/hubs")"></script><script> var lockedRecords = {}; var scheduleHub, hubStart; $(function() { scheduleHub = $.connection.scheduleHub; $.connection.hub.qs = {'subscriptionId': subscriptionId} scheduleHub.client.lockRecord = function(record) { lockedRecords[record.id] = true; }; scheduleHub.client.unlockRecord = function(record) { lockedRecords[record.id] = false; } hubStart = $.connection.hub.start(); }); function forRead(data, type) { var scheduler = $("#scheduler").data("kendoScheduler"); var timezone = scheduler.options.timezone; var startDate = kendo.timezone.convert(scheduler.view().startDate(), timezone, "Etc/UTC"); var initialEndDate = scheduler.view().endDate(); var augmentedEndDate = new Date(initialEndDate.valueOf()); augmentedEndDate.setDate(initialEndDate.getDate() + 1); var endDate = kendo.timezone.convert(augmentedEndDate, timezone, "Etc/UTC"); var result = { Start: new Date(startDate.getTime() - (startDate.getTimezoneOffset() * kendo.date.MS_PER_MINUTE)), End: new Date(endDate.getTime() - (endDate.getTimezoneOffset() * kendo.date.MS_PER_MINUTE)) } console.log(result); return result; } function forCreate(data, type) { console.log(data); console.log(type); } function onMap(data, type) { switch (type) { case "read": { return forRead(data, type); } case "create": { return forCreate(data, type); } default: { return data; } } }</script><script> function onDataBound(e) { this.view().content.on("click", ".k-event-delete", preventEvent); } function preventEvent(e) { var scheduler = $("#scheduler").data("kendoScheduler"); var eventUid = $(this).closest(".k-event").attr(kendo.attr("uid")); var event = scheduler.occurrenceByUid(eventUid); if (lockedRecords[event.id]) { e.stopImmediatePropagation(); alert("Currently the event cannot be deleted"); } } function scheduler_edit(e) { var salesPersonId = e.container.find("#SalesPersonPicker").data("kendoDropDownList"); salesPersonId.dataSource.data(e.sender.resources[0].dataSource.data()); } function scheduler_resize(e) { if (salesPersonIsOccupied(e.start, e.end, e.event, e.resources)) { this.wrapper.find(".k-marquee-color").addClass("invalid-slot"); e.preventDefault(); } } function scheduler_resizeEnd(e) { if (!checkAvailability(e.start, e.end, e.events)) { e.preventDefault(); } } function scheduler_move(e) { if (salesPersonIsOccupied(e.start, e.end, e.event, e.resources)) { this.wrapper.find(".k-event-drag-hint").addClass("invalid-slot"); } } function scheduler_moveEnd(e) { if (!checkAvailability(e.start, e.end, e.event, e.resources)) { e.preventDefault(); } } function scheduler_add(e) { if (!checkAvailability(e.event.start, e.event.end, e.event)) { e.preventDefault(); } } function scheduler_save(e) { if (!checkAvailability(e.event.start, e.event.end, e.event)) { e.preventDefault(); } } function occurrencesInRangeByResource(start, end, resourceFieldName, event, resources) { var scheduler = $("#scheduler").getKendoScheduler(); var occurrences = scheduler.occurrencesInRange(start, end); var idx = occurrences.indexOf(event); if (idx > -1) { occurrences.splice(idx, 1); } event = $.extend({}, event, resources); return filterByResource(occurrences, resourceFieldName, event[resourceFieldName]); } function filterByResource(occurrences, resourceFieldName, value) { var result = []; var occurrence; for (var idx = 0, length = occurrences.length; idx < length; idx++) { occurrence = occurrences[idx]; var resourceValue = occurrence[resourceFieldName]; if (resourceValue === value) { result.push(occurrence); } else if (resourceValue instanceof kendo.data.ObservableArray) { if (value) { for (var i = 0; i < value.length; i++) { if (resourceValue.indexOf(value[i]) != -1) { result.push(occurrence); break; } } } } } return result; } function salesPersonIsOccupied(start, end, event, resources) { var occurrences = occurrencesInRangeByResource(start, end, "SalesPersonId", event, resources); if (occurrences.length > 0) { return true; } return false; } function checkAvailability(start, end, event, resources) { if (salesPersonIsOccupied(start, end, event, resources)) { setTimeout(function () { alert("Verkoper heeft al een afspraak."); }, 0); return false; } return true; }</script>@(Html.Kendo() .Notification() .Name("notification") .Width("100%") .Position(pos => pos.Top(0).Left(0)))@(Html.Kendo() .Scheduler<ScheduleViewModel>() .Name("scheduler") .Date(DateTime.Now) .StartTime(7, 0, 0) .EndTime(19, 0, 0) .Height(600) .Views(views => { views.DayView(); views.WorkWeekView(wv => wv.Selected(true).Title("Werkweek")); views.MonthView(); views.AgendaView(); }) .Editable(e => { e.TemplateName("ScheduleEditorTemplate"); }) .Height(800) .Resources(resource => { resource.Add(m => m.SalesPersonId) .Title("Verkoper") .Name("SalesPerson") .DataTextField("Name") .DataValueField("Id") .DataSource(ds => ds.Read(read => read.Action("SalesPerson_Read", "Planning"))); }) .Events(events => events .Add("scheduler_add") .Save("scheduler_save") .Resize("scheduler_resize") .ResizeEnd("scheduler_resizeEnd") .Move("scheduler_move") .MoveEnd("scheduler_moveEnd") .Edit("scheduler_edit") .DataBound("onDataBound")) .DataSource(d => d .SignalR() .ServerFiltering(true) .Transport(tr => tr .ParameterMap("onMap") .Promise("hubStart") .Hub("scheduleHub") .Client(c => c .Read("read") .Create("create") .Update("update") .Destroy("destroy")) .Server(s => s .Read("read") .Create("create") .Update("update") .Destroy("destroy"))) .Schema(schema => schema .Data("Data") .Total("Total") .Model(model => { model.Id(m => m.Id); model.Field("subscriptionId", typeof(int)).From("SubscriptionId").Editable(false); model.Field("salesPersonId", typeof(int)).From("SalesPersonId").Editable(false); model.Field("prospectId", typeof(Guid)).From("ProspectId").Editable(false); model.Field("location", typeof(string)).From("Location"); model.Field("title", typeof(string)).From("Title"); model.Field("description", typeof(string)).From("Description"); model.Field("isAllDay", typeof(bool)).From("IsAllDay"); model.Field("start", typeof(DateTime)).From("Start"); model.Field("end", typeof(DateTime)).From("End"); model.Field("startTimezone", typeof(string)).From("StartTimezone"); model.Field("endTimezone", typeof(string)).From("EndTimezone"); model.Field("recurrenceRule", typeof(string)).From("RecurrenceRule"); model.Field("recurrenceException", typeof(string)).From("RecurrenceException"); }))))
The hub looks like this:
[HubName("scheduleHub")] public class ScheduleHub : Hub { //private static readonly ConcurrentDictionary<int, ConcurrentDictionary<string, string>> Connections = new ConcurrentDictionary<int, ConcurrentDictionary<string, string>>(); public IEnumerable<ScheduleViewModel> Read(FilterRange range) { var scheduleService = GetScheduleService(); return scheduleService.GetRange(range); } public void Update(ScheduleViewModel schedule) { var subscriptionId = GetSubscriptionId(); var scheduleService = new ScheduleService(subscriptionId); scheduleService.Update(schedule); Clients.OthersInGroup(subscriptionId.ToString()).update(schedule); } public void Destroy(ScheduleViewModel schedule) { var subscriptionId = GetSubscriptionId(); var scheduleService = new ScheduleService(subscriptionId); scheduleService.Delete(schedule); Clients.OthersInGroup(subscriptionId.ToString()).destroy(schedule); } public ScheduleViewModel Create(ScheduleViewModel schedule) { var subscriptionId = GetSubscriptionId(); var scheduleService = new ScheduleService(subscriptionId); scheduleService.Insert(schedule); Clients.OthersInGroup(subscriptionId.ToString()).create(schedule); return schedule; } public void LockRecord(int id) { Clients.OthersInGroup(GetSubscriptionId().ToString()).lockRecord(new {id = id}); } public void UnlockRecord(int id) { Clients.OthersInGroup(GetSubscriptionId().ToString()).unlockRecord(new {id = id}); } public override Task OnConnected() { var subscriptionId = GetSubscriptionId(); //var sessionId = Context.QueryString["sessionId"]; Groups.Add(Context.ConnectionId, subscriptionId.ToString()); //var subscriptionDictionary = Connections.GetOrAdd(subscriptionId, x => new ConcurrentDictionary<string, string>()); //subscriptionDictionary.GetOrAdd(sessionId, x => Context.ConnectionId); return base.OnConnected(); } private ScheduleService GetScheduleService() { var subscriptionId = GetSubscriptionId(); return new ScheduleService(subscriptionId); } private int GetSubscriptionId() { return Convert.ToInt32(Context.QueryString["subscriptionId"]); } }