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]).
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.
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.
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.
Potential dirty workaround
}
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.