When used in Bootstrap Grid the DropDownList popup is misaligned on the first open. On a second click, it is aligned.
This behavior is also valid for Combobox and Autocomplete.
Reproduction code:
<div class="box">
<div class="row mt-3" style="align-items:center">
<div class="col-12 col-lg-4">
<span class="bold">ComboBox</span>
</div>
<div class="col">
<TelerikComboBox Data="@myComboData" TextField="ComboTextField" ValueField="ComboValueField"
@bind-Value="selectedCombo" ClearButton="true" Filterable="true">
</TelerikComboBox>
</div>
</div>
<div class="row mt-3" style="align-items:center">
<div class="col-12 col-lg-4">
<span class="bold">DropDownList</span>
</div>
<div class="col">
<TelerikDropDownList Data="@myDdlData" TextField="MyTextField" ValueField="MyValueField"
@bind-Value="@selectedDropDown">
</TelerikDropDownList>
</div>
</div>
<div class="row mt-3" style="align-items:center">
<div class="col-12 col-lg-4">
<span class="bold">Autocomplete</span>
</div>
<div class="col">
<TelerikAutoComplete Data="@Suggestions" @bind-Value="@AutoCompleteValue"
ClearButton="true" />
</div>
</div>
</div>
@code {
public int selectedDropDown { get; set; }
public int selectedCombo { get; set; }
public string AutoCompleteValue { get; set; }
//ComboBox
public class ComboModel
{
public int ComboValueField { get; set; }
public string ComboTextField { get; set; }
}
IEnumerable<ComboModel> myComboData = Enumerable.Range(1, 20).Select(x => new ComboModel { ComboTextField = "item " + x, ComboValueField = x });
//DropDownList
public class MyDdlModel
{
public int MyValueField { get; set; }
public string MyTextField { get; set; }
}
IEnumerable<MyDdlModel> myDdlData = Enumerable.Range(1, 20).Select(x => new MyDdlModel { MyTextField = "item " + x, MyValueField = x });
//Autocomplete
List<string> Suggestions { get; set; } = new List<string> {
"Manager", "Developer", "QA", "Technical Writer", "Support Engineer", "Sales Agent", "Architect", "Designer"
};
}
I have two cascaded dropdownlists, in attached example car brands and selectable colors. The dependent dropdown has defaulttext option. After the parent dropdown has changed, in its event handler I reload the dependent data source and set a new valid value. If the data source does not contain the previous (!) value, the dropdownlist calls the valuechanged callback with default value. In next round when dependent dropdown holds the default value, after reloading datasource it works as expected. And parent changes again, if the updated datasource does not contain the previous dependent value the dropdownlist sets its value to default.
When I do not pass defaulttext parameter, the dependent dropdown selects the first element from the updated datasource. In my case it is not acceptable because I want to set a specific value (in this example the default color), not the first and not the default.
This feedback drives me to try setting value to default and wait for re-render the descendant dropdownlist before set the wanted value.
Is there any way to avoid this behaviour of the dropdownlist?
<TelerikDropDownList TItem="Brand"
TValue="int"
ValueField=@nameof(Brand.Id)
TextField=@nameof(Brand.Name)
Data="@BrandList"
Value="@Data.BrandId"
ValueChanged=@OnBrandChanged />
<TelerikDropDownList TItem="Color"
TValue="int"
ValueField=@nameof(Brand.Id)
TextField=@nameof(Brand.Name)
Data="@FilteredColorList"
Value="@Data.ColorId"
ValueChanged="@OnColorChanged"
DefaultText="select color ..." />
async Task OnBrandChanged(int brandId)
{
Data.ColorId = default;
await Task.Delay(5);
Data.BrandId = brandId;
Data.ColorId = GetDefaultColor(brandId);
FilteredColorList = GetFilteredColorList(brandId);
}
When the Default text value is changed by some business logic, the component does not react to that change to update the Default text value in the view model.
DropDownList should re-render on change of the Default text parameter
At the moment, typing with the keyboard focuses the first item that starts with the last letter you pressed. To focus the next one you should either use Down Arrow, or type the same letter again.
I would like it to behave like the regular <select> or like a combo box with filtering - typing characters quickly should highlight the item that begins with all those characters, instead of using only the first letter.
===
Admit edit: some keywords for better findability: DropDownList keyboard search filter accessibility
Hello everyone,
We have opened this Feature Request to gather your insights on the addition of disabled items for the DropDownList.
===
UPDATE: The associated built-in feature is the OnItemRender event, which can help style specific items without a template. Then, use the ValueChanged event to prevent selection of disabled items with the keyboard.
<TelerikDropDownList Data="@Products"
DefaultText="Select Product..."
OnItemRender="@OnDropDownListItemRender"
TextField="@nameof(Product.Name)"
ValueField="@nameof(Product.Id)"
Value="@SelectedProductId"
ValueChanged="@( (int? newValue) => SelectedProductChanged(newValue) )"
Width="200px">
</TelerikDropDownList>
@code {
private List<Product> Products { get; set; } = new();
private TelerikDropDownList<Product, int?>? DropDownListRef { get; set; }
private int? SelectedProductId { get; set; }
private void OnDropDownListItemRender(DropDownListItemRenderEventArgs<Product> args)
{
// args.Item is null for the DefaultText item
if (args.Item != null && !args.Item.Active)
{
args.Class = "k-disabled";
}
}
private void SelectedProductChanged(int? newValue)
{
var newProduct = Products.FirstOrDefault(x => x.Id == newValue);
if (newProduct?.Active == true || !newValue.HasValue)
{
// select only active items or DefaultText
SelectedProductId = newValue;
}
else
{
// skip disabled items during keyboard navigation
var oldProductIndex = Products.FindIndex(x => x.Id == SelectedProductId);
var newProductIndex = Products.FindIndex(x => x.Id == newValue);
if (newProductIndex > oldProductIndex)
{
// skip forward
SelectedProductId = Products[++newProductIndex].Id;
}
else
{
// skip backward
SelectedProductId = Products[--newProductIndex].Id;
}
}
}
protected override void OnInitialized()
{
for (int i = 1; i <= 7; i++)
{
var active = i % 3 != 0;
Products.Add(new Product()
{
Id = i,
Name = (active ? "" : "Disabled ") + "Product " + i,
Active = active
});
}
base.OnInitialized();
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public bool Active { get; set; }
}
}
===
The example below shows the legacy approach to style items as disabled with DropDownList ItemTemplate and prevent clicks with @onclick. Again, use the ValueChanged event to skip disabled items during keyboard navigation.
<TelerikDropDownList Data="@Products"
TItem="@Product"
TValue="@(int?)"
Value="@SelectedProductId"
ValueField="@nameof(Product.Id)"
TextField="@nameof(Product.Name)"
DefaultText="Select Product..."
ValueChanged="@( (int? newValue) => SelectedProductChanged(newValue) )"
Width="200px">
<DropDownListSettings>
<DropDownListPopupSettings Class="disabled-items" />
</DropDownListSettings>
<ItemTemplate>
@{ var p = context as Product; }
<div class="item-div @( !p.Active ? "disabled" : "" )"
@onclick:preventDefault="@(!p.Active)"
@onclick:stopPropagation="@(!p.Active)">
@p.Name
</div>
</ItemTemplate>
</TelerikDropDownList>
<style>
/* remove default item padding to prevent selection outside the template div */
.disabled-items .k-list-item {
padding: 0;
}
/* add back inner padding */
.disabled-items .item-div {
padding: 4px 8px;
}
/* style disabled items */
.disabled-items .disabled {
cursor: not-allowed;
width: 100%;
color: #ccc;
}
</style>
@code {
private List<Product> Products { get; set; }
private int? SelectedProductId { get; set; }
private async Task SelectedProductChanged(int? newValue)
{
var newProduct = Products.FirstOrDefault(x => x.Id == newValue);
if (newProduct?.Active == true || !newValue.HasValue)
{
// select only active items or DefaultText
SelectedProductId = newValue;
}
else
{
// skip disabled items during keyboard navigation
var oldProductIndex = Products.FindIndex(x => x.Id == SelectedProductId);
var newProductIndex = Products.FindIndex(x => x.Id == newValue);
if (newProductIndex > oldProductIndex)
{
// skip forward
SelectedProductId = Products[++newProductIndex].Id;
}
else
{
// skip backward
SelectedProductId = Products[--newProductIndex].Id;
}
}
}
protected override void OnInitialized()
{
Products = new List<Product>();
for (int i = 1; i <= 7; i++)
{
var active = i % 3 != 0;
Products.Add(new Product()
{
Id = i,
Name = (active ? "" : "Disabled ") + "Product " + i,
Active = active
});
}
base.OnInitialized();
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
}
Clicking on the <label> opens the dropdown, but clicking on its main element does not
Selected value: @selectedValue
<br />
<label>some label for the dropdown, open it and test whether clicking outside closes it
<TelerikDropDownList Data="@myDdlData" TextField="MyTextField" ValueField="MyValueField" @bind-Value="selectedValue">
</TelerikDropDownList>
</label>
@code {
//in a real case, the model is usually in a separate file
//the model type and value field type must be provided to the dropdpownlist
public class MyDdlModel
{
public int MyValueField { get; set; }
public string MyTextField { get; set; }
}
IEnumerable<MyDdlModel> myDdlData = Enumerable.Range(1, 20).Select(x => new MyDdlModel { MyTextField = "item " + x, MyValueField = x });
int selectedValue { get; set; } = 3; //usually the current value should come from the model data
}
Before, the Value from the first item in the Data was populated through @bind-Value to the view model. It no longer is.
In the following snippet, I expect to see "1" in the initial load of the page, but I see "0" - the default value for the integer.
@selectedValue
<TelerikDropDownList Data="@myDdlData" TextField="MyTextField" ValueField="MyValueField" @bind-Value="@selectedValue">
</TelerikDropDownList>
@code {
//in a real case, the model is usually in a separate file
//the model type and value field type must be provided to the dropdpownlist
public class MyDdlModel
{
public int MyValueField { get; set; }
public string MyTextField { get; set; }
}
IEnumerable<MyDdlModel> myDdlData = Enumerable.Range(1, 20).Select(x => new MyDdlModel { MyTextField = "item " + x, MyValueField = x });
int selectedValue { get; set; }
}
Hi!
When i use combination of LINQ inside TelerikDropDownLists Data attribute and TelerikGrid 's GridEditMode.Popup mode, i get weird behavior when i try to select an item from TelerikDropDownLists - everything freezes. Please check the solution. Everything works fine if i don't use LINQ or GridEditMode.Popup mode.
Thank you!
Hello,
Initial I want no item to be selected to let the user select a value. I thought it would be enough if my bindvalue would be of a nullable type. I'm getting an exception when trying. If this is not supported, how can I do it?
<div class="form-group col-md-2">
<label class="col-form-label" for="ddReleaseType">Fönstertyp</label>
<TelerikDropDownList Data="@ReleaseTypeFilterOptions" @bind-Value="FilterState.ReleaseType" TextField="DisplayText" ValueField="ReleaseType" Class="max-width form-control" @ref="ddReleaseType"></TelerikDropDownList>
</div>
public enum ReleaseType : int
{
All = 0,
Category = 1,
Season = 2,
Misc = 3
}
protected Telerik.Blazor.Components.TelerikDropDownList<ReleaseTypeFilterModel?, SessionState.ReleaseType?> ddReleaseType;
protected class ReleaseTypeFilterModel
{
public SessionState.ReleaseType ReleaseType { get; set; }
public string DisplayText { get { return ReleaseType.ToString(); } }
}
Telerik.Blazor.Components.Common.TelerikSelectBase<TItem, TValue>.TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
_Host.cshtml
As stated in the documentation the Event OnChange for DropDownList is shown by intellisense but should not be used. However, in some situations it would be very useful to bind the value of the DropDownList and additionally have an event when the value changes, e.g. show additional inputs when a value is selected.
Currently it is either possible to have data binding to value by @bind-Value or listen for the changed event by using Value and ValueChanged.
Is it possible to drop dropDownList from outside, for example after its data has been changed, without clicking on it ?
ADMIN EDIT:
This feature is also applicable to ComboBox, AutoComplete, Multiselect, Date and Time Pickers components.