Angular2 Get router params outside of router-outle

2019-02-13 05:53发布

问题:

I have a dashboard application which consists of a treeview component (which lists various content nodes) and a dashboard-edit component which renders some editable content depending on which branch of the tree is selected.

e.g. The tree is like this:

- Football
- - Premier League
- - - Arsenal
- - - Chelsea
- - - ...etc
- - Championship
- - - Derby
- - - ...etc

You click 'Arsenal' in the tree and it renders some content for that team in an editable panel on the page.

The component which renders the sub-components is like this:

@Component({
    selector: 'my-dashboard',
    template: `
        <div class="tree-panel-container">
            <div class="tree-panel-content">
                <content-tree [startNodeId]="startNodeIdContent"></content-tree>
            </div>
        </div>
        <router-outlet></router-outlet>
    `,
    directives: [
        ContentTreeComponent, 
        ContentDashboardComponent, 
        RouterOutlet
    ],
    providers: [
        HTTP_PROVIDERS
    ]
})

The editable content is rendered in a router-outlet so that each editable piece of content has its own distinct URL e.g. example.com/content/edit/123 where 123 is the id of the Arsenal content, for example.

This all works fine.

However, what I want to do is be able to access the id route parameter in the content-tree component. Currently, I'm pretty sure the code I have in that component should work:

import {Component, Input, OnInit}   from '@angular/core';
import {Router, RouteParams}        from '@angular/router-deprecated';

import {ContentNode}                from './content-node';
import {ContentService}             from '../services/content.service';


@Component({
    selector: 'content-tree',
    directives: [ContentTreeComponent],
    template: `
        <ol class="tree">
            <li *ngFor="let contentNode of contentNodes" class="tree__branch" [ngClass]="{'tree__branch--has-children': contentNode.HasChildren}">
                <a *ngIf="contentNode.HasChildren" (click)="contentNode.toggle=!contentNode.toggle" class="tree__branch__toggle">
                    {{ !!contentNode.toggle ? '-' : '+' }}
                </a> 
                <a class="tree__branch__link" (click)="onSelect(contentNode)">{{ contentNode.Name }}</a>
                <content-tree *ngIf="contentNode.toggle" [startNodeId]="contentNode.Id"></content-tree>
            </li>
        </ol>
        <div class="error" *ngIf="errorMessage">{{errorMessage}}</div>
    `
})
export class ContentTreeComponent implements OnInit {

    constructor(
        private _contentService: ContentService,
        private _router: Router,
        private _routeParams: RouteParams
    ) { }

    errorMessage: string;

    @Input('startNodeId')
    private _startNodeId: number;

    contentNodes: ContentNode[];

    ngOnInit() { 
        let nodeId = +this._routeParams.get('id');
        console.log('nodeId = ' + nodeId);
        this.getContentNodes();
    }

    onSelect(contentNode: ContentNode) {
        this._router.navigate( ['ContentEdit', { id: contentNode.Id }]  );
    }

    getContentNodes() {
        this._contentService.getContentNodes(this._startNodeId)
            .subscribe(
                contentNodes => this.contentNodes = contentNodes,
                error =>  this.errorMessage = <any>error
            );
    }
}

But the nodeId variable in the ngOnInit method is always returned as 0.

Questions: Is it only possible to access route params in a component rendered by a router-outlet? If so, then is the best method to deal with this to create a second (named, because there will now be 2) router-outlet? If not, then what am I doing wrong?

Many thanks.

EDIT:

A working (and very ugly ;)) Plnkr has now been generated to show the basics of the app: http://plnkr.co/edit/W3PVk3Ss5Wq59IbnLjaK?p=preview. See comments for what is supposed to happen...

回答1:

In the new router (>= RC.0 <=RC.2) this would be

  import 'rxjs/add/operator/first';
  ...

  constructor(private router:Router, private routeSerializer:RouterUrlSerializer, private location:Location) {
    router.changes.first().subscribe(() => {

    let urlTree = this.routeSerializer.parse(location.path());
      console.log('id', urlTree.children(urlTree.children(urlTree.root)[0])[0].segment);
    });
  }

See also Angular 2 RC1: Get parameters from the initial URL used



回答2:

Get the active route from outside a component in angular 2.1.0 and Router 3.1.0

I found a nice way to get all params, queryParmas, segments and fragments from the displayed route from anywhere inside your App. Just add this Code to any Component where you need it, or create a Service that can be injected throughout your App.

import { Router, NavigationEnd } from "@angular/router";
import { Component, OnInit } from '@angular/core';

...

export class MyComponentOrService implements OnInit {

constructor(private router: Router) {}

ngOnInit() {

  /* this subscription will fire always when the url changes */
  this.router.events.subscribe(val=> {

    /* the router will fire multiple events */
    /* we only want to react if it's the final active route */
    if (val instanceof NavigationEnd) {

     /* the variable curUrlTree holds all params, queryParams, segments and fragments from the current (active) route */
     let curUrlTree = this.router.parseUrl(this.router.url);
     console.info(curUrlTree);
    }
  });
}
...


回答3:

Is it only possible to access route params in a component rendered by a router-outlet?

Yes, the <router-outlet></router-outlet> tells Angular2 to treat the containing component as a "routing" component. Therefore you cannot get a RouteParams instance injected into the class as it wasn't instantiated via the routing directive.

If not, then what am I doing wrong?

I wouldn't say you're doing anything wrong, you simply had a misconception on how it was designed. I too has this initial misconception. I found this Angular2 article to be a great source for understanding how to pass data around and how to communicate betwixt parent and child components.


In your specific case I'd suggest removing the RouteParams from the constructor of the ContentTreeComponent as it will only be available if rendered from a "routing" component.

export class ContentTreeComponent implements OnInit {

    constructor(
        private _contentService: ContentService,
        private _router: Router
    ) { }

    // Omitted for brevity...
}

Then in order to get the id, you'd probably have to share a bit more of your top-level code so that I can see where it is coming from...



回答4:

Because of the solutions found in this post weren't able to solve my problem, I've just added an other solution that could currently fix or help you to deal with this kind of issue on an other post with a similar question: Angular 2: How do I get params of a route from outside of a router-outlet