6

Here is a plunker to play with:

https://plnkr.co/edit/qTjftING3Hk2fwN36bQa?p=preview

Everything works well, except when you manually change the id parameter in the url bar, because then the projects dropdown does not show the new projectId as current project. This happens when the user saves an url as favorite link and copy/paste it into the url bar! A common case I would say!

To fix this I can listen to route.params changes in the TestsListComponent and add a check with a sharedService/EventEmitter wheter the changed id exists in that dropdown. The bool return value existsProjectId inside the TestsListComponent decides wether I should redirect to the /projects page because the id did not exist.

But honestly, redirecting from the TestsListComponent is too late at least from a user experience perspective, because the route projects/:id/tests is already activated.

How would you fix that misbehavior?

P.S.

Pascal
  • 12,265
  • 25
  • 103
  • 195
  • I didn't fully read into your question. I just saw "I have just the feeling the router should offer a really componentless route with just a path: "projects/:id" like the angularJS ui router did!" What should that do? What you can do is a router with only a `path` **and** `children` (but without a component) – Günter Zöchbauer Jan 05 '17 at 09:30
  • Let us just assume I have that componentless route /projects/:id with children property. As soon the user changes the dropdown`s current project item the children get activated, what I do not want. I want the intermediate step that the user clicks the open button then the children property default route /projects/:id/overview must be activated. You have any solution for that case? – Pascal Jan 05 '17 at 10:23
  • Only `redirectTo`. I don't get why you don't want it. – Günter Zöchbauer Jan 05 '17 at 10:26
  • Maybe I do not understand you , but when the projects/:id route has a redirectTo property then there should no redirect to anywhere happen. First the user must click the open button. You have time for a quick chat in your prefered austria language ;-) – Pascal Jan 05 '17 at 10:30
  • Sorry, your question seems too complicated. I don't have the time right now. This is why "I didn't fully read into your question." (from my first comment) – Günter Zöchbauer Jan 05 '17 at 10:49
  • I cleaned up the whole post, hope its more clear now :-) – Pascal Jan 05 '17 at 18:03
  • I'll have a closer look later today or tomorrow. – Günter Zöchbauer Jan 05 '17 at 18:15
  • If you refresh the browser with the 'manually edited url' does it produce the desired behavior? – scottyaslan Aug 25 '18 at 02:31

3 Answers3

0

There is no global params change one can subscribe to.

One approach is to locate the route and its params starting from the root router:

console.log('router', this.router.routerState.root.firstChild.firstChild && this.router.routerState.root.firstChild.firstChild.snapshot.params);

This can be done within this.router.events.subscribe((val) => { or within a router guard like canActivate or resolve https://angular.io/docs/ts/latest/guide/router.html#!#guards

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    The code does not compile, as 'currentRouterState' property of router instance is private! Just a senseless side comment: with angularJS I had a global route change in m y app.js but no component orientation :P UPDATE I just saw there is the public 'routerState' property, i try that! – Pascal Jan 06 '17 at 09:44
  • The public getter seems to be `routerState`. I got `currentRouterState` from `console.log()` and in Plunker it was working. – Günter Zöchbauer Jan 06 '17 at 09:51
  • ok about the private currentRouterState I tried that in my typescript app. Maybe in the plunkr such things like private are ignored ROFL – Pascal Jan 06 '17 at 09:55
  • They are, Plunker doesn't do such static checks. At runtime they ignored everywhere anyway. I'm not actively using TypeScript with Angular myself (only Dart) and know TypeScript only from Plunker and therefore are not familiar with these static TS checks. – Günter Zöchbauer Jan 06 '17 at 09:57
  • I will check today/tomorrow more reasonly wether this line of code is better than my hack solution. I was hoping for more an achitectural interface I can hook into;-) hm... also in my TS app I get null for the firstChild while I get no errors in the plunkr. Ok I will have to if-out the chained property access for null values... – Pascal Jan 06 '17 at 10:01
  • Günter you can check my plunkr link from above. I have first extended the routes to support an outlet more according to my TS app on the notebook. Then I want to know wether I still get the same data consoled outl like before fine. I even found out how to edit the url of a plunkr see the answer in my plunkr! – Pascal Jan 06 '17 at 22:58
  • When I copy/paste a favorite link into the url and I am inside my subscribed events on page load, the router and route is always NULL. So that is not a solution for me. I think I am running into an edge case of the router. Any idea is appreciated else I throw that plunkr at Victor! – Pascal Jan 06 '17 at 23:00
  • You might need to use a `setTimeout()`. I remember a situation with the router where some data only becomes available in the next turn. AFAIR this is a known issue - not sure though if this is exactly that case. – Günter Zöchbauer Jan 06 '17 at 23:47
  • Plunkr updated! When I set the timeout to 3 seconds of course then the route/router is available. But I need that instances right in that event. SetTimeout does not block the app execution :P You have an issue link maybe? – Pascal Jan 07 '17 at 00:03
  • Could it be that issue? https://github.com/angular/angular/pull/13454#issuecomment-270716651 – Pascal Jan 07 '17 at 00:22
0

Update your app.routes.ts file like below and use HashLocationStrategy, for reference: HashLocationStrategy

import {Routes, RouterModule} from '@angular/router';
import ProjectListComponent from './projects-list.component';
import TestsListComponent from './tests-list.component';
import OverviewComponent from './overview.component';
import {LocationStrategy, HashLocationStrategy} from '@angular/common';


export const routes: Routes = [
    { path: '', redirectTo: 'projects', pathMatch: 'full' },
    {
        path: 'projects', component: ProjectListComponent,
        children: [
            { path: ':id', redirectTo: ':id', pathMatch: 'full' },
            {
                path: ':id', component: OverviewComponent,
                children: [
                    { path: '', redirectTo: 'tests', pathMatch: 'full' },
                    { path: 'tests', component: TestsListComponent },

                ]
        ]
    }
];

export const appRoutingProviders: any[] = [
    {
        provide: LocationStrategy,
        useClass: HashLocationStrategy
    }
];

export const routing = RouterModule.forRoot(routes);
  • Thanks for the hash location tip. Indeed I use that locally but forgot to set it up in the plunkr. But remember my question:"how would you fix that misbehavior". That is not solved yet because when you paste a link into the browser with id 7 the navstart event is not fired only the navend event but AFTER the params change of the overview/tests component. That means its waaay too late to intercept the id. – Pascal Jan 10 '17 at 23:14
0

I'm not sure if i correctly understand your question, anyway here is my plunkr.

  this.router.events.subscribe(event => {

        if (event instanceof RoutesRecognized)
        {
          var first=event.state.root.firstChild.firstChild;
          var id = first && event.state.root.firstChild.firstChild.params['id'];
          console.debug('recognized', id);
          this.currentProject = this.projects[+id-1];

          console.log(this.currentProject);
        } 
  });

Just navigate to #/projects/1/tests.

kemsky
  • 14,727
  • 3
  • 32
  • 51
  • COPY and PASTE this link: https://run.plnkr.co/JjcYBTtgvVwS90dk/#/projects/2/tests into a browser tab. The expectation is that I get the CURRENT Id to set in as current project in the dropdown. Its NOT about navigating with the ui elements like clicking somewhere. See also my comment to user Ibrahim above. – Pascal Jan 11 '17 at 07:55
  • i've updated plunkr , it will work with copy & paste – kemsky Jan 11 '17 at 19:47
  • Thanks for your help yes the plunkr works but... I have studied your code and there is one bummer for me: this.currentProject = this.projects[this.service.id]; I can NOT hardcode the database id to the index of the project list for obvious reason: 10 projects but one with id 20. These Id`s need not to be consecutive like 1,2,3,4,etc... http://stackoverflow.com/questions/14642013/why-are-there-gaps-in-my-identity-column-values – Pascal Jan 11 '17 at 21:02
  • My suggestion is to impl. a this.uniqueObjectIdentifier = Math.random(); for each project instance and use the myarray.find() method ;-) – Pascal Jan 11 '17 at 21:23
  • So much about the current Id which occurs only in the NavigationEnd event. I had that problem somewhere in that thread commented: https://github.com/angular/angular/issues/12912 With latest angular 2.4.2 its fixed! Maybe NavigationStart and the snapshot has now the current params. But maybe that doesn`t change the game much vs the RouteRecognized event approach. – Pascal Jan 11 '17 at 21:51
  • It is actually different question. You should use `Resolve` guard to preload project ids, see https://angular.io/docs/ts/latest/api/router/index/Resolve-interface.html. Also you could make `RouterService` more reusable. – kemsky Jan 12 '17 at 12:24
  • Forget about my unique object identifier, I was confused! I should find the object via the id in the projects array. BUT I have see you subtract -1 from the id ??? this seems hardcoded and not reusable for me. I have already used the Resolve before, it has its bugs with url changing and not loading data again. I try to avoid it atm. – Pascal Jan 12 '17 at 17:31
  • -1 is used because of array index lookup and array index starts from 0, it could be replaced with `let currentProject = this.currentProjects.find(p => p.id === id)` – kemsky Jan 12 '17 at 19:46
  • Sorry man for answering that late, family is sick;-) I quickly modified the plunkr: https://plnkr.co/edit/y7e1hQvzu8vX7gPWJqOK?p=preview to use the find method everywhere and it works as expected ;-) Thanks again for your help, the RoutesRecognized which I underestimated did the trick ;-) – Pascal Jan 15 '17 at 21:32