Allow providing any arguments to components
This commit is contained in:
Родитель
10f8ab28c5
Коммит
e90714251c
|
@ -55,7 +55,7 @@ namespace Microsoft.MobileBlazorBindings.Core
|
|||
/// <param name="parent"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IComponent> AddComponent(Type componentType, IElementHandler parent, Dictionary<string, string> parameters = null)
|
||||
public async Task<IComponent> AddComponent(Type componentType, IElementHandler parent, Dictionary<string, object> parameters = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ namespace Microsoft.MobileBlazorBindings.Core
|
|||
|
||||
_componentIdToAdapter[componentId] = rootAdapter;
|
||||
|
||||
SetNavigationParameters(component, parameters);
|
||||
SetParameterArguments(component, parameters);
|
||||
|
||||
await RenderRootComponentAsync(componentId).ConfigureAwait(false);
|
||||
return component;
|
||||
|
@ -146,110 +146,35 @@ namespace Microsoft.MobileBlazorBindings.Core
|
|||
return result;
|
||||
}
|
||||
|
||||
public static void SetNavigationParameters(IComponent component, Dictionary<string, string> parameters)
|
||||
internal static void SetParameterArguments(IComponent component, Dictionary<string, object> arguments)
|
||||
{
|
||||
if (component == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(component));
|
||||
}
|
||||
if (parameters == null || parameters.Count == 0)
|
||||
if (arguments == null || arguments.Count == 0)
|
||||
{
|
||||
//parameters will often be null. e.g. if you navigate with no parameters or when creating a root component.
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var parameter in parameters)
|
||||
foreach (var parameter in arguments)
|
||||
{
|
||||
var prop = component.GetType().GetProperty(parameter.Key);
|
||||
|
||||
if (prop != null)
|
||||
{
|
||||
var parameterAttribute = prop.GetCustomAttribute(typeof(ParameterAttribute));
|
||||
if (parameterAttribute == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Object of type '{component.GetType()}' has a property matching the name '{parameter.Key}', but it does not have [ParameterAttribute] or [CascadingParameterAttribute] applied.");
|
||||
}
|
||||
|
||||
if (TryParse(prop.PropertyType, parameter.Value, out var result))
|
||||
{
|
||||
prop.SetValue(component, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to set property {parameter.Key} on object of type '{component.GetType()}'.The value {parameter.Value}. can not be converted to a {prop.PropertyType.Name}");
|
||||
}
|
||||
}
|
||||
else
|
||||
if (prop == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Object of type '{component.GetType()}' does not have a property matching the name '{parameter.Key}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string into the specified type. If conversion was successful, parsed property will be of the correct type and method will return true.
|
||||
/// If conversion fails it will return false and parsed property will be null.
|
||||
/// This method supports the 8 data types that are valid navigation parameters in Blazor. Passing a string is also safe but will be returned as is because no conversion is neccessary.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="s"></param>
|
||||
/// <param name="result">The parsed object of the type specified. This will be null if conversion failed.</param>
|
||||
/// <returns>True if s was converted successfully, otherwise false</returns>
|
||||
public static bool TryParse(Type type, string s, out object result)
|
||||
{
|
||||
bool success;
|
||||
var parameterAttribute = prop.GetCustomAttribute(typeof(ParameterAttribute));
|
||||
if (parameterAttribute == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Object of type '{component.GetType()}' has a property matching the name '{parameter.Key}', but it does not have [ParameterAttribute] or [CascadingParameterAttribute] applied.");
|
||||
}
|
||||
|
||||
if (type == typeof(string))
|
||||
{
|
||||
result = s;
|
||||
success = true;
|
||||
prop.SetValue(component, parameter.Value);
|
||||
}
|
||||
else if (type == typeof(int))
|
||||
{
|
||||
success = int.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(Guid))
|
||||
{
|
||||
success = Guid.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
{
|
||||
success = bool.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(DateTime))
|
||||
{
|
||||
success = DateTime.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(decimal))
|
||||
{
|
||||
success = decimal.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(double))
|
||||
{
|
||||
success = double.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(float))
|
||||
{
|
||||
success = float.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(long))
|
||||
{
|
||||
success = long.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = null;
|
||||
success = false;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Microsoft.MobileBlazorBindings
|
|||
//This version also allows an optional set of parameters
|
||||
//The only downside is you can't have design/compiletime type safety
|
||||
//There's a lot of duplicate code between the two, can probably refactor the core of the method into a separate method that they both call
|
||||
public static async Task<IComponent> AddComponent(this IServiceProvider services, XF.Element parent, Type type, System.Collections.Generic.Dictionary<string, string> parameters = null)
|
||||
public static async Task<IComponent> AddComponent(this IServiceProvider services, XF.Element parent, Type type, System.Collections.Generic.Dictionary<string, object> parameters = null)
|
||||
{
|
||||
if (services is null)
|
||||
{
|
||||
|
|
|
@ -108,7 +108,8 @@ namespace Microsoft.MobileBlazorBindings
|
|||
var renderer = new MobileBlazorBindingsRenderer(_services, _services.GetRequiredService<ILoggerFactory>());
|
||||
#pragma warning restore CA2000 // Dispose objects before losing scope
|
||||
|
||||
var addComponentTask = renderer.AddComponent(componentType, container, route.Parameters);
|
||||
var convertedParameters = ConvertParameters(componentType, route.Parameters);
|
||||
var addComponentTask = renderer.AddComponent(componentType, container, convertedParameters);
|
||||
var elementAddedTask = container.WaitForElementAsync();
|
||||
|
||||
await Task.WhenAny(addComponentTask, elementAddedTask).ConfigureAwait(false);
|
||||
|
@ -147,5 +148,96 @@ namespace Microsoft.MobileBlazorBindings
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static Dictionary<string, object> ConvertParameters(Type componentType, Dictionary<string, string> parameters)
|
||||
{
|
||||
if (parameters is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var convertedParameters = new Dictionary<string, object>();
|
||||
|
||||
foreach (var keyValue in parameters)
|
||||
{
|
||||
var propertyType = componentType.GetProperty(keyValue.Key)?.PropertyType ?? typeof(string);
|
||||
if (!TryParse(propertyType, keyValue.Value, out var parsedValue))
|
||||
{
|
||||
throw new InvalidOperationException($"The value {keyValue.Value} can not be converted to a {propertyType.Name}");
|
||||
}
|
||||
|
||||
convertedParameters[keyValue.Key] = parsedValue;
|
||||
}
|
||||
|
||||
return convertedParameters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string into the specified type. If conversion was successful, parsed property will be of the correct type and method will return true.
|
||||
/// If conversion fails it will return false and parsed property will be null.
|
||||
/// This method supports the 8 data types that are valid navigation parameters in Blazor. Passing a string is also safe but will be returned as is because no conversion is neccessary.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="s"></param>
|
||||
/// <param name="result">The parsed object of the type specified. This will be null if conversion failed.</param>
|
||||
/// <returns>True if s was converted successfully, otherwise false</returns>
|
||||
internal static bool TryParse(Type type, string s, out object result)
|
||||
{
|
||||
bool success;
|
||||
|
||||
type = Nullable.GetUnderlyingType(type) ?? type;
|
||||
|
||||
if (type == typeof(string))
|
||||
{
|
||||
result = s;
|
||||
success = true;
|
||||
}
|
||||
else if (type == typeof(int))
|
||||
{
|
||||
success = int.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(Guid))
|
||||
{
|
||||
success = Guid.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
{
|
||||
success = bool.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(DateTime))
|
||||
{
|
||||
success = DateTime.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(decimal))
|
||||
{
|
||||
success = decimal.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(double))
|
||||
{
|
||||
success = double.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(float))
|
||||
{
|
||||
success = float.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else if (type == typeof(long))
|
||||
{
|
||||
success = long.TryParse(s, out var parsed);
|
||||
result = parsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = null;
|
||||
success = false;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче