From 27490e4eb16304aaa5c0c1071ce020e4a207e72c Mon Sep 17 00:00:00 2001 From: Yuriy Durov Date: Tue, 23 Apr 2024 13:49:19 +0400 Subject: [PATCH] Add QueryStringParameterAttribute --- .../QueryStringParameterAttribute.cs | 27 +++++++++ .../BitzArt.Blazor.MVVM.csproj | 1 + .../Components/PageBase.cs | 18 +++++- .../QueryStringParameterExtensions.cs | 56 +++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/BitzArt.Blazor.MVVM/Attributes/QueryStringParameterAttribute.cs create mode 100644 src/BitzArt.Blazor.MVVM/Extensions/QueryStringParameterExtensions.cs diff --git a/src/BitzArt.Blazor.MVVM/Attributes/QueryStringParameterAttribute.cs b/src/BitzArt.Blazor.MVVM/Attributes/QueryStringParameterAttribute.cs new file mode 100644 index 0000000..bde9d4f --- /dev/null +++ b/src/BitzArt.Blazor.MVVM/Attributes/QueryStringParameterAttribute.cs @@ -0,0 +1,27 @@ +namespace BitzArt.Blazor.MVVM; + +/// +/// Attribute to decorate properties that should be read from the query string. +/// +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] +public sealed class QueryStringParameterAttribute : Attribute +{ + /// Name of the query string parameter. It uses the property name by default. + public string? Name { get; } + + /// + /// Initializes a new instance of the class. + /// + public QueryStringParameterAttribute() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + public QueryStringParameterAttribute(string name) + { + Name = name; + } +} diff --git a/src/BitzArt.Blazor.MVVM/BitzArt.Blazor.MVVM.csproj b/src/BitzArt.Blazor.MVVM/BitzArt.Blazor.MVVM.csproj index 37a00aa..526624b 100644 --- a/src/BitzArt.Blazor.MVVM/BitzArt.Blazor.MVVM.csproj +++ b/src/BitzArt.Blazor.MVVM/BitzArt.Blazor.MVVM.csproj @@ -28,6 +28,7 @@ + diff --git a/src/BitzArt.Blazor.MVVM/Components/PageBase.cs b/src/BitzArt.Blazor.MVVM/Components/PageBase.cs index a3b0777..b5299dc 100644 --- a/src/BitzArt.Blazor.MVVM/Components/PageBase.cs +++ b/src/BitzArt.Blazor.MVVM/Components/PageBase.cs @@ -3,7 +3,7 @@ namespace BitzArt.Blazor.MVVM; /// -/// Blazor component base class with view model support. +/// Blazor page base class with view model support. /// /// Type of this component's ViewModel public abstract class PageBase : ComponentBase, IPersistentComponent @@ -21,6 +21,12 @@ public abstract class PageBase : ComponentBase, IPersistentComponent [Inject] protected TViewModel ViewModel { get; set; } = null!; + /// + /// Navigation manager. + /// + [Inject] + protected NavigationManager NavigationManager { get; set; } = null!; + /// /// Method invoked when the component is ready to start, having received its initial /// parameters from its parent in the render tree. Override this method if you will @@ -45,4 +51,14 @@ void IPersistentComponent.StateHasChanged() { StateHasChanged(); } + + /// + /// Set the parameters from the query string. + /// + public override Task SetParametersAsync(ParameterView parameters) + { + ViewModel.SetParametersFromQueryString(NavigationManager); + + return base.SetParametersAsync(parameters); + } } diff --git a/src/BitzArt.Blazor.MVVM/Extensions/QueryStringParameterExtensions.cs b/src/BitzArt.Blazor.MVVM/Extensions/QueryStringParameterExtensions.cs new file mode 100644 index 0000000..79b02b4 --- /dev/null +++ b/src/BitzArt.Blazor.MVVM/Extensions/QueryStringParameterExtensions.cs @@ -0,0 +1,56 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Primitives; +using System.Globalization; +using System.Reflection; + +namespace BitzArt.Blazor.MVVM; + +internal static class QueryStringParameterExtensions +{ + // Apply the values from the query string to the current component + public static void SetParametersFromQueryString(this T component, NavigationManager navigationManager) + where T : class + { + if (!Uri.TryCreate(navigationManager.Uri, UriKind.RelativeOrAbsolute, out var uri)) + throw new InvalidOperationException("The current url is not a valid URI. Url: " + navigationManager.Uri); + + // Parse the query string + Dictionary queryString = QueryHelpers.ParseQuery(uri.Query); + + // Enumerate all properties of the component + foreach (var property in GetProperties()) + { + // Get the name of the parameter to read from the query string + var parameterName = GetQueryStringParameterName(property); + if (parameterName == null) + continue; // The property is not decorated by [QueryStringParameterAttribute] + + if (queryString.TryGetValue(parameterName, out var value)) + { + // Convert the value from string to the actual property type + var convertedValue = ConvertValue(value, property.PropertyType); + property.SetValue(component, convertedValue); + } + } + } + + private static object ConvertValue(StringValues value, Type type) + { + return Convert.ChangeType(value[0], type, CultureInfo.InvariantCulture)!; + } + + private static PropertyInfo[] GetProperties() + { + return typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + } + + private static string? GetQueryStringParameterName(PropertyInfo property) + { + var attribute = property.GetCustomAttribute(); + if (attribute == null) + return null; + + return attribute.Name ?? property.Name; + } +}