MultiSelect Server Side Filtering - Razor Pages - ASP NET Core

1 Answer 422 Views
MultiSelect
Oliver
Top achievements
Rank 1
Oliver asked on 21 Aug 2022, 06:22 PM

Hello,

I have followed various examples and my very minimal knowledge of Kendo to attempt to filter a large dataset on the server side Razor Pages Page. 

I have used this: https://docs.telerik.com/aspnet-core/html-helpers/editors/multiselect/binding/razor-page

However, I cannot get the filtered data to return correctly without type arguments being thrown by the method.

Things I have checked

  1. Pascal Case is being returned despite getting a Splice error.
  2. An empty array is being returned on from the server as a default

Code

1. MultiSelect Component

@model Guid

@(Html.Kendo().MultiSelectFor(x => x)
    .DataTextField("OrderNumber")
    .DataValueField("Id")
    .Placeholder("Start typing order numbers...")
    .Filter(FilterType.Contains)
    .DataSource(dataSource =>
    {
        dataSource.Ajax();
        dataSource.Read(r => r.Url("/XXXX/XXXX/Index?handler=OrderMultiSelectRead")
            .Data("forgeryToken")
            .Type(HttpVerbs.Post))
            .ServerFiltering(true);
    }
    ).HtmlAttributes(new {style ="width: 100%" }))

2. Server Side Code

public async Task<IActionResult> OnPostOrderMultiSelectRead([DataSourceRequest] DataSourceRequest request, string text)
        {
            var result = new List<Order>();
            try
            {

                var filters = request?.Filters?.Cast<FilterDescriptor>().ToList();

                var firstFilter = filters.FirstOrDefault();

                if (firstFilter == null) return new JsonResult(await result.ToDataSourceResultAsync(request));
                
                firstFilter.MemberType = typeof(int?);

                var firstFilterValue = firstFilter.Value.ToString();

                if (string.IsNullOrEmpty(firstFilterValue) || firstFilterValue.Length < 5)
                    return new JsonResult(await result.ToDataSourceResultAsync(request));

                var orderNumberParsedAsInt = int.Parse(firstFilterValue);

                var matchingOrders =
                    (await this.orderRepository.GetModelsAsync(x => x.OrderNumber == orderNumberParsedAsInt))
                    .OrderByDescending(x => x.OrderNumber).ToList();

                return new JsonResult(await matchingOrders.ToDataSourceResultAsync(request));
            }
            catch(Exception exception)
            {
                var resultPayload = await result.ToDataSourceResultAsync(request);

                resultPayload.Errors = new List<Exception> { exception };

                return new JsonResult(resultPayload);
            }
        }
Really struggling with this!

1 Answer, 1 is accepted

Sort by
-1
Accepted
Momchil
Telerik team
answered on 24 Aug 2022, 01:47 PM

Hi Oliver,

Thank you for providing your configuration.

To filter the MultiSelect data server-side, follow the steps below:

1. Set the ServerFiltering() option of the DataSource to "true".

@(Html.Kendo().MultiSelect()
    .Name("demoMultiSelect")
    ...
    .DataSource(dataSource =>
    {
        ...
        dataSource.ServerFiltering(true);
    })
)

2. Set the Filter property of the MultiSelect in case you want to override the default filter operator ("StartsWith").

@(Html.Kendo().MultiSelect()
    .Name("demoMultiSelect")
    ...
    .Filter(FilterType.Contains)
    ...
)

3. In the PageModel, call the ToDataSourceResult() method and pass the DataSourceRequest to it.

public JsonResult OnPostOrderMultiSelectRead([DataSourceRequest] DataSourceRequest request)
{
            return new JsonResult(orders.ToDataSourceResult(request));
}

I am attaching a runnable demo that illustrates this example.

Let me know if it works as per your expectations.

In case the issue persists, I would like to ask you to modify the attached sample to reproduce the behavior and send it back to me. It would be of great help, as I will have a runnable sample to diagnose the issue further.

Looking forward to hearing back from you.

 

Best Regards,
Momchil
Progress Telerik

The Premier Dev Conference is back! 

Coming to you live from Progress360 in-person or on your own time, DevReach for all. Register Today.


Oliver
Top achievements
Rank 1
commented on 25 Aug 2022, 06:42 PM

As you can see from the code sample I originally attached, filtering on the server side, using the example code you have attached, which is the same as the code in the article, does not work.

In MVC this works: https://demos.telerik.com/aspnet-mvc/multiselect/serverfiltering

But there is no ASP.NET Core Razor Pages variant that mirrors this.

Also, the filter model is populated as you can see in my code sample and my variable 

orderNumberParsedAsInt
has a value that I can use to retrieve orders, however, binding the result back or returning anything from the method breaks the control and due to a lack of documentation around this, it remains a blocker.
Oliver
Top achievements
Rank 1
commented on 30 Aug 2022, 01:42 PM

