This is a migrated thread and some comments may be shown as answers.

Grid with SignalR binding and server operations

7 Answers 261 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Bernd
Top achievements
Rank 2
Bernd asked on 29 Dec 2016, 03:20 AM

Hi.

I have a grid bound to SignalR. To be able to use server side operations I use the Kendo.DynamicLinq lib. Fitlering works fine. Aggregates don't.

Problem is: My aggregates field is always null.

My data source request class:

using Kendo.DynamicLinq;
using System.Collections.Generic;
 
namespace idee5.Dispatcher.Models {
    public class CalendarEventGridDataSourceRequest : DataSourceRequest {
        public IEnumerable<int> Workplaces { get; set; }
        // these are missing in the Kendo Dlinq class
        public int PageSize { get; set; }
        public IEnumerable<Aggregator> Aggregates { get; set; }
    }
}

I took the field names from this post and it's attached soltuion.

The hub method to tetrieve the data:

01.public async Task<DataSourceResult> OperationEvents(CalendarEventGridDataSourceRequest request) {
02.    // workaround for ArgumentNullException in ToDataSourceResult
03.    if (request.Filter != null && request.Filter.Filters.Count() == 0 && request.Filter.Field == null) request.Filter = null;
04. 
05.    // SignalR has no session, so get the preferences from the preference service
06.    string userId = Context.User.Identity.GetUserId();
07.    PreferenceProfile activeProfile = await Task.Run(() => _userSettingService.ReadActiveProfile(userId));
08.    IQueryable<CalendarEvent> result = await _schedulerService.OperationEventsAsync(activeProfile);
09. 
10.    IEnumerable<int> workplaces = request.Workplaces ?? new List<int>();
11.     DataSourceResult retVal = result.Where(ce => ce.Operation.AllowedWorkplaces.Count(awp => workplaces.Contains(awp.WorkplaceId ?? 0)) > 0)
12.        .ToDataSourceResult(request.Take, request.Skip, request.Sort, request.Filter, request.Aggregates);
13.    // Late view model creation.
14.    // Kendo.DynamicLinq adds the filters at the end which hits the database for view model creation before filters get applied
15.    retVal.Data = retVal.Data.OfType<CalendarEvent>().Select((ce) => _mapper.Map<CalendarEventViewModel>(ce)).ToList();
16.    return retVal;
17.}

What is needed to have a signalr bound grid with server filtering, sorting and grouping/aggregation?

Kind regards

Bernd

7 Answers, 1 is accepted

Sort by
0
Alexander Popov
Telerik team
answered on 02 Jan 2017, 09:38 AM
Hello Bernd,

I am not sure what is causing this behavior in your case. Would be able to share a runnable sample project or at least the Grid's configuration?

Regards,
Alexander Popov
Telerik by Progress
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Bernd
Top achievements
Rank 2
answered on 04 Jan 2017, 07:30 AM

Hello Alexander.

The runnable solution is too big and your team mates weren't able to start my stripped down version I made for a support ticket.

But here is the grid configuration. Hope it helps.

