Skip to content
This repository has been archived by the owner on Nov 1, 2024. It is now read-only.

Commit

Permalink
Add QueryStringParameterAttribute
Browse files Browse the repository at this point in the history
  • Loading branch information
YuriyDurov committed Apr 23, 2024
1 parent 991719f commit 27490e4
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace BitzArt.Blazor.MVVM;

/// <summary>
/// Attribute to decorate properties that should be read from the query string.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class QueryStringParameterAttribute : Attribute
{
/// <summary>Name of the query string parameter. It uses the property name by default.</summary>
public string? Name { get; }

/// <summary>
/// Initializes a new instance of the <see cref="QueryStringParameterAttribute"/> class.
/// </summary>
public QueryStringParameterAttribute()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="QueryStringParameterAttribute"/> class.
/// </summary>
/// <param name="name"></param>
public QueryStringParameterAttribute(string name)
{
Name = name;
}
}
1 change: 1 addition & 0 deletions src/BitzArt.Blazor.MVVM/BitzArt.Blazor.MVVM.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.*" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.*" />
</ItemGroup>

</Project>
18 changes: 17 additions & 1 deletion src/BitzArt.Blazor.MVVM/Components/PageBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace BitzArt.Blazor.MVVM;

/// <summary>
/// Blazor component base class with view model support.
/// Blazor page base class with view model support.
/// </summary>
/// <typeparam name="TViewModel">Type of this component's ViewModel</typeparam>
public abstract class PageBase<TViewModel> : ComponentBase, IPersistentComponent
Expand All @@ -21,6 +21,12 @@ public abstract class PageBase<TViewModel> : ComponentBase, IPersistentComponent
[Inject]
protected TViewModel ViewModel { get; set; } = null!;

/// <summary>
/// Navigation manager.
/// </summary>
[Inject]
protected NavigationManager NavigationManager { get; set; } = null!;

/// <summary>
/// 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
Expand All @@ -45,4 +51,14 @@ void IPersistentComponent.StateHasChanged()
{
StateHasChanged();
}

/// <summary>
/// Set the parameters from the query string.
/// </summary>
public override Task SetParametersAsync(ParameterView parameters)
{
ViewModel.SetParametersFromQueryString(NavigationManager);

return base.SetParametersAsync(parameters);
}
}
Original file line number Diff line number Diff line change
@@ -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<T>(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<string, StringValues> queryString = QueryHelpers.ParseQuery(uri.Query);

// Enumerate all properties of the component
foreach (var property in GetProperties<T>())
{
// 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<T>()
{
return typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
}

private static string? GetQueryStringParameterName(PropertyInfo property)
{
var attribute = property.GetCustomAttribute<QueryStringParameterAttribute>();
if (attribute == null)
return null;

return attribute.Name ?? property.Name;
}
}

0 comments on commit 27490e4

Please sign in to comment.