Маршрутизаторы с именованными розетками, которые активируются один раз
Возможно ли иметь маршрутизаторы с именованными выходами, которые активируются один раз, а затем никогда не уничтожаются, независимо от того, по какому маршруту осуществляется навигация в основной розетке?
Намерение состоит в том, чтобы иметь компоненты, которые сохраняются на странице (например, на боковой панели), но получают преимущества маршрутизации при их начальной загрузке, такие как средства защиты (распознаватели) и отложенная загрузка.
Требование заключается в том, что именованные выходы не должны негативно влиять на UX, например, путем введения суффиксов мусора в URL-адрес SPA, например (outletName:routeName)
они также не должны быть случайно деактивированы. Если есть способ отсоединить их от маршрутизатора после начальной активации, это будет целесообразно.
skipLocationChange
опция не может быть использована для этой цели. В этом примере /login(popup:compose)
URL появляется, когда Contact
а также Login
маршруты последовательно перемещаются.
3 ответа
Маршрутизатор нуждается в информации об именованных торговых точках, поэтому есть хорошие шансы на реализацию ваших собственных UrlSerializer
поможет.
Идея проста, процесс десериализации должен знать маршруты, которые имеют статические именованные выходы и производят UrlTree
который содержит названные выходы, т.е. для /login
URL должен производить то же самое UrlTree
по умолчанию сериализатор будет выдавать для URL /login(popup:compose)
, Во время сериализации статические именованные параметры выхода не должны включаться в результирующий URL.
Мы столкнулись с тем же (критическим) требованием UX в нашем проекте и придумали полу-чистое, но пока полностью функциональное решение.
Реализуйте индивидуальный LocationStrategy
, мы просто расширяем значение по умолчанию PathLocationStrategy
class и предварительно обработайте URL-адрес (который будет представлен пользователю / браузеру):
@Injectable()
export class OnlyPrimaryLocationStrategy extends PathLocationStrategy implements LocationStrategy {
static readonly AUX_ROUTE_SEPERATOR = '//';
replaceState(state: any, title: string, url: string, queryParams: string): void {
super.replaceState(state, title, this.preprocessUrl(url), queryParams);
}
pushState(state: any, title: string, url: string, queryParams: string): void {
super.pushState(state, title, this.preprocessUrl(url), queryParams);
}
preprocessUrl(url: string): string {
if (url.includes(OnlyPrimaryLocationStrategy.AUX_ROUTE_SEPERATOR)) {
if (url.split(OnlyPrimaryLocationStrategy.AUX_ROUTE_SEPERATOR).length > 2) {
throw new Error(
'Usage for more than one auxiliary route on the same level detected - please recheck imlementation'
);
}
return url.split(OnlyPrimaryLocationStrategy.AUX_ROUTE_SEPERATOR)[0].replace('(', '');
} else {
return url;
}
}
}
Не забудьте указать это в своем модуле:
providers: [
{
// ...
provide: LocationStrategy,
useClass: OnlyPrimaryLocationStrategy,
},
],
Обработка строк, очевидно, не на 100% чиста, но она выполняет свою работу за нас - может быть, она вам поможет. Имейте в виду, что ваш URL-адрес теперь не может полностью восстановить состояние вашего маршрутизатора (очевидно).
Похоже, что мы не можем использовать вторичный маршрут, который нацелен на именованную точку, без добавления вторичного маршрута к URL. Как указано в вашем вопросе, одним из возможных решений будет отсоединение компонента от розетки маршрутизатора после его активации. Моя попытка реализовать это решение показана в следующем стеке:
<router-outlet name="popup" #popupRouterOutlet (activate)="processActivate($event)"></router-outlet>
<ng-container #popupContainer></ng-container>
export class AppComponent {
@ViewChild("popupRouterOutlet", { read: ViewContainerRef }) private popupRouterOutlet: ViewContainerRef;
@ViewChild("popupContainer", { read: ViewContainerRef }) private popupContainer: ViewContainerRef;
constructor(public router: Router) {
}
processActivate(e) {
let viewRef = this.popupRouterOutlet.detach(0);
this.popupContainer.insert(viewRef);
this.router.navigate([".", { outlets: { popup: null } }]);
}
}
в activate
обработчик события, компонент отсоединен от розетки маршрутизатора, он вставлен в ng-container
и выход маршрутизатора очищен. Затем компонент может оставаться в DOM, больше не используя вторичный маршрут.
Статическое содержимое компонента успешно передано, но, к сожалению, нет привязок. Об этой проблеме уже сообщалось. В Angular Github в выпуске 20824 был сделан запрос на перемещение компонента из одного контейнера в другой. До тех пор, пока не будет реализован этот запрос, передача такого рода невозможна.
skipLocationChange
опция навигации работает только для маршрутизатора, для которого она была предоставлена, затем в URL появляется именованная розетка, например /login(foo:bar)
,
Можно иметь постоянный foo
выход маршрутизатора путем переопределения UrlSerializer
, как было предложено @kemsky:
import {
UrlSerializer, DefaultUrlSerializer, UrlSegmentGroup, UrlTree
} from '@angular/router';
export class FooUrlSerializer extends DefaultUrlSerializer {
serialize(tree) {
const { foo, ...noFooChildren } = tree.root.children;
const root = new UrlSegmentGroup(tree.root.segments, noFooChildren);
const noFooTree = Object.assign(new UrlTree(), tree, { root });
return super.serialize(noFooTree);
}
}
...
providers: [{ provide: UrlSerializer, useClass: FooUrlSerializer }, ...]
...