Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to pass additional parameters to Shell navigation #457

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions samples/ControlGallery/Models/NavigationParameterModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

namespace ControlGallery.Models
{
public class NavigationParameterModel
{
public string Name { get; set; }
public string Value { get; set; }
}
}
8 changes: 8 additions & 0 deletions samples/ControlGallery/Views/NavigationSource.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@page "/navigation"
@using ControlGallery.Models
@inject ShellNavigationManager NavigationManager

<ContentPage>
Expand All @@ -15,6 +16,7 @@
<Button Text="Navigate with Guid" OnClick="NavigateWithGuid"></Button>
<Button Text="Navigate with Nullable<Guid>" OnClick="NavigateWithNullableGuid"></Button>
<Button Text="Navigate with Bool" OnClick="NavigateWithBool"></Button>
<Button Text="Navigate with additional parameters" OnClick="NavigateWithAdditionalParameters"></Button>
</StackLayout>
</ScrollView>
</ContentPage>
Expand All @@ -32,4 +34,10 @@
async Task NavigateWithGuid() => await NavigationManager.NavigateToAsync($"/guid/{Guid.NewGuid()}");
async Task NavigateWithNullableGuid() => await NavigationManager.NavigateToAsync($"/nullable-guid/{Guid.NewGuid()}");
async Task NavigateWithBool() => await NavigationManager.NavigateToAsync("/bool/true");

async Task NavigateWithAdditionalParameters() => await NavigationManager.NavigateToAsync("/target", new Dictionary<string, object>
{
["Query1"] = new NavigationParameterModel { Name = "Q1", Value = "V1" },
["Query2"] = new NavigationParameterModel { Name = "Q2", Value = null }
});
}
7 changes: 5 additions & 2 deletions samples/ControlGallery/Views/NavigationTarget.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
@page "/guid/{Guid}"
@page "/nullable-guid/{NullableGuid}"
@page "/bool/{Bool}"
@using ControlGallery.Models

<ContentPage>
<StackLayout>
Expand All @@ -26,6 +27,8 @@
<Label Text="@($"Guid = {Guid}")"></Label>
<Label Text="@($"Guid? = {NullableGuid}")"></Label>
<Label Text="@($"Bool = {Bool}")"></Label>
<Label Text="@($"Query1 = Name:{Query1?.Name}, Value:{Query1?.Value}")"></Label>
<Label Text="@($"Query2 = Name:{Query2?.Name}, Value:{Query2?.Value}")"></Label>
</StackLayout>
</ContentPage>

Expand All @@ -41,6 +44,6 @@
[Parameter] public Guid Guid { get; set; }
[Parameter] public Guid? NullableGuid { get; set; }
[Parameter] public bool Bool { get; set; }


