Declined
Last Updated: 26 Sep 2024 15:46 by ADMIN
Aaron
Created on: 12 Sep 2024 17:13
Category: ComboBox
Type: Bug Report
1
ComboBox with virtualization does not account for sorting when mapping values

REPL to reproduce the problem following the steps below: https://netcorerepl.telerik.com/wSYNlmbA55iBcMhu20

Steps to reproduce

Type "wil" to get suggestions:


Hit tab to accept the first suggestion.

Click the arrow to display the list of options:


Observe the list of unrelated options, and the textbox now containing the value of the first option listed:

 

Additional information

The "normal" read request sends what is ultimately handled as a DataSourceRequest object in the controller that includes (among other things) information on how the data should be sorted. The "value mapper" request, on the other hand, sends only the raw values that need to be mapped (without any information on how they are being sorted by the DataSource). As a result, the positional index returned to the value mapper is incorrect if/when the data is sorted differently.

For example, the "normal" read request calls the controller action, it retrieves data [A, C, D, B], and the DataSourceRequest is applied to sort it as [A, B, C, D] and this is how it's displayed in the dropdown list. If the value "B" needs to be mapped, the "value mapper" request calls a similar (but different) controller action, it retrieves the same set of data [A, C, D, B] but does not make any attempt to sort it, finds "B" in the list (#4), and this is returned to the component (which then sets the dropdown's selectedIndex = 4, but in the dropdown's sorted list of data, this corresponds with the value "D", not "B" [which would be 2]).

4 comments
ADMIN
Mihaela
Posted on: 26 Sep 2024 15:46

Hello Aaron,

Thank you for your valuable feedback.

I have discussed the case with the development team and they informed me that by design, the ValueMapper() is a handler, which can be configured based on the developers' needs. Its function is to pass the item's Id to the server, so any additional information such as the sorting expression can be sent through the Ajax request to the server. As you suggested, the respective sorting expression can be obtained through the ComboBox's DataSource settings (sort() method) and passed through the request payload.


Regards,
Mihaela
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Aaron
Posted on: 19 Sep 2024 18:37
The behavior you are experiencing when the ComboBox data is initially sorted is expected since the server-side Action that retrieves the index of the selected option based on the received ID value is not modified to use the sorted data. 

The action does not use the sorted data because the ComboBox does not provide the information needed in order to do this. It does include the DataSourceRequest info with the "read" request, but not with the value mapper request.

 

By design, it is in the hands of the developers to implement the JavaScript function for the ValueMapper() option and the respective server-side logic that returns the data item index to the ComboBox.

This makes sense. However, the ComboBox needs to provide the information needed to do this (ex. DataSourceRequest info). 

 

Also, the virtualization feature can be used with a custom type of DataSource, as well, where the DataSourceRequest is not applicable

I'm not entirely sure what this is supposed to mean, but if the DataSourceRequest isn't applicable, then just don't include it (or include a default/empty version of it). The ComboBox Virtualization demo you linked to doesn't have any client sorting enabled, but as you pointed out it clearly still has the DataSourceRequest included. It's not applicable, but it's also not causing any problems.

The read request (that includes the DataSourceRequest) is responsible for retrieving the list of options to display in the list. The value mapper (that does not include any sorting info) is responsible for answering the question "where in my list should this item be positioned". I cannot for the life of me think of a reason why the info used to return sorted data for the "read" request would not also be included in the value mapper request.

ADMIN
Mihaela
Posted on: 19 Sep 2024 14:11

Hello Aaron,

Thank you for the provided screenshots and code snippets that replicates the issue.

The behavior you are experiencing when the ComboBox data is initially sorted is expected since the server-side Action that retrieves the index of the selected option based on the received ID value is not modified to use the sorted data. 

According to the ComboBox Virtualization demo, the Orders_ValueMapper Action returns the respective index using GetOrders() method, which returns the non-sorted data:

        public ActionResult Virtualization_Read([DataSourceRequest] DataSourceRequest request)
        {
            // The data is passed to the ToDataSourceResult() method, which sorts the items based on the "request" information.
            return Json(GetOrders().ToDataSourceResult(request));
        }

        public ActionResult Orders_ValueMapper(int?[] values)
        {
            var indices = new List<int>();
            if (values != null && values.Any())
            {
                var index = 0;
                foreach (var order in GetOrders()) // GetOrders() returns the non-sorted data collection
                {
                    ...
                }
            }
            return Json(indices); // the returned index is based on the non-sorted data
        }

        private IEnumerable<OrderViewModel> GetOrders()
        {
            using (var northwind = GetContext())
            {
                return northwind.Orders.Select(order => new OrderViewModel
                {
                    ContactName = order.Customer.ContactName,
                    ...
                }).ToList();
            }
        }

By design, it is in the hands of the developers to implement the JavaScript function for the ValueMapper() option and the respective server-side logic that returns the data item index to the ComboBox. Also, the virtualization feature can be used with a custom type of DataSource, as well, where the DataSourceRequest is not applicable, so the developer can handle the sorting and mapping of the data item differently.

In this particular case, I would suggest sorting the data within the Orders_ValueMapper Action:

        public ActionResult Orders_ValueMapper(int?[] values)
        {
            var indices = new List<int>();

            if (values != null && values.Any())
            {
                var index = 0;
                var sortedData = GetOrders().OrderBy(item => item.ShipName).ThenBy(item => item.OrderID).ToList();
                foreach (var order in sortedData)
                {
                    ...
                }
            }
            return Json(indices);
        }

or storing the sorted data in a static collection in order to persist in the application memory:

public static List<OrderViewModel> sortedData;

public HomeController()
{
  if(sortedData == null)
  {
       sortedData = new List<OrderViewModel>();
       sortedData = GetOrders().OrderBy(item => item.ShipName).ThenBy(item => item.OrderID) .ToList();
  }
}

public ActionResult Orders_ValueMapper(int?[] values)
{
            var indices = new List<int>();
            if (values != null && values.Any())
            {
                var index = 0;
                foreach (var order in sortedData)
                {
                    ...
                }
            }
            return Json(indices);
}

If any questions arise, don't hesitate to share them.

Regards,
Mihaela
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Aaron
Posted on: 12 Sep 2024 21:43

Potential dirty workaround

    function convertValues(value) {
        var data = {};
        value = $.isArray(value) ? value : [value];
        for (var idx = 0; idx < value.length; idx++) {
            data["values[" + idx + "]"] = value[idx];
        }
        var sorts = $("#OrderID").data("kendoComboBox").dataSource.sort();
        data["sort"] = sorts
            .map((sort) => `${sort.field}-${sort.dir}`)
            .join("~");
        return data;

    }

Then you can update the controller action method:
public ActionResult Orders_ValueMapper(int?[] values, [DataSourceRequest] DataSourceRequest request)

Then you have access to the same ToDataSourceResult method used to sort the "normal" read method.