Is there any update on this? As you can see, I have already used the code you provided in my original submission?
Oliver
Top achievements
Rank 1
commented on 30 Aug 2022, 02:15 PM

https://demos.telerik.com/aspnet-mvc/multiselect/serverfiltering

This is what I am trying to do but in Razor Pages ASP-NET Core.

Mihaela
Telerik team
commented on 30 Aug 2022, 02:28 PM

Hello Oliver,

The sample from my previous reply uses Ajax binding and relies on the ToDataSourceResult() method to take care of the filtering.

However, if you want to implement custom server-side filtering, the method shown in the MVC MultiSelect server filtering demo, indeed, does not work for Razor Pages because the "text" field is not sent along with the request. I will contact our developer team to discuss whether this behavior is intended and will get back to you once I have more information.

Meanwhile, I would suggest using the following approach to implement a custom server-side filtering for the MultiSelect on a Razor Pages scenario:

1. Specify the minimum number of characters needed to perform a search and remove the Ajax() option of the DataSource.

@(Html.Kendo().MultiSelect()
    .Name("demoMultiSelect")
    ...
    .MinLength(3)
    .DataSource(dataSource =>
    {
        //dataSource.Ajax();
        dataSource.Read(r => r.Url("/Index?handler=OrderMultiSelectRead")...
    })
    .HtmlAttributes(new { style = "width: 100%" })
)

2. Pass the current input as additional data to the request using the Data handler.

@(Html.Kendo().MultiSelect()
    ...
    .DataSource(dataSource =>
    {
        dataSource.Read(r => r.Url("/Index?handler=OrderMultiSelectRead").Data("forgeryToken").Type(HttpVerbs.Post));
        dataSource.ServerFiltering(true);
    })
    .HtmlAttributes(new { style = "width: 100%" })
)

<script>
    function forgeryToken() {
        return {
            __RequestVerificationToken: kendo.antiForgeryTokens().__RequestVerificationToken,
            text: $("#demoMultiSelect_taglist > input").val()
        };
    }
</script

3. Filter the result as per your requirements in the PageModel.

public IActionResult OnPostOrderMultiSelectRead(string text)
        {
            var empty = Enumerable.Empty<OrderViewModel>();

            if (String.IsNullOrEmpty(text))
            {
                return new JsonResult(empty);
            }

            try
            {
                var orderNumberParsedAsInt = int.Parse(text);

                var filteredOrders = orders.Where(x => x.OrderNumber == orderNumberParsedAsInt).ToList();

                return new JsonResult(filteredOrders);
            }
            catch
            {
                return new JsonResult(empty);
            }            
        }

Also, I have updated the demo from my initial answer based on this example. You can find it attached to this reply.

On a separate note, if you would like to customize the default DataSource request filters, refer to the Grid custom ajax binding demo for an example implementation ("ApplyOrdersFiltering" method in the GridController).

Let me know if the suggested approach helps you achieve the desired behavior.

 

Regards,MomchilProgress Telerik

Oliver
Top achievements
Rank 1
commented on 30 Aug 2022, 03:57 PM

Thank you Momchil.

I was able to fix my implementation by using your above example.

Mario
Top achievements
Rank 1
commented on 30 Jun 2023, 04:27 AM

Hi, I was using this method too, but on upgrading to 2023.2.606 it seems to no longer work. The exact same code in the previous version would send this to Razor Pages method (custom parameter is called filterValue)

 

But on upgrading, with no changes to the code, it is no longer sending the customer parameter:

 

Mario
Top achievements
Rank 1
commented on 30 Jun 2023, 04:46 AM

Found the reason why. The HTML for the multiselect has changed - where the input used to be a child of the div, it is now a sibling. So this selector is incorrect:

function forgeryToken() {
        return {
            __RequestVerificationToken: kendo.antiForgeryTokens().__RequestVerificationToken,
            text: $("#demoMultiSelect_taglist > input").val()
        };
    }

 

This would so much easier if we didn't need this hack to get it to work.

 

Mihaela
Telerik team
commented on 04 Jul 2023, 08:53 AM

Hi Mario,

As of the R1 2022 release, the MultiSelect component uses a new rendering, as explained in the following article:

https://docs.telerik.com/aspnet-core/html-helpers/editors/multiselect/appearance#old-vs-new-rendering

Another option to pass the search entry through the Read request is:

function forgeryToken() {
        return {
            __RequestVerificationToken: kendo.antiForgeryTokens().__RequestVerificationToken,
            text: kendo.ui.MultiSelect.requestData($("#demoMultiSelect"))
        };
    }

Tags
MultiSelect
Asked by
Oliver
Top achievements
Rank 1
Answers by
Momchil
Telerik team
Share this question
or