Предварительная оценка
Итак, после некоторой отладки я нашел проблему.
Извините за длинный ответ, я просто добавил несколько советов для вашего кода после решения вашей проблемы.
TL;DR
Я подготовил для вас оптимизированный Stackblitz, проверьте его, чтобы увидеть все изменения!
https://stackblitz.com/edit/stackoverflow-question-52109889
Исходная проблема
Посмотрите на свои маршруты:
const routes: Routes = [
{
path: ':token', component: AppComponent, children: [
{ path: 'dashboard', component: DashboardComponent },
{ path: 'inbox', component: InboxComponent },
{ path: '', component: NavbarComponent, outlet: 'navbar' }
]
}
];
Проблемы с этими маршрутами и комментариями
- Angular использует параметры пути. Таким образом, параметр
:token
передается только AppComponent.
Возможное решение ниже.
- Несвязанный, но тоже запах кода: AppComponent внутри маршрутизатора
Маршрутизатор заполняет теги <router-outlet>
компонентами на основе URL-адреса. Поскольку AppComponent уже является корневым компонентом, вы загружаете AppComponent в RouterOutlet самого себя.
- Ваша панель навигации работает только потому, что вы позволяете ей соответствовать ВСЕМ URL-адресам.
Поскольку вы поместили ее ниже пути :token
в качестве дочернего маршрута, она также получает параметр :token
, и поскольку ваша логика для ее чтения находится внутри BasePage.ts ` он также может прочитать его.
Таким образом, ваша панель навигации работает только случайно.
Простейшие решения
Самое простое решение — зайти в BasePage.ts
и изменить следующий код
this.queryParams = this.route.snapshot.queryParams
this.routeParams = this.route.snapshot.params;
to
this.queryParams = this.route.parent.snapshot.queryParams
this.routeParams = this.route.parent.snapshot.params;
Вот документация для класса ActivatedRoute:
https://angular.io/guide/router#activated-route
Еще несколько советов
Интегрируйте ссылку на приложение для Stackblitz в качестве маршрута по умолчанию
Вместо того, чтобы публиковать свой Stackblitz и ссылку для вызова внутри stackblitz, добавьте свою ссылку внутри stackblitz в качестве маршрута по умолчанию. Просто добавьте этот маршрут в массив маршрутов, и ваш Stackblitz автоматически загрузит нужный маршрут:
{ path: '', pathMatch: 'full', redirectTo: '2345abcf/inbox' }
ActivatedRoute.params и ActivatedRoute.queryParam устарели.
Вместо этого используйте ActivatedRoute.paramMap и ActivatedRoute.queryParamMap.
См. официальные документы angular: https://angular.io/guide/router#activated-route
Максимально используйте наблюдаемые версии paramMaps.
Вы должны использовать paramMaps как Observable практически каждый раз вместо snapshot.paramMaps.
Проблема со снимком заключается в следующем:
- Маршрутизатор активирует компонент 1
- Например, ParamSnapshot красный в ngOnInit
- Маршрутизатор активирует Component2
- Router activates Component1 again, but with another path param.
2 Problems:
- ParamSnapshot is immutable, so the new value would not be accessible
- ngOnInit больше не запускается, так как компонент уже находится в dom.
Создавайте свои маршруты, используя атрибут routerLink
См.: https://angular.io/guide/router#router-links. позволяет избежать возможных ошибок при построении маршрутов вручную. Обычно маршрут с двумя точками-маршрутизаторами выглядит так:
https://some-url.com/home(conference:status/online)
/home
— это основной маршрут, а (conference:status/online)
говорит, что <router-outlet name=‚conference‘>
должен загрузить маршрут /status/online.
Поскольку вы опускаете последнюю часть для второго выхода, не сразу понятно, как angular будет обрабатывать маршруты.
Не используйте вторую розетку роутера только для навигации
Это делает вещи более сложными, чем они должны быть. Вам не нужно перемещаться по навигации отдельно от контента, не так ли? Если вам не нужна отдельная навигация, проще включить ваше меню напрямую в шаблон AppComponent.
Второй маршрутизатор-выход имеет смысл, когда у вас есть что-то вроде боковой панели для чата, где вы можете выбирать контакты, иметь окно чата и страницу для настроек чата, при этом все они независимы от вашей основной стороны. Тогда вам понадобятся два маршрутизатора-розетки.
Используйте либо базовый класс, либо службу angular
Вы предоставляете свою BasePage как услугу Angular через
@Injectable({
providedIn: ‚root‘
})
Использование внедряемого сервиса в качестве базового класса кажется очень странным замыслом. Я не могу придумать причину, я хотел бы использовать это. Либо я использую BaseClass как BaseClass, который работает независимо от angular. Или я использую угловой сервис и внедряю его в компоненты, которым нужна функциональность сервиса.
Примечание. Вы не можете просто создать службу, которая вводит Router
и ActivatedRoute
, потому что она получит корневые объекты этих компонентов, которые пусты. Angular генерирует собственный экземпляр ActivatedRoute
для каждого компонента и внедряет его в компонент.
Решение. См. «Лучшее решение: TokenComponent с Token Service» ниже.
Лучшее решение: TokenComponent с Token Service
Лучшее решение, которое я могу придумать прямо сейчас, — это создать TokenComponent вместе с TokenService. Вы можете просмотреть рабочую копию этой идеи на Stackblitz:
https://stackblitz.com/edit/angular-l1hhj1
Теперь важные фрагменты кода, на случай, если Stackblitz будет удален.
Мой TokenService выглядит так:
import { OnInit, Injectable } from '@angular/core';
import { Observable, ReplaySubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class TokenService {
/**
* Making the token an observable avoids accessing an 'undefined' token.
* Access the token value in templates with the angular 'async' pipe.
* Access the token value in typescript code via rxjs pipes and subscribe.
*/
public token$: Observable<string>;
/**
* This replay subject emits the last event, if any, to new subscribers.
*/
private tokenSubject: ReplaySubject<string>;
constructor() {
this.tokenSubject = new ReplaySubject(1);
this.token$ = this.tokenSubject.asObservable();
}
public setToken(token: string) {
this.tokenSubject.next(token);
}
}
И этот TokenService используется в TokenComponent в ngOnInit:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, ParamMap } from '@angular/router';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { TokenService } from './token.service';
@Component({
selector: 'app-token',
template: `
<p>Token in TokenComponent (for Demonstration):
{{tokenService.token$ | async}}
</p>
<!--
This router-outlet is neccessary to hold the child components
InboxComponent and DashboardComponent
-->
<router-outlet></router-outlet>
`
})
export class TokenComponent implements OnInit {
public mytoken$: Observable<string>;
constructor(
public route: ActivatedRoute,
public router: Router,
public tokenService: TokenService
) {
}
ngOnInit() {
this.route.paramMap.pipe(
map((params: ParamMap) => params.get('token')),
// this 'tap' - pipe is only for demonstration purposes
tap((token) => console.log(token))
)
.subscribe(token => this.tokenService.setToken(token));
}
}
Наконец, конфигурация маршрута, чтобы склеить все это вместе:
const routes: Routes = [
{
path: ':token', component: TokenComponent, children: [
{ path: '', pathMatch: 'full', redirectTo: 'inbox'},
{ path: 'dashboard', component: DashboardComponent },
{ path: 'inbox', component: InboxComponent },
]
},
{ path: '', pathMatch: 'full', redirectTo: '2345abcf/inbox' }
];
Как получить доступ к токену сейчас
Вставьте TokenService туда, где вам нужен токен.
Когда вам это нужно в шаблоне, получите к нему доступ с помощью асинхронного канала следующим образом:
<p>Token: {{tokenService.token$ | async}} (should be 2345abcf)</p>
Когда вам нужен токен внутри машинописного текста, получите доступ к нему, как к любому другому наблюдаемому (пример в компоненте «Входящие»)
this.tokenService.token$
.subscribe(token => console.log(`Token in Inbox Component: ${token}`));
Я надеюсь, это поможет вам. Это был интересный вызов! :)
person
Benjamin Jesuiter
schedule
13.09.2018