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

Support @page with custom route template on components #16436

Closed
danroth27 opened this issue Mar 8, 2018 · 13 comments
Closed

Support @page with custom route template on components #16436

danroth27 opened this issue Mar 8, 2018 · 13 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components

Comments

@danroth27
Copy link
Member

You should be able to specify the @page on a component with a custom route template to make that component routable with parameters, similar to what is supported in Razor Pages.

@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented Mar 12, 2018

Proposed design

Each IComponent can optionally declare one or more class attributes of type Microsoft.AspNetCore.Blazor.Routing.RouteAttribute, e.g.,

[Route("/customers")]
[Route("/customers/{customerId}")]
public CustomerInfo : IComponent
{
    ...
}

@rynowak is working on adding a Razor directive for this, so that @page "something" compiles as [Route("something")] on the generated class.

Route template string formats

For now, we don't need to support all Microsoft.AspNetCore.Routing-supported route template syntaxes. For example, we don't necessarily need {id:int} (though it would be nice, eventually, to avoid the developer having to call int.Parse manually). Similarly we don't need mixed segments like token{paramname}.

For 0.1.0 it's sufficient if the only syntax we support is slash-separated segments of the form stringliteral and {paramname}. All route template strings should start with / to avoid developer confusion, even though the application itself might be mounted on a non-root URL path (the existing router code already accounts for this).

Runtime behavior

The existing Router component already contains a method, GetComponentTypeForPath, that's responsible for route matching. We need to change this so that;

  • It no longer uses PagesNamespace (and in fact we can remove that whole property)
  • Instead, it considers each of the IComponent types in AppAssembly, and returns the first one with a matching RouteAttribute.
    • Eventually we might want to build a Microsoft.AspNetCore.Routing-style suffix tree to make this into an O(log N) lookup, but that's unnecessary at the moment. For now it's preferable to keep it simple and just do a simple O(N) linear search.
  • To supply the matched parameters to the component, have Route pass an IDictionary<string, object> containing the params to the LayoutDisplay that it renders. In turn, the LayoutDisplay can iterate through that dictionary and render attribute frames for each of the matched params on the actual page component that it renders. By default, this will result in the component itself getting the route values assigned to public properties of the same name, just the same as if they were passed as <MyComponent SomeProperty="somevalue" />.
    • Note: this only works if the component declares its route param properties with type string, since that's what we extract from the URL. If we wanted to support props of type int, etc., then we'd need to support route tokens like {customerId:int}.
  • Update the samples and templates so they have @page directives on their page components so they keep working.

That's it for 0.1.0. We can consider more advanced routing features in the future, but I think this covers the key scenarios while being conceptually quite self-contained.

@danroth27 Do you agree it's OK to require all page components to declare @page "/myurl" at the top, or do you think we also need some convention where all the cshtml files in a certain directory are assumed to implicitly have @page "/my/path/in/that/directory"?

@rynowak
Copy link
Member

rynowak commented Mar 12, 2018

All route template strings should start with / to avoid developer confusion, even though the application itself might be mounted on a non-root URL path (the existing router code already accounts for this).

I'm MVC you're not required to use the leading slash because it means something different, it's also a little more complicated.

Examples:

@* in /Index.cshtml *@
@page 

<p>I match /</p>
@* in /Admin/Index.cshtml *@
@page 

<p>I match /Admin</p>
@* in /Customer.cshtml *@
@page "{id}"

<p>I match /Customer/43</p>

For now I'm going to go with just requiring the leading slash. Everything else builds upon that.

@SteveSandersonMS
Copy link
Member

@rynowak Dan and I spoke with @javiercn about him implementing the runtime part of this, with you having already said you'd do the compile-time piece generating the [Route(...)] attributes.

If you were interested in doing the runtime part as well could you communicate with @javiercn about that? Otherwise you might both be working on it :)

@rynowak
Copy link
Member

rynowak commented Mar 12, 2018

ok cool beans.

@rynowak
Copy link
Member

rynowak commented Mar 12, 2018

dotnet/blazor#244

@rynowak
Copy link
Member

rynowak commented Mar 13, 2018

The language support for this is in.

@javiercn
Copy link
Member

Instead, it considers each of the IComponent types in AppAssembly, and returns the first one with a matching RouteAttribute

@rynowak It would be nice if we expose the components in the assembly through an assembly level attribute in a similar fashion to how pages get discovered.

@rynowak
Copy link
Member

rynowak commented Mar 13, 2018

Oh oops, yeah I forgot to add that. I'll do the needful.

@SteveSandersonMS
Copy link
Member

Does the assembly-level attribute help though? You'd still have to scan for other types that have [Route], since people don't have to author components using Razor.

@rstropek
Copy link

Today, we are using Angular quite a lot to build reusable components (each component being used in a hand full of customer projects; I have seen similar practices at many ISVs). These components have different routes in different end products. Angular enables that by separating routing from component metadata. Maybe this is a use-case that you could consider for Blazor, too.

Another important routing feature of Angular that we regularly use are route guards. We implement functions that are called when a route is activated. The function e.g. checks whether the navigation is possible (e.g. permission check). If it is not, we can redirect the user to a different route (e.g. proper error message). Another use-case for such functions is logging (e.g. write usage log based on navigation between routes). It would be nice to have similar functionality at some time in Blazor, too.

@rynowak
Copy link
Member

rynowak commented Mar 14, 2018

Yeah, that's a good point. We can't rely on this, so we might as well not do it. We'll have to scan for types regardless. I think I had the same thought during dotnet/blazor#244 which lead to me leaving it out. In my addled state of mind I've become very suggestible.

@javiercn
Copy link
Member

Fair enough

@guardrex
Copy link
Contributor

@javiercn Optional route parameters (e.g., /MyComponentPath/{optionalParam?}) aren't supported at this time, correct? I'll work around it with ...

@page /MyComponentPath
@page /MyComponentPath/{optionalParam}

SteveSandersonMS referenced this issue in SteveSandersonMS/BlazorMigration Nov 27, 2018
* Updates the router component to scan for components within assemblies.
* Parses the templates on `[Route]` in component instances and builds a
  route table that maps paths to components.
* Uses the route table to map paths to components.
@mkArtakMSFT mkArtakMSFT transferred this issue from dotnet/blazor Oct 27, 2019
@mkArtakMSFT mkArtakMSFT added the area-blazor Includes: Blazor, Razor Components label Oct 27, 2019
@ghost ghost locked as resolved and limited conversation to collaborators Dec 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

No branches or pull requests

7 participants