Blazor DEEP LINKING Support: - seeking suggestions or interest.
I have discovered that Blazor "Deep Linking" is something with not much .NET API support and community code blogs.
ShortComing:
This solution does not use the LocationChanged Event. Blazors NavigationManager LocationChanged Event is not friendly enough to support deep linking URL's that are not managed under a Blazor "Circuit". eg an initial request by clicking a browser history/favorite link.
KeyNote:
So far, the findings indicate that the LocationChanged Event will not fire for this type of initial web request with subsequent requests managed in a Blazor "Circuit". It's too late because the first request was not caught by the LocationChanged Event.
Obtained GOAL:
Supports detailed "deep linking" to Restore Navigation UI Components and also restore workflow state for a page defined in the URL route.
This is a Blazor Server implementation to support Blazor "Deep Linking".
The code base has been refined, good comments and well ordered.; looking production ready.
Blazor Server was chosen to reduce the security attack surface. The application using this type solution is not ultra high bandwidth so adding a few extra micro-seconds (or even a milli-second) of code runtime is not an issue.
>>> Before scaling out adding application page-specific content, now seeking alternatives, insight and suggestions (yeah, should have asked sooner)
>>> Ideally, I should have created a few block diagrams and code fragments to help visualize. (perhaps support files can be added if further interest.)
Primary Application Components:
The MainLayout Component supports four primary Blazor Components:
1) "Module List": in the upper title area, a TelerikBreadcrumb Component showing a list of "Module Items" to click on.
2) "Module Item": a TelerikTreeView Component containing a navigational hierarchical list of TreeItems located along the left side of the page. Each TreeItem contains url route segments to a Blazor Page Component. Each item contains properties for customization.
3) "NavBreadcrumb": a TelerikBreadcrumb Component showing the clicked selectedTreeView item, and its parent items. Located just above the Module Item on the left side MainLayout Component.
4) "Page": the Page Component in the selected TreeItem from the selected ModuleItem. KeyNote: in this solution, a page may be under multiple module list and module items. Thus the format of the URL route as defined below.
URL Route Schema:
The following is the overall Route Template scheme for the website solution:
/ ModuleListName / ModuleItemName / PageName / optionalRouteParam1 / optionalRouteParam2 ? value1=xxx & value2=yyy
In each Page Component, the @Page Directive Is Not used. Instead, @attribute [Route("route template")] is used in all page type components. This allows the use of string constants. @Page only allows string literals.
Here are two example route templates in one of the Page Components: (each page may contain multiple routes)
@attribute [Route($"/{ModuleListName.Tenant}/{ModuleItemName.ClientAdmin}/{PageItem.UserGrid}/{PageAction.Manage}")]
@attribute [Route($"/{ModuleListName.Tenant}/{ModuleItemName.UserAdmin}/{PageItem.UserGrid}/{PageAction.Search}")]
Naming Constants and Dictionary:
In the above example, four static classes with multiple static string constants are used to define all navigation type lists and items, and the routing/query string values:
The four static classes are:
ModuleList - A list of possible Module lists to display in the MainLayout Title Area. (each contains a list of "ModuleItems")
ModuleItem - An item in each of the above Module Lists
PageItem - The actual Page Component Name (pageitems use "nameof(pagecomponent)" to assign a page item value.
PageAction - Optional route fragments
Key Point: using string constants in a centrally located file subdirectory allows global renaming. And, allows using friendlier names in the source code while using more-so cryptic names in the built public url routes.
* it does come with a bit of overhead managing names and assigned values but IMO worth the organizational effort; especially after the solution grows with many pages and complex list/item navigation. Maintaining this type information can be handled by even non-coders with app/solution familiarity.
App Specific Component Base Class:
A base class that inherits "ComponentBase" is used by all page components instead of ComponentBase. This is where incoming URL requests are handled. This class contains a number of methods and state management that are in common to all pages.
Route Manager Class:
There are "ModuleList" and ModuleItem" definition files that are essentially dictionary's to look up a name to .NET Type then activate the type to an instance.
The "RouteManager" class contains URL Encoding and URL Decoding methods.
Route Manager - URL Encoding:
When a user clicks on a ModuleItem TreeItem selection, the tree item information is passed to a URL Encoding method to build a "deep link". Also, application specific query string parameters are appended.
Route Manager - URL Decoding:
The incoming request route segments are parsed and mapped to the above blazor components: ModuleList, ModuleItem, PageItem.
If a route change is detected, then events are fired to notify the MainLayout Component to update the affected component contents.
SessionStateManger Service Class:
Implemented is a comprehensive more-so generic type session manager class. It handles app-specific requirements.
Retaining login and navigation to all "module's". ie the feature set and supporting pages.
The solution also contains other supporting code.
>>> Overall, there is a lot going on to manage everything thus creating a working API for all page component content developers to code around as an de-facto standard.
DEEP LINKING:
Using the above, the blazor solution supports detailed deep linking to fully restore state; restoring the four primary components listed above. Plus, optional route fragments and query string values to further define state for a given page component.
*** I hope I have described the architecture clearly enough to grasp and visualize the implementation.***
I'd appreciate relevant input to affect the outcome of the implementation.
*** Enhanced Blazor Router Component ***
Download the source code for the Microsoft built in Blazor "Router" Component.
From there a few changes to modify the source code to support an alternate "Custom" router match method.
Create a new revised version of the Router Component which will now have two action-methods to find a route instead of just one.
1) via lists of 'TreeItem' models; which are the same models I use with the Telerik TreeView Component (ie ModuleItem type NavMenu).
2) via @page or route attributes <-- the original out of box way of finding a page route
* number 2 above can be disabled to only use a list of TreeItem models to find a page route.
* this means all page content components do not require decorating them with the @page directive (or route attribute)
if your web app solution has a URL route template pattern as follows, then using this enhanced Router Component might be for you.
xyz.com /ModuleListName/ModuleItemName/PageName/ optionalSeg1/optionalSeg2 ?yourQueryString=true
* See the five attached files for insight how-to
* This enhanced Blazor Router Component seems to make sense allowing a typical developer create their own URL router to target page decoder.
ie this is an alternate custom way to navigate to a specific "page" without the need to decorate components intended as "pages" with an @page directive or route attribute. Instead uses TreeItem lists as the route template source of truth; for both decoding and encoding URL's
NOTE: source code not shown is an extensive library I created. It also contains a method to pass to the enhanced router component as an action delegate; where it is invoked to decode the request URL route to a target page.
This library also encodes a URL to use with "NavigateTo()" which uses the same TreeItem model lists to build the url.
KeyPoint: can make the TreeItem model lists static or store and load from a database table for user navigation configuration changes