diff --git a/docs/content/features/binding.md b/docs/content/features/binding.md
index 2ae3a4c..d8abbf4 100644
--- a/docs/content/features/binding.md
+++ b/docs/content/features/binding.md
@@ -142,6 +142,56 @@ which we can use later when submitting the form.
> NOTE: Make sure the temporary storage is cleared periodically.
+### Multiple files
+
+To support multiple files in one field, you can use the `multiple` attribute:
+
+```razor
+
+
+@model AddAttachment
+
+
+
+
+```
+
+Then your component code would change to:
+
+
+```c#
+// AddAttachment.cshtml.cs
+
+public class AddAttachment : HydroComponent
+{
+ [Transient]
+ public IFormFile[] DocumentFiles { get; set; }
+
+ [Required]
+ public List DocumentIds { get; set; }
+
+ public override async Task BindAsync(PropertyPath property, object value)
+ {
+ if (property.Name == nameof(DocumentFiles))
+ {
+ DocumentIds = [];
+ var files = (IFormFile[])value;
+
+ foreach (var file in files)
+ {
+ DocumentIds.Add(await GetStoredTempFileId(file));
+ }
+ }
+ }
+
+ // rest of the file same as in the previous example
+}
+```
+
## Styling
`.hydro-request` CSS class is toggled on the elements that are currently in the binding process
\ No newline at end of file
diff --git a/docs/content/features/errors-handling.md b/docs/content/features/errors-handling.md
index 559d6cf..0b6fdff 100644
--- a/docs/content/features/errors-handling.md
+++ b/docs/content/features/errors-handling.md
@@ -46,6 +46,7 @@ app.UseExceptionHandler(b => b.Run(async context =>
{
if (!context.IsHydro())
{
+ context.Response.Redirect("/Error");
return;
}
diff --git a/src/HydroComponent.cs b/src/HydroComponent.cs
index a144d7d..eb26b5b 100644
--- a/src/HydroComponent.cs
+++ b/src/HydroComponent.cs
@@ -64,7 +64,7 @@ public abstract class HydroComponent : ViewComponent
/// Default identifier used to specify place of the page to replace when during location change
///
public const string LocationTargetId = "hydro";
-
+
///
/// Provides list of already accessed component's properties
///
@@ -569,15 +569,33 @@ private async Task BindModel(IFormCollection formCollection)
}
}
- foreach (var file in formCollection.Files)
+ var fileGroups = formCollection.Files.GroupBy(m => m.Name);
+
+ foreach (var fileGroup in fileGroups)
{
- var setter = PropertyInjector.GetPropertySetter(this, file.Name, file);
- var propertyPath = PropertyPath.ExtractPropertyPath(file.Name);
+ var setter = PropertyInjector.GetPropertySetter(this, fileGroup.Key, null);
+ var propertyPath = PropertyPath.ExtractPropertyPath(fileGroup.Key);
if (setter != null)
{
- setter.Value.Setter(file);
- await BindAsync(propertyPath, file);
+ if (setter.Value.PropertyType.IsArray)
+ {
+ var formFiles = fileGroup.ToArray();
+ setter.Value.Setter(formFiles);
+ await BindAsync(propertyPath, formFiles);
+ }
+ else if (typeof(IEnumerable).IsAssignableFrom(setter.Value.PropertyType))
+ {
+ var formFiles = fileGroup.ToList();
+ setter.Value.Setter(formFiles);
+ await BindAsync(propertyPath, formFiles);
+ }
+ else if (setter.Value.PropertyType == typeof(IFormFile))
+ {
+ var formFile = fileGroup.First();
+ setter.Value.Setter(formFile);
+ await BindAsync(propertyPath, formFile);
+ }
}
else
{
@@ -685,7 +703,7 @@ private void PopulateDispatchers()
HttpContext.Response.Headers.TryAdd(HydroConsts.ResponseHeaders.Trigger, JsonConvert.SerializeObject(data, JsonSerializerSettings));
}
-
+
private void PopulateClientScripts()
{
if (!_clientScripts.Any())
@@ -1084,4 +1102,4 @@ internal void AddClientScript(string script) =>
private static string Hash(string input) =>
$"W{Convert.ToHexString(MD5.HashData(Encoding.ASCII.GetBytes(input)))}";
-}
+}
\ No newline at end of file
diff --git a/src/PropertyInjector.cs b/src/PropertyInjector.cs
index f90be6d..2f6f5cc 100644
--- a/src/PropertyInjector.cs
+++ b/src/PropertyInjector.cs
@@ -98,7 +98,7 @@ public static void SetPropertyValue(object target, string propertyPath, object v
propertyInfo.SetValue(currentObject, convertedValue);
}
- public static (object Value, Action