@(Html.Kendo().Grid<CalendarEventViewModel>()
    .Name(componentName: "eventGrid")
    .Pageable(p => { p.PageSizes(enabled: true); p.Refresh(enabled: true); p.Enabled(value: true); })
    .Scrollable(s => { s.Height(value: "auto"); s.Enabled(value: true); })
    .Filterable()
    .Groupable()
    .Selectable()
    .Sortable(s => s.Enabled(value: true))
    .Resizable(r => r.Columns(value: true))
    .Reorderable(r => r.Columns(value: true))
    .ColumnMenu(cm => cm.Enabled(value: true))
    .ToolBar(tb => tb.Excel())
    .Columns(columns => {
        columns.Bound(c => c.Operation.OperationIdFormatted)
            .Sortable(value: false)
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.MasterSystemId)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.MasterSystemId)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Title)
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.Title)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.Title)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Operation.WorkOrder.EndProduct.ArticleNumber)
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(ItemViewModel)}.{nameof(ItemViewModel.ArticleNumber)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(ItemViewModel)}.{nameof(ItemViewModel.ArticleNumber)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Operation.WorkOrder.EndProduct.Dimensions)
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(ItemViewModel)}.{nameof(ItemViewModel.Dimensions)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(ItemViewModel)}.{nameof(ItemViewModel.Dimensions)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Operation.TotalTimeSpan)
            .Sortable(value: false)
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.TotalTimeSpan)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.TotalTimeSpan)}", resourceSet: ResourceSet.Properties))
            .ClientTemplate(value: "#if (data.TotalTimeSpan) {# #:kendo.toString(TotalTimeSpan.Hours, '00')#:#:kendo.toString(TotalTimeSpan.Minutes, '00')#:#:kendo.toString(TotalTimeSpan.Seconds, '00')# #}#")
            .EditorTemplateName(templateName: "TimeSpan");
        columns.Bound(c => c.Operation.WorkOrder.QuantityToProduce).Format(value: "{0:n}")
            .ClientGroupHeaderTemplate(value: "#=sum#")
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.QuantityToProduce)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.QuantityToProduce)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Operation.WorkOrder.UnitOfMeasure.Label)
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(UnitOfMeasureViewModel)}.{nameof(UnitOfMeasureViewModel.Label)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(UnitOfMeasureViewModel)}.{nameof(UnitOfMeasureViewModel.Label)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Operation.AllowedWorkplaces)
            .ClientTemplate(value: "#=workplacesTemplate(Operation.AllowedWorkplaces)#")
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.AllowedWorkplaces)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.AllowedWorkplaces)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Operation.QuantityConfirmed).Format(value: "{0:n}")
            .ClientGroupHeaderTemplate(value: "#=sum#")
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.QuantityConfirmed)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.QuantityConfirmed)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Operation.QuantityOpen).Format(value: "{0:n}")
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.QuantityOpen)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.QuantityOpen)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Operation.TimeSpanPerUnit)
            .Sortable(value: false)
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.TimePerUnit)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(OperationViewModel)}.{nameof(OperationViewModel.TimePerUnit)}", resourceSet: ResourceSet.Properties))
            .ClientTemplate(value: "#if (data.TimeSpanPerUnit) {# #:kendo.toString(TimeSpanPerUnit.Hours, '00')#:#:kendo.toString(TimeSpanPerUnit.Minutes, '00')#:#:kendo.toString(TimeSpanPerUnit.Seconds, '00')# #}#");
        columns.Bound(c => c.StartWeek)
            .ClientGroupHeaderTemplate(value: "Woche: #=value# - Offen: #=aggregates['Operation-WorkOrder-QuantityOpen'].sum#")
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.StartWeek)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.StartWeek)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.EndWeek)
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.EndWeek)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.EndWeek)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Start).Format(value: "{0:g}")
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.Start)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.Start)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.End).Format(value: "{0:g}")
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.End)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.End)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Workplace.MasterSystemId)
           .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(WorkplaceViewModel)}.{nameof(WorkplaceViewModel.MasterSystemId)}" })
           .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(WorkplaceViewModel)}.{nameof(WorkplaceViewModel.MasterSystemId)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Description)
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Properties, data_resource_id = $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.Description)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(CalendarEventViewModel)}.{nameof(CalendarEventViewModel.Description)}", resourceSet: ResourceSet.Properties));
        columns.Bound(c => c.Operation.WorkOrder.OrderType.Id).Hidden()
            .HeaderHtmlAttributes(new { data_resource_set = ResourceSet.Entities, data_resource_id = $"{nameof(OrderType)}" })
            .Title(Html.GetGlobalResourceString(resourceKey: $"{nameof(OrderType)}", resourceSet: ResourceSet.Entities))
            .ClientTemplate(value: "#=Operation.WorkOrder.OrderType.Label#");
    })
    .DataSource(ds => ds
        .SignalR()
        .AutoSync(enabled: false) // to prevent the popup from closing after changing a field http://www.telerik.com/forums/issue-in-kendo-grid-with-signalr-add-and-edit-mode
        // mimic ServerOperation(true)
        .ServerFiltering(enabled: true)
        .ServerPaging(enabled: true)
        .ServerSorting(enabled: true)
        .ServerGrouping(enabled: true)
        .ServerAggregates(enabled: true)
        .PageSize(pageSize: 20)
        .Filter(f => f.Add(ce => ce.Workplace.UnlimitedCapacity).IsEqualTo(value: true))
        .Transport(tr => tr
            .ParameterMap(handler: "addGridParameters")
            .Promise(handler: "hubStart")
            .Hub(handler: "schedulerHub")
            .Client(c => c
                .Read(method: "operationEvents")
                .Update(method: "updateWithScheduling"))
            .Server(s => s
                .Read(method: "operationEvents")
                .Update(method: "updateWithScheduling"))
            )
            .Schema(schema => schema
                .Data(data: "Data")
                .Total(total: "Total")
                .Aggregates(aggregates: "Aggregates")
                .Model(m => {
                    m.Id(f => f.Id);
                    m.Field(f => f.Id).Editable(enabled: false);
                    m.Field(f => f.StartWeek);
                    m.Field(f => f.EndWeek);
                    m.Field(f => f.ActivityStatus);
                    // same naming as in the scheduler
                    m.Field(memberName: nameof(CalendarEventViewModel.Description).PascalToCamelCase(), memberType: typeof(string)).From(nameof(CalendarEventViewModel.Description));
                    m.Field(memberName: nameof(CalendarEventViewModel.End).PascalToCamelCase(), memberType: typeof(DateTime)).From(nameof(CalendarEventViewModel.End));
                    m.Field(memberName: nameof(CalendarEventViewModel.EndTimezone).PascalToCamelCase(), memberType: typeof(string)).From(nameof(CalendarEventViewModel.EndTimezone));
                    m.Field(f => f.EventType);
                    m.Field(memberName: nameof(CalendarEventViewModel.IsAllDay).PascalToCamelCase(), memberType: typeof(bool)).From(nameof(CalendarEventViewModel.IsAllDay));
                    m.Field(f => f.Operation);
                    m.Field(f => f.OperationId);
                    m.Field(f => f.Operation.WorkOrder.EndConfirmed);
                    m.Field(memberName: nameof(CalendarEventViewModel.Recurrence).PascalToCamelCase(), memberType: typeof(string)).From(nameof(CalendarEventViewModel.Recurrence));
                    m.Field(memberName: nameof(CalendarEventViewModel.RecurrenceException).PascalToCamelCase(), memberType: typeof(string)).From(nameof(CalendarEventViewModel.RecurrenceException));
                    m.Field(memberName: "recurrenceID", memberType: typeof(int?)).From(nameof(CalendarEventViewModel.RecurrenceID));
                    m.Field(memberName: nameof(CalendarEventViewModel.RecurrenceRule).PascalToCamelCase(), memberType: typeof(string)).From(nameof(CalendarEventViewModel.RecurrenceRule));
                    m.Field(memberName: nameof(CalendarEventViewModel.Start).PascalToCamelCase(), memberType: typeof(DateTime)).From(nameof(CalendarEventViewModel.Start));
                    m.Field(memberName: nameof(CalendarEventViewModel.StartTimezone).PascalToCamelCase(), memberType: typeof(string)).From(nameof(CalendarEventViewModel.StartTimezone));
                    m.Field(f => f.Status);
                    m.Field(memberName: nameof(CalendarEventViewModel.Title).PascalToCamelCase(), memberType: typeof(string)).From(nameof(CalendarEventViewModel.Title))
                        .DefaultValue(this.LocalResources(key: "CalendarEventViewModel.Title.DefaultValue"));
                    m.Field(f => f.Workplace);
                    m.Field(f => f.WorkplaceId);
                })
            )
            .Aggregates(agg => {
                agg.Add(ce => ce.Operation.QuantityConfirmed).Sum();
                agg.Add(ce => ce.Operation.QuantityDefective).Sum();
                agg.Add(ce => ce.Operation.WorkOrder.QuantityToProduce).Sum();
                agg.Add(ce => ce.Operation.WorkOrder.QuantityOpen).Sum();
            })
            .Events(e => e.Error(handler: "scheduler_error"))
    )
    .ClientDetailTemplateId(id: "operationDetailTemplate")
    .Events(e => {e.DataBound(handler: "dispatcher.onGridDataBound"); e.Change(handler: "dispatcher.onGridChange"); })
    .Deferred()
)

