Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for an error handler template upon searching #277

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions samples/BlazorServer/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,23 @@
<hr />
}

<h1>Blazored Typeahead - Error Handler</h1>

<BlazoredTypeahead SearchMethod="@GetErrorPeople"
@bind-Value="@SelectedPersonNull"
placeholder="Search by first name...">
<SelectedTemplate Context="person">
@person.Firstname
</SelectedTemplate>
<ResultTemplate Context="person">
@person.Firstname @person.Lastname
</ResultTemplate>
<ErrorTemplate>
<span style="color: red;">@context.Message</span>
</ErrorTemplate>
</BlazoredTypeahead>
<hr />

@code {

private bool IsDisabled = true;
Expand Down Expand Up @@ -264,6 +281,16 @@
{
return await Task.FromResult(People.Where(x => x.Firstname.ToLower().Contains(searchText.ToLower())).ToList());
}

private async Task<IEnumerable<Person>> GetErrorPeople(string searchText)
{
if (searchText.Contains("win", StringComparison.InvariantCultureIgnoreCase))
{
return await GetPeopleLocal(searchText);
}

throw new InvalidOperationException("Only people with 'win' in name allowed");
}

private void HandleFormSubmit()
{
Expand Down
6 changes: 6 additions & 0 deletions src/Blazored.Typeahead/BlazoredTypeahead.razor
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,10 @@
}
</div>
}
else if (ShowError())
{
<div class="blazored-typeahead__notfound">
@ErrorTemplate(Error)
</div>
}
</div>
53 changes: 43 additions & 10 deletions src/Blazored.Typeahead/BlazoredTypeahead.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public partial class BlazoredTypeahead<TItem, TValue> : ComponentBase, IDisposab
[Parameter] public RenderFragment<TValue> SelectedTemplate { get; set; }
[Parameter] public RenderFragment HeaderTemplate { get; set; }
[Parameter] public RenderFragment FooterTemplate { get; set; }
[Parameter] public RenderFragment<Exception> ErrorTemplate { get; set; }

[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; }
[Parameter] public int MinimumLength { get; set; } = 1;
Expand All @@ -62,6 +63,10 @@ public partial class BlazoredTypeahead<TItem, TValue> : ComponentBase, IDisposab
private TItem[] Suggestions { get; set; } = new TItem[0];
private int SelectedIndex { get; set; }
private bool ShowHelpTemplate { get; set; } = false;

private Exception Error { get; set; }
private bool HasError => Error != null;

private string SearchText
{
get => _searchText;
Expand Down Expand Up @@ -304,7 +309,6 @@ private async Task ResetControl()
await ValuesChanged.InvokeAsync(Values);
_editContext?.NotifyFieldChanged(_fieldIdentifier);
}

}

[JSInvokable("ResetControlBlur")]
Expand Down Expand Up @@ -332,7 +336,7 @@ private async Task ShowMaximumSuggestions()
IsSearching = true;
await InvokeAsync(StateHasChanged);

Suggestions = (await SearchMethod?.Invoke(_searchText)).Take(MaximumSuggestions).ToArray();
Suggestions = await SearchForSuggestionsAsync();

IsSearching = false;
await InvokeAsync(StateHasChanged);
Expand Down Expand Up @@ -386,15 +390,32 @@ private async void Search(Object source, ElapsedEventArgs e)
ShowHelpTemplate = false;
IsSearching = true;
await InvokeAsync(StateHasChanged);
Suggestions = (await SearchMethod?.Invoke(_searchText)).Take(MaximumSuggestions).ToArray();
Suggestions = await SearchForSuggestionsAsync();

IsSearching = false;
IsShowingSuggestions = true;
await HookOutsideClick();
SelectedIndex = 0;
await InvokeAsync(StateHasChanged);
}

private async Task<TItem[]> SearchForSuggestionsAsync()
{
try
{
Error = null;
var suggestions = (await SearchMethod?.Invoke(_searchText)).Take(MaximumSuggestions).ToArray();
IsShowingSuggestions = true;

return suggestions;
}
catch (Exception e) when (ErrorTemplate != null)
{
IsShowingSuggestions = false;
Error = e;
return null;
}
}

private async Task HookOutsideClick()
{
await JSRuntime.OnOutsideClick(_searchInput, this, "ResetControlBlur", true);
Expand All @@ -403,7 +424,7 @@ private async Task HookOutsideClick()
private async Task SelectResult(TItem item)
{
var value = ConvertMethod(item);

if (IsMultiselect)
{
var valueList = Values ?? new List<TValue>();
Expand Down Expand Up @@ -444,14 +465,16 @@ private async Task SelectNotFoundPlaceholder()
private bool ShouldShowHelpTemplate()
{
return SearchText.Length > 0 &&
ShowHelpTemplate &&
HelpTemplate != null;
ShowHelpTemplate &&
HelpTemplate != null;
}

private bool ShouldShowSuggestions()
{
return IsShowingSuggestions &&
Suggestions.Any() && !IsSearchingOrDebouncing;
Suggestions.Any() &&
!IsSearchingOrDebouncing &&
!HasError;
}

private void MoveSelection(int count)
Expand All @@ -478,11 +501,21 @@ private Task SelectTheFirstAndOnlySuggestion()
}

private bool IsSearchingOrDebouncing => IsSearching || _debounceTimer.Enabled;

private bool ShowNotFound()
{
return IsShowingSuggestions &&
!IsSearchingOrDebouncing &&
!Suggestions.Any();
!Suggestions.Any() &&
!HasError;
}

private bool ShowError()
{
return ErrorTemplate != null &&
HasError &&
!IsShowingSuggestions &&
!IsSearchingOrDebouncing;
}

public void Dispose()
Expand All @@ -504,4 +537,4 @@ public async Task Focus()
await HandleClickOnMask();
}
}
}
}