激活一次的路由器命名插座

Router named outlets that are activated once

是否可以让名为 outlets 的路由器无论在主 outlet 中导航到什么路由,只要激活一次就永远不会被销毁?

目的是让组件持久存在于页面上(例如,侧边栏),但在初始加载时获得路由的好处 - 例如守卫(解析器)和惰性加载中。

要求是命名插座不应以任何负面方式影响用户体验,例如通过向 SPA URL 引入垃圾后缀,例如(outletName:routeName),它们也不应该被意外停用。如果有办法在初始激活后将它们与路由器分离,那将是合适的。

skipLocationChange 选项不能用于此目的。在this example /login(popup:compose) URL 中,当 ContactLogin 路线依次导航时出现。

看来我们不能使用以指定出口为目标的辅助路由,而无需将辅助路由附加到 URL。正如您的问题所建议的那样,一种可能的解决方案是在组件被激活后将其从路由器插座上拆下。 this stackblitz:

中显示了我实施该解决方案的尝试
<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,而不再使用辅助路由。

组件的静态内容已成功传输,但不幸的是,绑定没有。已经报告了该问题。已在 issue 20824 中的 Angular Github 上提出请求,以允许将组件从一个容器移动到另一个容器。在实现该功能请求之前,这种传输似乎是不可能的。

路由器需要有关命名插座的信息,因此实施您自己的 UrlSerializer 很有可能会有所帮助。

想法很简单,反序列化过程应该知道具有静态命名出口的路由并生成包含命名出口的 UrlTree/login url 应该产生相同的 UrlTree 作为默认序列化程序将为 url /login(popup:compose) 生成。在序列化期间,静态命名出口参数不应包含在结果 url.

skipLocationChange 导航选项仅适用于为其提供的路由器,然后命名插座出现在 url 中,如 /login(foo:bar).

可以通过覆盖 UrlSerializer 来获得永久 foo 路由器出口,正如@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 }, ...]
...

我们在项目中遇到了相同的(关键的)用户体验需求,并提出了一个半干净但到目前​​为止功能齐全的解决方案。

实现自定义 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 现在还不能完全重建您的路由器状态(很明显)。