0

I have code similar to the following:

@page "/somepage/{Id:guid}/add"
@page "/somepage/{Id:guid}/remove"

@inject NavigationManager navigationManager
@implements IAsyncDisposable

if ( @remove ) {
    <div>Remove!</div>
} else {
    <div>Add!</div>
}

@code{
    [Parameter]
    public Guid Id { get; set; }

    protected override void OnInitialized()
    {
        navigationManager.LocationChanged += HandleLocationChanged;
    }

    public async ValueTask DisposeAsync()
    {
        navigationManager.LocationChanged -= HandleLocationChanged;
    }

    protected override async Task OnParametersSetAsync()
    {
        remove = navigationManager.Uri.Contains("remove");

        // Load some data...

        await base.OnParametersSetAsync();
    }

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
    {
        var pageUris = new[] {
            $"/somepage/{Id}/add",
            $"/somepage/{Id}/remove"
        };

        var uri = navigationManager.Uri.Replace( navigationManager.BaseUri, "" );

        if ( !pageUris.Contains(uri) ) return;

        remove = navigationManager.Uri.Contains("remove");

        // Load some data...

        StateHasChanged();
    }
}

In the HandleLocationChanged event, I realised that if the user clicks on a link to a completely different page, then data will be loaded for the wrong page. So I added a guard to check NavigationManager.Uri - to see if it points to a different location.

Is there some way to make this more generic? For example, can I get a list of routes for a particular page and then use that in my comparison?

Mitkins
  • 4,031
  • 3
  • 40
  • 77
  • If you look at [this answer](https://stackoverflow.com/a/60073500/189950) you'll see that the `@page` directive translates into an attribute defined on your page class, such as: `@attribute [Microsoft.AspNetCore.Components.RouteAttribute("/somepage/add")]` From there, it's just a matter of using a light amount of reflection to interrogate the attributes defined on the page and pull out the urls from each one defined. – Kirk Woll Apr 11 '22 at 13:23
  • I'm not sure what you are trying to do. If navigation is to the same "Page" i.e. the same component what do you want to happen? If to another Page then what? – MrC aka Shaun Curtis Apr 11 '22 at 21:34
  • In the real page I have an "edit name" feature. If you click the edit button, then it changes the route. I then use the route to display an editable form instead of readonly text. To achieve this I need the LocationChanged event - but only when it's navigating to the same page/component – Mitkins Apr 11 '22 at 23:45
  • Oh, and the main reason for all of this is that I want to be able to refresh the page and it stays in "edit name" mode – Mitkins Apr 11 '22 at 23:48

1 Answers1

0

Your question asks how to replace your hardcoded pageUris variable:

var pageUris = new[] {
    $"{navigationManager.BaseUri}somepage/add",
    $"{navigationManager.BaseUri}somepage/remove"
};

Knowing that @page "/foo" results in a [RouteAttribute("foo")] being applied to your page's generated class, it's straightforward to create a function that will replace your code:

private string[] GetPageUrls()
{
    return GetType().GetCustomAttributes<RouteAttribute>().Select(x => x.Template).ToArray();
}

This will produce one element for each @page directive you have, containing the values:

"/somepage/add", "/somepage/remove"

In your question, you combine it with navigationManager.BaseUri which I believe should not be necessary, since by default a Blazor app sets the base url (i.e. <base href="/" />), but if you want that returned, it's obviously trivial to change GetPageUrls to incorporate that as well.

Finally, if you want to use this elsewhere, it should be straightforward to convert this into an extension method.

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • Maybe I should just remove `navigationManager.BaseUri` from `navigationManager.Uri` instead - I only included it so the comparison would work. Because `navigationManager.Uri` will always return the absolute url? – Mitkins Apr 13 '22 at 00:23
  • In my app. Template ends up being something like `/p/{PackageId:guid}/edit` - which means that I'll have to do some substitution to make the comparison work properly. Which would be particular to each page. Is there a generic way I could do this do you think? – Mitkins Apr 13 '22 at 00:25
  • I've adjusted the original post to reflect the above questions – Mitkins Apr 13 '22 at 00:35
  • 1
    @Mitkins I'm pretty sure you can entirely omit the `navigationManager` business, as relative Uris should work perfectly well. As for the substitutions, that's a bit more complicated, but it's a fair detail for you to add. If I were in your position, I'd simply create a method that parses the string and substitutes the values yourself. Regular expressions should work fine for that task. For example, `(\{(.*)\})`. But if you really want to go the full mile, I'd probably create a class that handles this more parsimoniously where you parse the string and convert it into a type that handles it – Kirk Woll Apr 13 '22 at 01:15
  • I'm not sure what you mean about `navigationManager`? `navigationManager.Uri` or `e.Location` both have an absolute url. If I'm going to do a `.Equals` comparison or a `string.StartsWith` - then I need to either prepend or sunbstitute the `BaseUri`? Or am I missing something? – Mitkins Apr 13 '22 at 06:44
  • I think this is the best possible answer. So the next step would be to decide what I want to do with regards to parameter substitution. But I'm kind of surprised that there isn't any url/page helpers built-in to Blazor/Asp.Net Core (I think I've been spoilt by the Phoenix framework) – Mitkins Apr 13 '22 at 07:09
  • @Mitkins, I've been working on a related open source project (still in progress), but you may derive some inspiration from my [`RoutePath` class](https://github.com/kswoll/DirectRoute/blob/master/DirectRoute/RoutePath.cs). The `FormatUrl` method should do what you want if you provide the constructor the route you got from the route attribute. Also, good point about the navigation manager; I forgot you were ultimately comparing it to an absolute URL. – Kirk Woll Apr 15 '22 at 13:20