-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add Atc.Blazor base package and added some extensions methods f…
…or handling QueryString parameters / bindings
- Loading branch information
1 parent
6a06b16
commit 9c7d2eb
Showing
13 changed files
with
391 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<PackageId>Atc.Blazor</PackageId> | ||
<PackageTags>blazor</PackageTags> | ||
<Description>Atc.Blazor is a collection of classes and extension methods for common functionality.</Description> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Atc" Version="2.0.108" /> | ||
<PackageReference Include="Microsoft.AspNetCore.Components" Version="6.0.2" /> | ||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
21 changes: 21 additions & 0 deletions
21
src/Atc.Blazor/Attributes/QueryStringParameterAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// ReSharper disable once CheckNamespace | ||
namespace Atc; | ||
|
||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] | ||
public sealed class QueryStringParameterAttribute : Attribute | ||
{ | ||
public QueryStringParameterAttribute() | ||
{ | ||
Name = string.Empty; | ||
} | ||
|
||
public QueryStringParameterAttribute(string name) | ||
{ | ||
Name = name; | ||
} | ||
|
||
/// <summary> | ||
/// Name of the query string parameter. It uses the property name by default. | ||
/// </summary> | ||
public string Name { get; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// ReSharper disable once CheckNamespace | ||
// ReSharper disable ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator | ||
// ReSharper disable SuggestBaseTypeForParameter | ||
namespace Atc; | ||
|
||
public static class ComponentBaseExtensions | ||
{ | ||
public static void SetPropertiesWithDecoratedQueryStringParameterFromQueryString<T>(this T component, NavigationManager navigationManager) | ||
where T : ComponentBase | ||
{ | ||
ArgumentNullException.ThrowIfNull(navigationManager); | ||
|
||
if (!Uri.TryCreate(navigationManager.Uri, UriKind.RelativeOrAbsolute, out var uri)) | ||
{ | ||
throw new InvalidOperationException("The current url is not a valid URI. Url: " + navigationManager.Uri); | ||
} | ||
|
||
var queryString = QueryHelpers.ParseQuery(uri.Query); | ||
foreach (var property in GetPublicAndNonPublicProperties<T>()) | ||
{ | ||
var parameterName = GetQueryStringParameterName(property); | ||
if (parameterName is null) | ||
{ | ||
continue; | ||
} | ||
|
||
if (!queryString.TryGetValue(parameterName, out var value)) | ||
{ | ||
continue; | ||
} | ||
|
||
var convertedValue = Convert.ChangeType(value[0], property.PropertyType, CultureInfo.InvariantCulture); | ||
property.SetValue(component, convertedValue); | ||
} | ||
} | ||
|
||
public static void UpdateQueryStringFromPropertiesWithDecoratedQueryStringParameter<T>(this T component, NavigationManager navigationManager) | ||
where T : ComponentBase | ||
{ | ||
ArgumentNullException.ThrowIfNull(navigationManager); | ||
|
||
if (!Uri.TryCreate(navigationManager.Uri, UriKind.RelativeOrAbsolute, out var uri)) | ||
{ | ||
throw new InvalidOperationException("The current url is not a valid URI. Url: " + navigationManager.Uri); | ||
} | ||
|
||
var parameters = QueryHelpers.ParseQuery(uri.Query); | ||
foreach (var property in GetPublicAndNonPublicProperties<T>()) | ||
{ | ||
var parameterName = GetQueryStringParameterName(property); | ||
if (parameterName is null) | ||
{ | ||
continue; | ||
} | ||
|
||
var value = property.GetValue(component); | ||
if (value is null) | ||
{ | ||
parameters.Remove(parameterName); | ||
} | ||
else | ||
{ | ||
var convertedValue = Convert.ToString(value, CultureInfo.InvariantCulture); | ||
parameters[parameterName] = convertedValue; | ||
} | ||
} | ||
|
||
var newUri = uri.GetComponents(UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path, UriFormat.UriEscaped); | ||
foreach (var (key, stringValues) in parameters) | ||
{ | ||
foreach (var value in stringValues) | ||
{ | ||
newUri = QueryHelpers.AddQueryString(newUri, key, value); | ||
} | ||
} | ||
|
||
navigationManager.NavigateTo(newUri); | ||
} | ||
|
||
[SuppressMessage("Major Code Smell", "S3011:Reflection should not be used to increase accessibility of classes, methods, or fields", Justification = "OK - By design.")] | ||
private static IEnumerable<PropertyInfo> GetPublicAndNonPublicProperties<T>() | ||
=> typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); | ||
|
||
private static string? GetQueryStringParameterName(PropertyInfo propertyInfo) | ||
{ | ||
var attribute = propertyInfo.GetCustomAttribute<QueryStringParameterAttribute>(); | ||
if (attribute is null) | ||
{ | ||
return null; | ||
} | ||
|
||
return !string.IsNullOrEmpty(attribute.Name) | ||
? attribute.Name | ||
: propertyInfo.Name; | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
src/Atc.Blazor/Extensions/NavigationManagerExtendedExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// ReSharper disable once CheckNamespace | ||
// ReSharper disable ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator | ||
namespace Atc; | ||
|
||
public static class NavigationManagerExtendedExtensions | ||
{ | ||
public static bool TryGetQueryString<T>(this NavigationManager navigationManager, string key, out T value) | ||
{ | ||
ArgumentNullException.ThrowIfNull(navigationManager); | ||
|
||
var uri = navigationManager.ToAbsoluteUri(navigationManager.Uri); | ||
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue(key, out var valueFromQueryString)) | ||
{ | ||
if (typeof(T) == typeof(bool) && | ||
bool.TryParse(valueFromQueryString, out var valueAsBool)) | ||
{ | ||
value = (T)(object)valueAsBool; | ||
return true; | ||
} | ||
|
||
if (typeof(T) == typeof(decimal) && | ||
decimal.TryParse(valueFromQueryString, NumberStyles.Any, CultureInfo.InvariantCulture, out var valueAsDecimal)) | ||
{ | ||
value = (T)(object)valueAsDecimal; | ||
return true; | ||
} | ||
|
||
if (typeof(T) == typeof(double) && | ||
double.TryParse(valueFromQueryString, NumberStyles.Any, CultureInfo.InvariantCulture, out var valueAsDouble)) | ||
{ | ||
value = (T)(object)valueAsDouble; | ||
return true; | ||
} | ||
|
||
if (typeof(T) == typeof(int) && | ||
int.TryParse(valueFromQueryString, NumberStyles.Any, CultureInfo.InvariantCulture, out var valueAsInt)) | ||
{ | ||
value = (T)(object)valueAsInt; | ||
return true; | ||
} | ||
|
||
if (typeof(T) == typeof(Guid) && | ||
Guid.TryParse(valueFromQueryString, out var valueAsGuid)) | ||
{ | ||
value = (T)(object)valueAsGuid; | ||
return true; | ||
} | ||
|
||
if (typeof(T) == typeof(string)) | ||
{ | ||
value = (T)(object)valueFromQueryString.ToString(); | ||
return true; | ||
} | ||
} | ||
|
||
value = default!; | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
global using System.Diagnostics.CodeAnalysis; | ||
global using System.Globalization; | ||
global using System.Reflection; | ||
global using Microsoft.AspNetCore.Components; | ||
global using Microsoft.AspNetCore.WebUtilities; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<IsPackable>false</IsPackable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" /> | ||
<PackageReference Include="FluentAssertions" Version="6.5.1" /> | ||
<PackageReference Include="Microsoft.AspNetCore.Components" Version="6.0.2" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" /> | ||
<PackageReference Include="xunit" Version="2.4.1" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="coverlet.collector" Version="3.1.2"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Atc.Blazor\Atc.Blazor.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Oops, something went wrong.