diff --git a/README.md b/README.md index f992cb5..e0d49f3 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,9 @@ of `Person` but when a `Person` was selected you wanted the control to bind to a a `ConvertMethod` The convert method will be invoked by the control when a selection is made and will be passed the type selected. The method will need to handle the conversion and return the new type. +If you want to allow adding an item based on the search when no items have been found, you can achieve this by providing the `AddItemOnEmptyResultMethod` as a parameter. +This method will make the `NotFoundTemplate` selectable the same way a item would normally be, and will be invoked when the user selects the `NotFoundTemplate`. +This method passes the `SearchText` and expects a new item to be returned. ### Local Data Example ```cs diff --git a/samples/BlazorServer/Pages/Index.razor b/samples/BlazorServer/Pages/Index.razor index a78cf0e..83c9312 100644 --- a/samples/BlazorServer/Pages/Index.razor +++ b/samples/BlazorServer/Pages/Index.razor @@ -150,8 +150,44 @@ {

@person.Firstname @person.Lastname

} +
} +

Blazored Typeahead - Multi-select - Adding items on empty search result

+ + + + @person.Firstname + + + Please enter a minimum of 2 characters to perform a search. + + + Add "@searchText" as a new user
+ The newly added user will be selected after being added. +
+ + @person.Firstname @person.Lastname (Id: @person.Id) + +
+ +
+ +@if (SelectedPeopleWithNotFoundTemplate != null) +{ +

Selected People:

+ foreach (var person in SelectedPeopleWithNotFoundTemplate) + { +

@person.Firstname @person.Lastname

+ } +
+} @code { @@ -164,6 +200,7 @@ private Person SelectedPersonNull; private int? SelectedPersonId; private IList SelectedPeople; + private IList SelectedPeopleWithNotFoundTemplate; private FormExample FormModel = new FormExample(); protected override void OnInitialized() @@ -242,4 +279,14 @@ { return People.FirstOrDefault(p => p.Id == id); } + + private readonly Random _random = new Random(); + private Task ItemAddedMethod(string searchText) + { + var randomPerson = People[_random.Next(People.Count - 1)]; + var newPerson = new Person(_random.Next(1000, int.MaxValue), searchText, randomPerson.Lastname, _random.Next(10, 70), randomPerson.Location); + People.Add(newPerson); + return Task.FromResult(newPerson); + } + } diff --git a/samples/BlazorWebAssembly/Pages/Index.razor b/samples/BlazorWebAssembly/Pages/Index.razor index 0aaa38e..d5e5e00 100644 --- a/samples/BlazorWebAssembly/Pages/Index.razor +++ b/samples/BlazorWebAssembly/Pages/Index.razor @@ -146,10 +146,45 @@ {

@person.Firstname @person.Lastname

} +
} +

Blazored Typeahead - Multi-select - Adding items on empty search result

+ + + + @person.Firstname + + + Please enter a minimum of 2 characters to perform a search. + + + Add "@searchText" as a new user
+ The newly added user will be selected after being added. +
+ + @person.Firstname @person.Lastname (Id: @person.Id) + +
+
+@if (SelectedPeopleWithNotFoundTemplate != null) +{ +

Selected People:

+ foreach (var person in SelectedPeopleWithNotFoundTemplate) + { +

@person.Firstname @person.Lastname

+ } +
+} +

Blazored Typeahead - Header and footer templates

SelectedPeople; + private IList SelectedPeopleWithNotFoundTemplate; private FormExample FormModel = new FormExample(); private BlazoredTypeahead LocalBlazoredTypeahead; @@ -266,4 +302,13 @@ { return People.FirstOrDefault(p => p.Id == id); } + + private readonly Random _random = new Random(); + private Task ItemAddedMethod(string searchText) + { + var randomPerson = People[_random.Next(People.Count - 1)]; + var newPerson = new Person(_random.Next(1000, int.MaxValue), searchText, randomPerson.Lastname, _random.Next(10, 70), randomPerson.Location); + People.Add(newPerson); + return Task.FromResult(newPerson); + } } diff --git a/src/Blazored.Typeahead/BlazoredTypeahead.razor b/src/Blazored.Typeahead/BlazoredTypeahead.razor index ab59520..c6d7350 100644 --- a/src/Blazored.Typeahead/BlazoredTypeahead.razor +++ b/src/Blazored.Typeahead/BlazoredTypeahead.razor @@ -185,9 +185,19 @@ @if (NotFoundTemplate != null) { -
- @NotFoundTemplate -
+ @if (AddItemOnEmptyResultMethod != null) + { +
+ @NotFoundTemplate(SearchText) +
+ } + else + { +
+ @NotFoundTemplate(SearchText) +
+ } } else { diff --git a/src/Blazored.Typeahead/BlazoredTypeahead.razor.cs b/src/Blazored.Typeahead/BlazoredTypeahead.razor.cs index a78a3a3..7931227 100644 --- a/src/Blazored.Typeahead/BlazoredTypeahead.razor.cs +++ b/src/Blazored.Typeahead/BlazoredTypeahead.razor.cs @@ -4,6 +4,7 @@ using Microsoft.JSInterop; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; @@ -35,8 +36,9 @@ public partial class BlazoredTypeahead : ComponentBase, IDisposab [Parameter] public Func>> SearchMethod { get; set; } [Parameter] public Func ConvertMethod { get; set; } + [Parameter] public Func> AddItemOnEmptyResultMethod { get; set; } - [Parameter] public RenderFragment NotFoundTemplate { get; set; } + [Parameter] public RenderFragment NotFoundTemplate { get; set; } [Parameter] public RenderFragment HelpTemplate { get; set; } [Parameter] public RenderFragment ResultTemplate { get; set; } [Parameter] public RenderFragment SelectedTemplate { get; set; } @@ -272,6 +274,10 @@ private async Task HandleKeyup(KeyboardEventArgs args) { await SelectTheFirstAndOnlySuggestion(); } + else if (args.Key == "Enter" && ShowNotFound() && AddItemOnEmptyResultMethod != null) + { + await SelectNotFoundPlaceholder(); + } else if (args.Key == "Enter" && SelectedIndex >= 0 && SelectedIndex < Suggestions.Count()) { await SelectResult(Suggestions[SelectedIndex]); @@ -369,6 +375,13 @@ private string GetSelectedSuggestionClass(TItem item, int index) return Equals(value, Value) ? resultClass : string.Empty; } + private string GetSelectedSuggestionClass(int index) + { + const string resultClass = "blazored-typeahead__active-item"; + + return index == SelectedIndex ? resultClass : string.Empty; + } + private async void Search(Object source, ElapsedEventArgs e) { if (_searchText.Length < MinimumLength) @@ -422,6 +435,21 @@ private async Task SelectResult(TItem item) Initialize(); } + private async Task SelectNotFoundPlaceholder() + { + Debug.Assert(AddItemOnEmptyResultMethod != null); + try + { + // Potentially dangerous code + var item = await AddItemOnEmptyResultMethod(SearchText); + await SelectResult(item); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + private bool ShouldShowHelpTemplate() { return SearchText.Length > 0 &&