Kind regards

Bernd

0
Alexander Popov
Telerik team
answered on 06 Jan 2017, 09:22 AM
Hello,

I reviewed the provided code snippets but I am still not sure what's causing the issue.
Does the OperationEvents request.Aggregates argument gets populated on the server? I've also noticed that the aggregate fields are nested properties of the Operation object. The Grid's DataSource however is unable to resolve the data type of nested properties, which might be related. You can exclude this as a possibility by trying to use top-level ViewModel fields for the aggregates or using a flattened ViewModel.

Regards,
Alexander Popov
Telerik by Progress
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Bernd
Top achievements
Rank 2
answered on 27 Jan 2017, 06:10 AM

Hello Alexander.

Sorrry for the delay.

I flattened moved the aggregate properties to the top level, but the aggregates argument still isn't populated.

Any other ideas?

0
Alexander Popov
Telerik team
answered on 31 Jan 2017, 09:34 AM
Hello,

May I ask you for some code snippets showing the model, as well as the request and response headers?

Regards,
Alexander Popov
Telerik by Progress
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Phil
Top achievements
Rank 1
answered on 18 Oct 2019, 08:47 AM

Hi Bernd,

 

Did you ever solve this?

0
Bernd
Top achievements
Rank 2
answered on 18 Oct 2019, 08:51 AM
More or less. The customer didn't really need the aggregates and I removed them.

Why are you asking? Do you have the same problem?
Tags
Grid
Asked by
Bernd
Top achievements
Rank 2
Answers by
Alexander Popov
Telerik team
Bernd
Top achievements
Rank 2
Phil
Top achievements
Rank 1
Share this question
or