diff --git a/src/AutoCrudAdmin/Controllers/AutoCrudAdminController.cs b/src/AutoCrudAdmin/Controllers/AutoCrudAdminController.cs
index 1e0f27f..a7de897 100644
--- a/src/AutoCrudAdmin/Controllers/AutoCrudAdminController.cs
+++ b/src/AutoCrudAdmin/Controllers/AutoCrudAdminController.cs
@@ -219,6 +219,7 @@ public override void OnActionExecuting(ActionExecutingContext context)
/// The property to search.
/// The autocomplete results.
[HttpGet]
+ [Obsolete]
public virtual IEnumerable Autocomplete([FromQuery] string searchTerm, string searchProperty)
{
var entityType = typeof(TEntity);
@@ -244,6 +245,38 @@ public virtual IEnumerable Autocomplete([FromQuery] string se
return entities;
}
+ ///
+ /// Handles autocomplete extended searches.
+ ///
+ /// The search term.
+ /// The property to search is configured by FormControlViewModel.
+ /// The max autocomplete result is configured by FormControlViewModel.
+ /// The autocomplete results.
+ [HttpGet]
+ public IEnumerable AutocompleteExtended(
+ [FromQuery] string searchTerm,
+ string searchProperty,
+ int maxResults)
+ {
+ if (string.IsNullOrWhiteSpace(searchProperty))
+ {
+ throw new ArgumentException("No search property added!");
+ }
+
+ var entityType = typeof(TEntity);
+ var searchedProperty = entityType.GetProperty(searchProperty);
+
+ var keys = entityType.GetPrimaryKeyPropertyInfos();
+
+ if (searchedProperty == null || !keys.Any())
+ {
+ throw new ArgumentException("No such property exists on the entity!");
+ }
+
+ var containsExpression = ExpressionsBuilder.ForGetPropertyContains(searchedProperty, searchTerm);
+ return this.FilterAutocompleteResults(maxResults, searchedProperty, keys, containsExpression);
+ }
+
///
/// Shows the index view. Contains a grid with paging, sorting and filtering.
///
@@ -720,7 +753,7 @@ protected IActionResult RedirectToActionWithStringFilter(
actionName,
gridStringFilterType.ToString());
- ///
+ ///
/// Builds custom grid columns.
///
/// Page columns.
@@ -836,6 +869,23 @@ private static void CopyFormPropertiesToExistingEntityFromNewEntity(TEntity exis
.ToList()
.ForEach(property => property.SetValue(existingEntity, property.GetValue(newEntity, null), null));
+ private IEnumerable FilterAutocompleteResults(int maxResults, PropertyInfo searchedProperty, IEnumerable keys, Expression> expression)
+ {
+ var entities = this.Set
+ .AsNoTracking()
+ .Where(expression)
+ .Take(maxResults)
+ .ToList();
+
+ return entities
+ .Select(x => new DropDownViewModel
+ {
+ Value = keys.Select(k => k.GetValue(x) !.ToString()),
+ Name = searchedProperty.GetValue(x) !.ToString() !,
+ })
+ .ToList();
+ }
+
private IHtmlGrid GenerateGrid(IHtmlHelper htmlHelper)
=> htmlHelper
.Grid(this.GetQueryWithIncludes(this.MasterGridFilter))
diff --git a/src/AutoCrudAdmin/Enumerations/FormControlType.cs b/src/AutoCrudAdmin/Enumerations/FormControlType.cs
index 85b68c2..2b95ce9 100644
--- a/src/AutoCrudAdmin/Enumerations/FormControlType.cs
+++ b/src/AutoCrudAdmin/Enumerations/FormControlType.cs
@@ -29,4 +29,9 @@ public enum FormControlType
/// Represents an autocomplete control, where the user's input is completed automatically as they type.
///
Autocomplete = 4,
+
+ ///
+ /// Represents an autocomplete control, where the user's input is completed automatically as they type. Extended to work with more then just strings and configure results received.
+ ///
+ AutocompleteExtended = 5,
}
diff --git a/src/AutoCrudAdmin/Extensions/TypeExtensions.cs b/src/AutoCrudAdmin/Extensions/TypeExtensions.cs
index 37f30a1..27432a6 100644
--- a/src/AutoCrudAdmin/Extensions/TypeExtensions.cs
+++ b/src/AutoCrudAdmin/Extensions/TypeExtensions.cs
@@ -149,6 +149,18 @@ public static bool IsNavigationProperty(this Type type)
public static bool IsEnumerableExceptString(this Type type)
=> typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string);
+ ///
+ /// This method returns the MethodInfo object for the ToString method of the Object class.
+ ///
+ /// Returns MethodInfo? (object.ToString()).
+ public static MethodInfo? GetObjectToStringMethodInfo() => typeof(object).GetMethod(nameof(string.ToString));
+
+ ///
+ /// This method returns the MethodInfo object for the Contains method of the String class.
+ ///
+ /// Returns MethodInfo? (string.Contains()).
+ public static MethodInfo? GetStringContainsMethodInfo() => typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) });
+
private static DbContext? CreateDbContext(Type type)
{
try
diff --git a/src/AutoCrudAdmin/Helpers/ExpressionsBuilder.cs b/src/AutoCrudAdmin/Helpers/ExpressionsBuilder.cs
index ecd96c6..1f04872 100644
--- a/src/AutoCrudAdmin/Helpers/ExpressionsBuilder.cs
+++ b/src/AutoCrudAdmin/Helpers/ExpressionsBuilder.cs
@@ -112,6 +112,42 @@ public static Func ForGetPropertyValue(PropertyInfo pr
instanceParam).Compile();
}
+ ///
+ /// Constructs a method for getting values that contains specific search term.
+ ///
+ /// The type of the entity.
+ /// The property for which we construct the method.
+ /// The search term.
+ /// All matching obejects.
+ public static Expression> ForGetPropertyContains(PropertyInfo property, string searchTerm)
+ {
+ var entityType = typeof(TEntity);
+ var parameter = Expression.Parameter(entityType, "model");
+
+ var propertyAccess = Expression.Property(parameter, property);
+ var searchTermExpression = Expression.Constant(searchTerm, typeof(string));
+
+ var containsMethod = Extensions.TypeExtensions.GetStringContainsMethodInfo();
+
+ if (property.PropertyType == typeof(string))
+ {
+ // model.Property.Contains(searchTerm)
+ var containsCall = Expression.Call(propertyAccess, containsMethod !, searchTermExpression);
+
+ // model => model.Property.Contains(searchTerm)
+ return Expression.Lambda>(containsCall, parameter);
+ }
+
+ // If the property type is not string, convert it to string and then use Contains method
+ var toStringMethod = Extensions.TypeExtensions.GetObjectToStringMethodInfo();
+
+ MethodCallExpression? toStringCall = null;
+ toStringCall = Expression.Call(propertyAccess, toStringMethod !);
+
+ var body = Expression.Call(toStringCall, containsMethod !, searchTermExpression);
+ return Expression.Lambda>(body, parameter);
+ }
+
private static Expression ForPrimaryKeySubExpression(
string name,
string value,
diff --git a/src/AutoCrudAdmin/TagHelpers/FormInputTagHelper.cs b/src/AutoCrudAdmin/TagHelpers/FormInputTagHelper.cs
index 43ee4bc..1e9e878 100644
--- a/src/AutoCrudAdmin/TagHelpers/FormInputTagHelper.cs
+++ b/src/AutoCrudAdmin/TagHelpers/FormInputTagHelper.cs
@@ -138,7 +138,7 @@ public override Task ProcessAsync(TagHelperContext context, TagHelperOutput outp
{
this.PrepareExpandableMultiChoiceCheckBox(output);
}
- else if (this.FormControlType == FormControlType.Autocomplete)
+ else if (this.FormControlType == FormControlType.Autocomplete || this.FormControlType == FormControlType.AutocompleteExtended)
{
this.PrepareAutocompleteDropdown(output);
}
diff --git a/src/AutoCrudAdmin/ViewModels/FormControlViewModel.cs b/src/AutoCrudAdmin/ViewModels/FormControlViewModel.cs
index 9de97d7..51cf7dd 100644
--- a/src/AutoCrudAdmin/ViewModels/FormControlViewModel.cs
+++ b/src/AutoCrudAdmin/ViewModels/FormControlViewModel.cs
@@ -82,4 +82,14 @@ public string DisplayName
/// Gets or sets the entity id for the autocomplete feature of the form control.
///
public string? FormControlAutocompleteEntityId { get; set; }
+
+ ///
+ /// Gets or sets the entity property that autocomplete will search by. (Autocomplete Extended).
+ ///
+ public string? SearchByPropertyAutocompleteConfiguration { get; set; }
+
+ ///
+ /// Gets or sets how much autocomplete results will be shown (20 by default). (Autocomplete Extended).
+ ///
+ public int NumberOfAutocompleteItemsShownConfiguration { get; set; } = 20;
}
\ No newline at end of file
diff --git a/src/AutoCrudAdmin/Views/Shared/Autocomplete/_AutocompleteExtendedPartial.cshtml b/src/AutoCrudAdmin/Views/Shared/Autocomplete/_AutocompleteExtendedPartial.cshtml
new file mode 100644
index 0000000..fcd0e7a
--- /dev/null
+++ b/src/AutoCrudAdmin/Views/Shared/Autocomplete/_AutocompleteExtendedPartial.cshtml
@@ -0,0 +1,44 @@
+@model AutoCrudAdmin.ViewModels.FormControlViewModel
+
+@{
+ var controllerName = Model.FormControlAutocompleteController;
+}
+
+@Html.Hidden("AutocompleteId", "#" + Model.Name + "Id")
+@Html.Hidden("ControllerName", controllerName)
+@Html.Hidden(Model.FormControlAutocompleteEntityId, "")
+
+
\ No newline at end of file
diff --git a/src/AutoCrudAdmin/Views/Shared/Autocomplete/_AutocompletePartial.cshtml b/src/AutoCrudAdmin/Views/Shared/Autocomplete/_AutocompletePartial.cshtml
new file mode 100644
index 0000000..ae716c9
--- /dev/null
+++ b/src/AutoCrudAdmin/Views/Shared/Autocomplete/_AutocompletePartial.cshtml
@@ -0,0 +1,42 @@
+@model AutoCrudAdmin.ViewModels.FormControlViewModel
+
+@{
+ var controllerName = Model.FormControlAutocompleteController;
+}
+
+@Html.Hidden("AutocompleteId", "#" + Model.Name + "Id")
+@Html.Hidden("ControllerName", controllerName)
+@Html.Hidden(Model.FormControlAutocompleteEntityId, "")
+
+
\ No newline at end of file
diff --git a/src/AutoCrudAdmin/Views/Shared/_EnityFormControlsPartial.cshtml b/src/AutoCrudAdmin/Views/Shared/_EnityFormControlsPartial.cshtml
index 5c8c657..1b9babf 100644
--- a/src/AutoCrudAdmin/Views/Shared/_EnityFormControlsPartial.cshtml
+++ b/src/AutoCrudAdmin/Views/Shared/_EnityFormControlsPartial.cshtml
@@ -56,44 +56,11 @@
if (formControl.FormControlType == FormControlType.Autocomplete)
{
- @Html.Hidden("AutocompleteId", "#" + formControl.Name + "Id")
- var controllerName = formControl.FormControlAutocompleteController;
- @Html.Hidden("ControllerName", controllerName)
- @Html.Hidden(formControl.FormControlAutocompleteEntityId, "")
-
-
+ @await Html.PartialAsync("Autocomplete/_AutocompletePartial", formControl);
+ }
+ else if (formControl.FormControlType == FormControlType.AutocompleteExtended)
+ {
+ @await Html.PartialAsync("Autocomplete/_AutocompleteExtendedPartial", formControl);
}
}