Предварительная оценка
Итак, после некоторой отладки я обнаружил проблему.
Извините за длинный ответ, я просто добавил несколько подсказок для вашего кода после решения вашей проблемы.
TL; DR
Я подготовил оптимизированный Stackblitz для вас, проверьте его, чтобы увидеть все изменения!
https://stackblitz.com/edit/angular-l1hhj1
Оригинальная проблема
Посмотрите на ваши маршруты:
const routes: Routes = [
{
path: ':token', component: AppComponent, children: [
{ path: 'dashboard', component: DashboardComponent },
{ path: 'inbox', component: InboxComponent },
{ path: '', component: NavbarComponent, outlet: 'navbar' }
]
}
];
Проблемы с этими маршрутами и комментариями
- Угловой расходует параметры пути.
Таким образом, параметр
: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;
до
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 загрузится
нужный маршрут автоматически:
{ path: '', pathMatch: 'full', redirectTo: '2345abcf/inbox' }
ActivatedRoute.params и ActivatedRoute.queryParam устарели
Пожалуйста, используйте взамен ActivatedRoute.paramMap и ActivatedRoute.queryParamMap.
Смотрите официальные угловые документы: https://angular.io/guide/router#activated-route
Максимально используйте наблюдаемые версии paramMaps
Вы должны использовать paramMaps как Observable практически каждый раз вместо snapshot.paramMaps.
Проблема со снимком заключается в следующем:
- Маршрутизатор активирует Компонент1
- ParamSnapshot в ngOnInit имеет красный цвет, например
- Маршрутизатор активирует Компонент2
- Маршрутизатор снова активирует Component1, но с другим параметром пути.
2 проблемы:
- ParamSnapshot является неизменным, поэтому новое значение будет недоступно
- 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‘>
должен загрузить маршрут / статус / онлайн.
Поскольку вы опускаете последнюю часть для второго выхода, не сразу понятно, как угловые будут обрабатывать маршруты.
Не используйте второй выход маршрутизатора только для навигации.
Это делает вещи более сложными, чем они должны быть.
Вам не нужно перемещаться по навигации отдельно от контента, или нет?
Если вам не нужна отдельная навигация, проще включить ваше меню в шаблон AppComponent напрямую.
Вторая розетка-маршрутизатор будет иметь смысл, если у вас есть что-то вроде боковой панели для чата, где вы можете выбрать контакты, окно чата и страницу настроек чата, при этом все они независимы от вашей основной стороны.
Тогда вам понадобятся два роутера.
Используйте либо базовый класс, либо угловой сервис
Вы предоставляете свою базовую страницу в качестве услуги для Angular через
1114 * *
Использование инъецируемой службы в качестве базового класса выглядит очень странным дизайном.
Я могу'Не думаю, что причина, я хотел бы использовать это.
Либо я использую BaseClass в качестве BaseClass, который работает независимо от угловых.
Или я использую угловой сервис и внедряю его в компоненты, которым требуется функциональность сервиса.
Примечание: Вы не можете просто создать службу, которая получает Router
и ActivatedRoute
, потому что она получит корневые объекты этих компонентов, которые пусты.
Angular генерирует свой собственный экземпляр ActivatedRoute
для каждого компонента и внедряет его в компонент.
Решение: см. «Лучшее решение: TokenComponent с Token Service» ниже.
Лучшее решение: TokenComponent с сервисом Token
Лучшее решение, о котором я могу подумать сейчас, - это создать 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}`));
Надеюсь, это поможет вам. Это было забавное испытание! :)