Angular2 Получить параметры маршрутизатора за пределами маршрутизатора-розетки

У меня есть приложение панели мониторинга, которое состоит из компонента дерева (который перечисляет различные узлы контента) и компонента редактирования панели, который отображает некоторый редактируемый контент в зависимости от того, какая ветвь дерева выбрана.

Например, дерево выглядит так:

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

Вы нажимаете "Арсенал" в дереве, и он отображает некоторый контент для этой команды в редактируемой панели на странице.

Компонент, который отображает подкомпоненты, выглядит следующим образом:

@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
    ]
})

Редактируемый контент отображается в виде router-outlet так что каждый редактируемый фрагмент контента имеет свой собственный отдельный URL, например example.com/content/edit/123 где 123 например, идентификатор контента Арсенала.

Это все отлично работает.

Тем не менее, я хочу получить доступ к id параметр маршрута в content-tree составная часть. В настоящее время я почти уверен, что код, который у меня есть в этом компоненте, должен работать:

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
            );
    }
}

Но nodeId переменная в ngOnInit метод всегда возвращается как 0,

Вопросы: возможно ли получить доступ к параметрам маршрута в компоненте, отображаемом через маршрутизатор-выход? Если так, то лучший ли способ справиться с этим, чтобы создать второй (именованный, потому что теперь будет 2) роутер-выход? Если нет, то что я делаю не так?

Большое спасибо.

РЕДАКТИРОВАТЬ:

Работающий (и очень некрасивый;)) теперь был создан Plnkr, чтобы показать основы приложения: http://plnkr.co/edit/W3PVk3Ss5Wq59IbnLjaK?p=preview. Смотрите комментарии о том, что должно произойти...

4 ответа

Решение

В новом маршрутизаторе (> = RC.0 <= RC.2) это будет

  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);
    });
  }

См. Также Angular 2 RC1: получение параметров из исходного использованного URL.

Получить активный маршрут извне компонента в angular 2.1.0 и Router 3.1.0

Я нашел хороший способ получить все параметры, queryParmas, сегменты и фрагменты из отображаемого маршрута из любой точки вашего приложения. Просто добавьте этот Код к любому Компоненту, где он вам нужен, или создайте Сервис, который можно внедрить в ваше Приложение.

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);
    }
  });
}
...

Это решение сработало для меня: полный пример.

constructor(
  private readonly router: Router,
  private readonly rootRoute: ActivatedRoute,
){
  router.events.pipe(
    filter(e => e instanceof NavigationEnd),
    map(e => this.getParams(this.rootRoute))
  ).subscribe(params => {
   //
  });
}

private getParams(route: ActivatedRoute): Params {
  // route param names (eg /a/:personId) must be ditinct within
  // a route otherwise they'll be overwritten
  let params = route.snapshot.params
  params = { ...route.snapshot.queryParams, ...params}
  if(route.children){
    for(let r of route.children){
      params = {...this.getParams(r), ...params};        
    }
  }
  return params;
}

Кредиты на Токсичные.

Возможно ли получить доступ только к параметрам маршрута в компоненте, отображаемом через маршрутизатор-выход?

Да, <router-outlet></router-outlet> говорит Angular2 обрабатывать содержащий компонент как компонент маршрутизации. Поэтому вы не можете получить RouteParams экземпляр внедрен в класс, поскольку он не был создан с помощью директивы маршрутизации.

Если нет, то что я делаю не так?

Я бы не сказал, что вы делаете что-то не так, у вас просто было неправильное представление о том, как это было задумано. У меня тоже есть это первоначальное заблуждение. Я обнаружил, что эта статья на Angular2 - отличный источник информации о том, как передавать данные и как общаться между родительскими и дочерними компонентами.


В вашем конкретном случае я бы предложил удалить RouteParams от constructor из ContentTreeComponent поскольку он будет доступен только в том случае, если визуализируется из компонента маршрутизации.

export class ContentTreeComponent implements OnInit {

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

    // Omitted for brevity...
}

Затем, чтобы получить idвам, вероятно, придется поделиться чуть большим количеством кода верхнего уровня, чтобы я мог видеть, откуда он взялся...

Из-за того, что решения, найденные в этом посте, не смогли решить мою проблему, я просто добавил другое решение, которое в настоящее время может исправить или помочь вам справиться с такого рода проблемами в другом посте с похожим вопросом: Angular 2: Как получить параметры маршрута снаружи роутера-розетки

Другие вопросы по тегам