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