[Parameter] public NavigationParameterModel Query1 { get; set; }
[Parameter] public NavigationParameterModel Query2 { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace Microsoft.MobileBlazorBindings.ShellNavigation
{
//Based on the forms TypeRouteFactory https://github.com/xamarin/Xamarin.Forms/blob/9fd882e6c598a51bffbbb2f4de72c3bd9023ab41/Xamarin.Forms.Core/Routing.cs
public class MBBRouteFactory : MC.RouteFactory
internal class MBBRouteFactory : MC.RouteFactory
{
private readonly Type _componentType;
private readonly ShellNavigationManager _navigationManager;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using MC = Microsoft.Maui.Controls;

Expand Down Expand Up @@ -65,22 +66,22 @@ private void FindRoutes()
}

#pragma warning disable CA1054 // Uri parameters should not be strings
public void NavigateTo(string uri)
public void NavigateTo(string uri, Dictionary<string, object> parameters = null)
#pragma warning restore CA1054 // Uri parameters should not be strings
{
_ = NavigateToAsync(uri);
_ = NavigateToAsync(uri, parameters);
}

#pragma warning disable CA1054 // Uri parameters should not be strings
public async Task NavigateToAsync(string uri)
public async Task NavigateToAsync(string uri, Dictionary<string, object> parameters = null)
#pragma warning restore CA1054 // Uri parameters should not be strings
{
if (uri is null)
{
throw new ArgumentNullException(nameof(uri));
}

var route = StructuredRoute.FindBestMatch(uri, Routes);
var route = StructuredRoute.FindBestMatch(uri, Routes, parameters);

if (route != null)
{
Expand Down Expand Up @@ -110,12 +111,35 @@ public async Task NavigateToAsync(string uri)

var renderer = serviceProvider.GetRequiredService<MobileBlazorBindingsRenderer>();

var convertedParameters = ConvertParameters(componentType, route.Parameters);
var addComponentTask = renderer.AddComponent(componentType, container, convertedParameters);
var parameters = ConvertParameters(componentType, route.PathParameters);

if (route.AdditionalParameters is not null)
{
if (parameters is null)
{
parameters = route.AdditionalParameters;
}
else
{
foreach (var (key, value) in route.AdditionalParameters)
{
parameters.Add(key, value);
}
}
}

var addComponentTask = renderer.AddComponent(componentType, container, parameters);
var elementAddedTask = container.WaitForElementAsync();

await Task.WhenAny(addComponentTask, elementAddedTask).ConfigureAwait(false);

if (addComponentTask.Exception != null)
{
// If any exception ecountered during the rendering - throw it directly instead of wrapping in another exception.
var exception = addComponentTask.Exception.InnerException;
ExceptionDispatchInfo.Throw(exception);
}

if (container.Elements.Count != 1)
{
throw new InvalidOperationException("The target component of a Shell navigation must have exactly one root element.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace Microsoft.MobileBlazorBindings.ShellNavigation
{
//Used to map blazor route syntax to forms query syntax
public class StructuredRoute
internal class StructuredRoute
{
#pragma warning disable CA1056 // Uri properties should not be strings
public string OriginalUri { get; }//Full route as it is registered in the razor component
Expand Down Expand Up @@ -41,12 +41,12 @@ public StructuredRoute(string originalRoute, Type type)
}
}

internal static StructuredRouteResult FindBestMatch(string uri, List<StructuredRoute> routes)
internal static StructuredRouteResult FindBestMatch(string uri, List<StructuredRoute> routes, Dictionary<string, object> additionalParameters)
{
var match = routes.FirstOrDefault(x => x.BaseUri == uri);
if (match != null && match.ParameterCount == 0)
{
return new StructuredRouteResult(match);
return new StructuredRouteResult(match, additionalParameters: additionalParameters);
}

var pieces = uri.Split('/').ToList();
Expand All @@ -71,27 +71,31 @@ internal static StructuredRouteResult FindBestMatch(string uri, List<StructuredR
}

parameters.Reverse();
return new StructuredRouteResult(match, parameters);
return new StructuredRouteResult(match, parameters, additionalParameters);
}
}

public class StructuredRouteResult
internal class StructuredRouteResult
{
public StructuredRoute Route { get; }
public Dictionary<string, string> Parameters { get; } = new Dictionary<string, string>();
public Dictionary<string, string> PathParameters { get; } = new Dictionary<string, string>();
public Dictionary<string, object> AdditionalParameters { get; }

public StructuredRouteResult(StructuredRoute match)
{
Route = match;
}

public StructuredRouteResult(StructuredRoute match, List<string> parameters)
public StructuredRouteResult(StructuredRoute match,
List<string> parameters = null,
Dictionary<string, object> additionalParameters = null)
{
Route = match ?? throw new ArgumentNullException(nameof(match));
for (var i = 0; i < match.ParameterKeys.Count; i++)

if (parameters is not null)
{
Parameters[match.ParameterKeys[i]] = parameters.ElementAtOrDefault(i);
for (var i = 0; i < match.ParameterKeys.Count; i++)
{
PathParameters[match.ParameterKeys[i]] = parameters.ElementAtOrDefault(i);
}
}

AdditionalParameters = additionalParameters;
}
}
}