<p dir="auto">This issue describes a design for an injectable alternative to <code class="notranslate">@CanActivate</code></p>
<p dir="auto">It is both a fresh start and a continuation of the conversations associated with issues <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="58469474" data-permission-text="Title is private" data-url="https://github.com/angular/angular/issues/748" data-hovercard-type="issue" data-hovercard-url="/angular/angular/issues/748/hovercard" href="https://github.com/angular/angular/issues/748">#748</a> and <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="105758695" data-permission-text="Title is private" data-url="https://github.com/angular/angular/issues/4112" data-hovercard-type="issue" data-hovercard-url="/angular/angular/issues/4112/hovercard" href="https://github.com/angular/angular/issues/4112">#4112</a> and tangentially in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="136037476" data-permission-text="Title is private" data-url="https://github.com/angular/angular/issues/7256" data-hovercard-type="issue" data-hovercard-url="/angular/angular/issues/7256/hovercard" href="https://github.com/angular/angular/issues/7256">#7256</a>, <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="94122106" data-permission-text="Title is private" data-url="https://github.com/angular/angular/issues/2965" data-hovercard-type="issue" data-hovercard-url="/angular/angular/issues/2965/hovercard" href="https://github.com/angular/angular/issues/2965">#2965</a>, <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="133821990" data-permission-text="Title is private" data-url="https://github.com/angular/angular/issues/7091" data-hovercard-type="issue" data-hovercard-url="/angular/angular/issues/7091/hovercard" href="https://github.com/angular/angular/issues/7091">#7091</a></p>
<hr>
<p dir="auto">I've been talking to the team about these router hook methods. I have a good feeling about an emerging design that should arrive before RC. I thought I'd share it here so we can get some feedback.</p>
<p dir="auto">CAVEAT: this is <em>Ward's</em> interpretation of what he <em>thinks</em> he heard which may vary from the team's interpretation and is not necessarily what will happen.</p>
<p dir="auto">As I understand it, <code class="notranslate">CanActivate</code> will become an instance method, probably named <strong><code class="notranslate">routerCanActivate</code></strong>.</p>
<p dir="auto">The catch is that we implement <code class="notranslate">routerCanActivate</code> on (what I call) a <strong>_routing component</strong>_: a component with an attached router and routes and whose template includes a <code class="notranslate">&lt;router-outlet&gt;</code>.</p>
<blockquote>
<p dir="auto">We didn't talk about it but I think Angular should ignore it on a non-routing component or perhaps throw an error.</p>
</blockquote>
<p dir="auto">The router will consult this method before pursuing any of the routes that would populate that <code class="notranslate">&lt;router-outlet&gt;</code>. It gates navigation to any of the routes configured for this component's router.</p>
<p dir="auto">To be clear, it has no role in allowing/preventing navigation to <em>the routing component itself</em>; only navigation to the routes configured for this routing component's <code class="notranslate">&lt;router-outlet&gt;</code>.</p>
<p dir="auto">As an instance method, the <code class="notranslate">routerCanActivate</code> has access to anything we inject into its routing component. The injector (and its scope) is that of this routing component.</p>
<p dir="auto">I think this addresses many concerns:</p>
<ol dir="auto">
<li>We can inject anything we need without crazy hacks</li>
<li>We can scope injection at an appropriate level in the component tree. We don't have to capture the root injector and hope that the service we need has been provided there. We can access services injected "down here", at the local router configuration level.</li>
<li>We can make decisions about whether to allow navigation to a lazy loaded component. Before we would have had to load that component and call its <code class="notranslate">@CanActivate</code>, perhaps only to belatedly disallow it.</li>
</ol>
<p dir="auto">I had a few initial objections:</p>
<ol dir="auto">
<li>I didn't think the routing component should be responsible for <em>can activate</em> decisions about all of its routes.</li>
<li>Why should it know all of the details about whether a route's navigation should be allowed? That's a decision that belongs to the destination component, right?</li>
<li>Some people have used <code class="notranslate">CanActivate</code> to pre-load data from the server for the destination component.  There won't be an obvious way to do that with this design which doesn't provide a way to deliver a payload to the destination component.</li>
<li>The <em>can activate</em> logic has access to services at the routing component level and above but not the services (if any) that will be provided by (and therefore scoped at) the destination component itself. That could be too limiting.</li>
</ol>
<p dir="auto">Here is why these objections no longer trouble me.</p>
<ol dir="auto">
<li>
<p dir="auto">If deciding is a burden, the routing component can delegate that burden to a custom injected <code class="notranslate">CanActivateService</code>, scoped to the routing component itself. That service can inject much of what it needs to make decisions. The routing component can stay lean. This is <em>our</em> decision as application developers; Angular 2 neither requires it nor blocks it.  I like that.</p>
</li>
<li>
<p dir="auto">While I like the idea that the <em>can activate</em> logic is part of the development of the destination component, that logic doesn't have to be inscribed within the destination component class. We can come up with other patterns that will work well, particularly in concert with the <code class="notranslate">CanActivateService</code> approach I just mentioned. I think we can get the allocation of responsibilities right without too much friction. And this approach does put us in position to gate navigation to a lazy-loaded destination component which I think is the more important benefit.</p>
</li>
<li>
<p dir="auto">Actually, I always thought using <em>can activate</em> logic to pre-load data was a hack. Many folks did that kind of thing with resolve in Angular 1.  I never liked it.  Please don't bring back <code class="notranslate">resolve</code>!</p>
<p dir="auto">But like/dislike aside, we don't need it in Angular 2.  In Angular 2, we have both the <code class="notranslate">routerOnActivate</code> and the <code class="notranslate">ngOnInit</code> hooks (missing in Angular 1) which are fine places to pre-load data.</p>
</li>
<li>
<p dir="auto">I couldn't think of a use case in which it was essential that we consult a service instance created just for the destination component. OTOH, if we <em>could</em> do that, we'd be faced with the obligation to create such a service which brings unwanted complexity of its own.</p>
<p dir="auto">For example, if we allow navigation, we have to make sure that service (whatever it is), is ultimately injected into the destination component. There is no facility for doing that kind of thing today. Today we can't access the internal steps that go into hydrating a component ... and I rather doubt I'd want to risk what comes with breaking into that sausage factory.</p>
<p dir="auto">What if we created that service and then <em>rejected</em> the navigation? What did that service do? Did it know it was going to be created and then thrown away? Does it need to be cleaned up before being thrown away.  It's a horrible can of worms.</p>
</li>
</ol>
<p dir="auto">In the end, I think this design will work well for us. It's safe, clean, easy to understand once explained.</p>
<p dir="auto"><strong>Angular consults a routing component's  <code class="notranslate">routerCanActivate</code> method before navigating to any of the routes defined for that component's router.</strong></p>
<p dir="auto">I don't know what the method parameters will be. Obviously something about where the router is going.  This is a good time for you all to chime in.</p>
<p dir="auto">p.s.: I don't know what will become of the current <code class="notranslate">@CanActivate</code> decorator. I suppose it could live on as a vestigial relic. Personally, I'd vote for it to be deprecated immediately and removed before release.</p>
<p dir="auto">p.p.s.:  I have stopped thinking that the <code class="notranslate">routerOnActivate</code> method is or could be a substitute for <code class="notranslate">routerCanActivate</code> (quite apart from the fact that it doesn't work as a substitute today).</p>
<p dir="auto">It should not be involved in the decision about whether we should navigate to this component. It should concentrate on what to do <em>now that we're here</em>. And in that respect it should differentiate from its companion hook method, <code class="notranslate">routerOnReuse</code>, which concerns what to do <em>now that we're back</em>.</p>
<p dir="auto">Puzzling with SubRouters in Angular 2 I stumbled upon this issues</p>
<p dir="auto"><em>Assumption</em>: I'm not very confident due to the state of beta and lack of firm stance in the documentation, but it seems that only root router defined paths will use query (optional) parameters and all sub-routers seems to use <a href="https://www.w3.org/DesignIssues/MatrixURIs.html" rel="nofollow">Matrix URLs</a> for optional parameters.</p>
<p dir="auto">There are couple of issues as of <code class="notranslate">beta-15</code>:</p>
<ol dir="auto">
<li>
<p dir="auto">[routerLink] generates query parameters, instead of matrix ones for links to sub-routes. Is this expected behaviour?</p>
<blockquote>
<p dir="auto">In this case <code class="notranslate">routeParams.get()</code> <strong>doesn't</strong>  work probably by design, because it expects matrix params.</p>
</blockquote>
</li>
<li>
<p dir="auto">Router.navigate() <strong>doesn't</strong> convert query parameters to martix one for sub-routes also. Is this expected behavior?</p>
<blockquote>
<p dir="auto">In this case <code class="notranslate">routeParams.get()</code>, also <strong>doesn't</strong> work probably by design, because it expects matrix params.</p>
</blockquote>
</li>
<li>
<p dir="auto">It does so on page reload or direct hit. (Try it: <a href="http://run.plnkr.co/plunks/x7rR4NfZMLYj34IyCiyI/?value=2" rel="nofollow">http://run.plnkr.co/plunks/x7rR4NfZMLYj34IyCiyI/?value=2</a>)</p>
<blockquote>
<p dir="auto"><code class="notranslate">?value=2</code> becomes <code class="notranslate">/;value=2?value=2</code><br>
In this case <code class="notranslate">routeParams.get()</code> <strong>do</strong> work and gets the matrix parameter value!</p>
</blockquote>
</li>
<li>
<p dir="auto">(side issue) On page reload the router (I assume correctly) converts query params to matrix one, but leaves query ones duplicated. (<code class="notranslate">?value=2</code> becomes <code class="notranslate">/;value=2?value=2</code>).</p>
<blockquote>
<p dir="auto">In this case <code class="notranslate">routeParams.get()</code> <strong>do</strong> work and gets the matrix parameter value, <strong>but</strong> leaves duplicates!</p>
</blockquote>
</li>
</ol>
<p dir="auto">To sum it, in current state of <code class="notranslate">[routerLink]</code>, <code class="notranslate">Router.navigate()</code> and <code class="notranslate">routeParams.get()</code> is almost imposible to get sub-routers to work.</p>
<p dir="auto"><strong>Steps to reproduce and a minimal demo of the problem</strong></p>
<p dir="auto"><a href="http://run.plnkr.co/plunks/x7rR4NfZMLYj34IyCiyI/" rel="nofollow">http://run.plnkr.co/plunks/x7rR4NfZMLYj34IyCiyI/</a></p>
<ol dir="auto">
<li>Click a link (Two provided for <code class="notranslate">[routerLink]</code>, <code class="notranslate">Router.navigate()</code> respectively)</li>
<li>Refresh the page (F5 on windows, CMD+R on Mac usually)</li>
</ol>
<p dir="auto"><strong>Current behavior</strong><br>
Inconsistent behavior (not obvious) and  lack of documentation for matrix URL strategy for sub-routers.</p>
<p dir="auto"><strong>Expected/desired behavior</strong></p>
<ol dir="auto">
<li>If we are going in the direction of Matrix URLs, support that in <code class="notranslate">[routerLink]</code>, <code class="notranslate">Router.navigate()</code>.<br>
OR</li>
<li>Hack <code class="notranslate">routeParams.get()</code> to fall back to query parameters in scenarios above (I personally not for this, because it will fall in scenarios with multiple sub-routes with optional parameters)</li>
</ol>
<p dir="auto"><strong>Other information</strong></p>