Маршрутизаторы с именованными розетками, которые активируются один раз

Возможно ли иметь маршрутизаторы с именованными выходами, которые активируются один раз, а затем никогда не уничтожаются, независимо от того, по какому маршруту осуществляется навигация в основной розетке?

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

Требование заключается в том, что именованные выходы не должны негативно влиять на 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 }, ...]
...
Другие вопросы по тегам