1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Threading . Tasks ;
4
+ using Microsoft . AspNetCore . Components ;
5
+ using Microsoft . AspNetCore . Components . RenderTree ;
6
+ using Microsoft . AspNetCore . Components . Routing ;
7
+
8
+ namespace BlazorRouter
9
+ {
10
+ public class Switch : ComponentBase , IDisposable
11
+ {
12
+ private int CreateCascadingValue < T > ( RenderTreeBuilder builder , int seq , T value , string name , RenderFragment child )
13
+ {
14
+ builder . OpenComponent < CascadingValue < T > > ( seq ++ ) ;
15
+ builder . AddAttribute ( seq ++ , "Value" , value ) ;
16
+ builder . AddAttribute ( seq ++ , "Name" , name ) ;
17
+ builder . AddAttribute ( seq ++ , "ChildContent" , child ) ;
18
+ builder . CloseComponent ( ) ;
19
+ return seq ;
20
+ }
21
+ protected override void BuildRenderTree ( RenderTreeBuilder builder )
22
+ {
23
+ var seq = 0 ;
24
+
25
+ seq = CreateCascadingValue ( builder , seq , this , "SwitchInstance" , ChildContent ) ;
26
+ CreateCascadingValue ( builder , seq , parameters , "RouteParameters" , currentFragment ) ;
27
+ }
28
+
29
+ [ Inject ] private IUriHelper UriHelper { get ; set ; }
30
+ [ Inject ] private INavigationInterception NavigationInterception { get ; set ; }
31
+ [ Inject ] private IComponentContext ComponentContext { get ; set ; }
32
+ [ Parameter ] public RenderFragment ChildContent { get ; set ; }
33
+ [ Parameter ] public EventHandler < RouteMatchedEventArgs > OnMatch { get ; set ; }
34
+
35
+ private readonly RouteTable routes = new RouteTable ( ) ;
36
+ private bool navigationInterceptionEnabled ;
37
+ private string location = "" ;
38
+ private string baseUri = "" ;
39
+ private RenderFragment currentFragment ;
40
+ private IDictionary < string , object > parameters ;
41
+
42
+ static readonly char [ ] queryOrHashStartChar = new [ ] { '?' , '#' } ;
43
+ private async void LocationChanged ( object sender , LocationChangedEventArgs e )
44
+ {
45
+ this . location = e . Location ;
46
+
47
+ await SwitchContent ( e . IsNavigationIntercepted ) ;
48
+ }
49
+
50
+ private string StringUntilAny ( string str , char [ ] chars )
51
+ {
52
+ var firstIndex = str . IndexOfAny ( chars ) ;
53
+ return firstIndex < 0 ? str : str . Substring ( 0 , firstIndex ) ;
54
+ }
55
+
56
+ private Task SwitchContent ( bool isNavigationIntercepted )
57
+ {
58
+ var path = UriHelper . ToBaseRelativePath ( this . baseUri , this . location ) ;
59
+ path = "/" + StringUntilAny ( path , queryOrHashStartChar ) ;
60
+
61
+ var context = new RouteContext ( path ) ;
62
+ routes . Route ( context ) ;
63
+
64
+ if ( context . Fragment != null )
65
+ {
66
+ currentFragment = context . Fragment ;
67
+ parameters = context . Parameters ;
68
+ OnMatch ? . Invoke ( this , new RouteMatchedEventArgs ( this . location , context . TemplateText , context . Parameters , context . Fragment ) ) ;
69
+
70
+ this . StateHasChanged ( ) ;
71
+ }
72
+ else
73
+ {
74
+ if ( isNavigationIntercepted )
75
+ {
76
+ UriHelper . NavigateTo ( this . location , forceLoad : true ) ;
77
+ }
78
+ }
79
+
80
+ return Task . CompletedTask ;
81
+ }
82
+
83
+ protected override Task OnInitAsync ( )
84
+ {
85
+ this . baseUri = UriHelper . GetBaseUri ( ) ;
86
+ this . location = UriHelper . GetAbsoluteUri ( ) ;
87
+ UriHelper . OnLocationChanged += LocationChanged ;
88
+ return Task . CompletedTask ;
89
+ }
90
+
91
+ public Task RegisterRoute ( RenderFragment fragment , string template )
92
+ {
93
+ routes . Add ( template , fragment ) ;
94
+ return Task . CompletedTask ;
95
+ }
96
+
97
+ protected override async Task OnAfterRenderAsync ( )
98
+ {
99
+ if ( ! this . navigationInterceptionEnabled && ComponentContext . IsConnected )
100
+ {
101
+ this . navigationInterceptionEnabled = true ;
102
+ await SwitchContent ( false ) ;
103
+ await NavigationInterception . EnableNavigationInterceptionAsync ( ) ;
104
+ }
105
+ }
106
+
107
+ public void Dispose ( )
108
+ {
109
+ UriHelper . OnLocationChanged -= LocationChanged ;
110
+ }
111
+ }
112
+ }
0 commit comments