Skip to content

Commit 4b5a149

Browse files
Tsvetomir-HrTsvetomir-Hr
authored andcommitted
chore: modify solution section
1 parent b4283ac commit 4b5a149

File tree

1 file changed

+124
-48
lines changed

1 file changed

+124
-48
lines changed

knowledge-base/combobox-auto-select-on-blur.md

Lines changed: 124 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -23,87 +23,163 @@ res_type: kb
2323

2424
The article asnwers to the following question:
2525

26-
* How can I configure the ComboBox to automatically select the first matching item when the input loses focus (e.g., when the user tabs away or clicks outside)?
27-
* How do I auto-select a ComboBox item based on user input when focus is lost?
26+
* How to configure the ComboBox to automatically select the first matching item when the input loses focus (e.g., when the user tabs away or clicks outside)?
27+
* How to auto-select a ComboBox item based on user input when focus is lost?
2828
* Can the ComboBox select a suggested item on blur without pressing Enter?
2929
* How to set the ComboBox value when the user leaves the input field?
3030

3131
## Solution
32-
To automatically select the first matching item in the ComboBox when the input loses focus (on blur), follow these steps:
32+
To automatically select the first matching item in the ComboBox when the input loses focus, use a combination of the ComboBox [`OnRead` event](slug:components/combobox/events#onread) and JavaScript interop. The provided example demonstrates how to:
3333

34-
1. Handle the [`OnBlur` event](slug:components/combobox/events#onblur) of the ComboBox to detect when the input loses focus.
35-
2. Retrieve the currently highlighted or filtered item using a JavaScript helper function via `JS interop`.
36-
3. Check for a matching item in the ComboBox data based on the user's input.
37-
4. Set the matching item as the selected value programmatically.
34+
1. Use the `OnRead` event to filter data and store the first matching item.
35+
2. Attach a JavaScript event handler to detect when the user blurs the ComboBox input.
36+
3. Invoke a .NET method from JavaScript to set the ComboBox value to the first matching item when focus is lost.
37+
4. Update the ComboBox selection programmatically and refresh the UI.
3838

3939
>caption Auto-select the first matching item on blur
4040
4141
````RAZOR
42+
@using Telerik.DataSource.Extensions
43+
44+
@implements IDisposable
45+
4246
@inject IJSRuntime js
4347
48+
<p>ComboBoxFirstItem: @ComboBoxFirstItem?.Text</p>
49+
4450
<p>Selected value: @ComboBoxValue</p>
45-
<p>First Filtered value: @FirstFilteredItem</p>
46-
47-
48-
<span onkeyup="@GetFirstFilteredItem">
49-
<TelerikComboBox Data="@ComboBoxData"
50-
@bind-Value="@ComboBoxValue"
51-
TextField="@nameof(ListItem.Text)"
52-
ValueField="@nameof(ListItem.Value)"
53-
Filterable="true"
54-
OnBlur="@SelectItemOnTab"
55-
OnOpen="@( () => IsComboBoxOpen = true )"
56-
OnClose="@( () => IsComboBoxOpen = false )"
57-
Width="200px">
58-
<ComboBoxSettings>
59-
<ComboBoxPopupSettings Class="select-on-tab" />
60-
</ComboBoxSettings>
61-
</TelerikComboBox>
62-
</span>
63-
<br />
64-
<br />
65-
<TelerikTextBox Width="200px" Placeholder="another element" />
6651
52+
<TelerikComboBox OnRead="@OnComboBoxRead"
53+
TItem="@ListItem"
54+
TValue="@int"
55+
Value="@ComboBoxValue"
56+
ValueChanged="@( (int newValue) => ComboBoxValueChanged(newValue) )"
57+
TextField="@nameof(ListItem.Text)"
58+
ValueField="@nameof(ListItem.Id)"
59+
Filterable="true"
60+
FilterOperator="@StringFilterOperator.Contains"
61+
Placeholder="Select an item..."
62+
ShowClearButton="true"
63+
Width="200px"
64+
Id="combo-1">
65+
<ComboBoxSettings>
66+
<ComboBoxPopupSettings Class="select-on-tab" />
67+
</ComboBoxSettings>
68+
</TelerikComboBox>
69+
<br/>
70+
<br/>
71+
<TelerikTextBox Placeholder="Next form item" Width="200px"/>
72+
73+
@* Move JavaScript to a separate JS file *@
6774
<script suppress-error="BL9992">
68-
function getHighligtedComboItem() {
69-
var focusedItem = document.querySelector(".select-on-tab .k-list-item.k-focus");
70-
if (focusedItem) {
71-
return focusedItem.innerText;
75+
function attachComboKeyDown(selector) {
76+
var comboInput = document.querySelector(selector);
77+
if (comboInput) {
78+
comboInput.addEventListener("keydown", onComboInputKeyDown);
79+
}
80+
}
81+
82+
function detachComboKeyDown(selector) {
83+
var comboInput = document.querySelector(selector);
84+
if (comboInput) {
85+
comboInput.removeEventListener("keydown", onComboInputKeyDown);
86+
}
87+
}
88+
89+
function onComboInputKeyDown(e) {
90+
if (e.key == "Tab") {
91+
dotNet.invokeMethodAsync("OnComboBoxTab", e.target.value);
92+
}
93+
}
94+
95+
var dotNet;
96+
97+
function saveDotNetRef(dotNetRef) {
98+
dotNet = dotNetRef;
7299
}
73-
}
74100
</script>
75101
76102
@code {
77-
private IEnumerable<ListItem> ComboBoxData = Enumerable.Range(1, 123).Select(x => new ListItem { Text = "Item " + x, Value = x });
103+
private DotNetObjectReference<__Main>? DotNetRef { get; set; }
104+
105+
private List<ListItem> ComboBoxData { get; set; } = new();
78106
private int ComboBoxValue { get; set; }
79-
private string FirstFilteredItem { get; set; } = string.Empty;
80-
private bool IsComboBoxOpen { get; set; }
107+
private ListItem? ComboBoxFirstItem { get; set; }
108+
109+
[JSInvokable("OnComboBoxTab")]
110+
public void OnComboBoxTab(string newStringValue)
111+
{
112+
if (ComboBoxFirstItem is not null && ComboBoxFirstItem.Text.Contains(newStringValue))
113+
{
114+
ComboBoxValue = ComboBoxFirstItem.Id;
115+
ComboBoxFirstItem = default;
116+
117+
StateHasChanged();
118+
}
119+
}
81120
82-
private async Task GetFirstFilteredItem()
121+
private void ComboBoxValueChanged(int newValue)
83122
{
84-
FirstFilteredItem = await js.InvokeAsync<string>("getHighligtedComboItem");
123+
ComboBoxValue = newValue;
124+
ComboBoxFirstItem = default;
85125
}
86126
87-
private void SelectItemOnTab()
127+
private async Task OnComboBoxRead(ReadEventArgs args)
88128
{
89-
if (!string.IsNullOrEmpty(FirstFilteredItem))
129+
var result = await ComboBoxData.ToDataSourceResultAsync(args.Request);
130+
131+
args.Data = result.Data;
132+
args.Total = result.Total;
133+
134+
if (args.Request.Filters.Count > 0)
90135
{
91-
var matchingItem = ComboBoxData.FirstOrDefault(x => x.Text.ToLowerInvariant().Contains(FirstFilteredItem.Trim().ToLowerInvariant()));
92-
if (matchingItem != null)
93-
{
94-
ComboBoxValue = matchingItem.Value;
95-
FirstFilteredItem = string.Empty;
96-
}
136+
ComboBoxFirstItem = args.Data.Cast<ListItem>().First();
137+
}
138+
else
139+
{
140+
ComboBoxFirstItem = default;
141+
}
142+
}
143+
144+
protected override async Task OnAfterRenderAsync(bool firstRender)
145+
{
146+
if (firstRender)
147+
{
148+
await Task.Delay(1); // ensure HTML is ready
149+
await js.InvokeVoidAsync("saveDotNetRef", DotNetRef);
150+
await js.InvokeVoidAsync("attachComboKeyDown", "#combo-1");
97151
}
152+
153+
await base.OnAfterRenderAsync(firstRender);
154+
}
155+
156+
protected override void OnInitialized()
157+
{
158+
DotNetRef = DotNetObjectReference.Create(this);
159+
160+
for (int i = 1; i <= 24; i++)
161+
{
162+
ComboBoxData.Add(new ListItem()
163+
{
164+
Id = i,
165+
Text = $"Item {i}"
166+
});
167+
}
168+
}
169+
170+
public void Dispose()
171+
{
172+
DotNetRef?.Dispose();
173+
_ = js.InvokeVoidAsync("detachComboKeyDown", "#combo-1");
98174
}
99175
100176
public class ListItem
101177
{
102-
public int Value { get; set; }
178+
public int Id { get; set; }
103179
public string Text { get; set; } = string.Empty;
104180
}
105181
}
106182
````
107183
## See Also
108184

109-
- [ComboBox Events in Telerik UI for Blazor](slug:components/combobox/events)
185+
- [ComboBox Events](slug:components/combobox/events)

0 commit comments

Comments
 (0)