【问题标题】:Router named outlets that are activated once激活一次的路由器命名出口
【发布时间】:2018-07-27 22:04:24
【问题描述】:

是否有可能让路由器命名的 outlets 被激活一次,然后永远不会被销毁,无论在主 outlet 中导航什么路由?

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

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

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

【问题讨论】:

  • 你可以创建二级网点
  • @pixelbits 是的,就是这样。但是它们应该如何定义呢?我相信这个问题足够具体,不会被认为“太宽泛”,并且可能的质量答案数量有限。我没有提供任何代码,因为在这种情况下它是通用的。在this example from the guide 中可以看到,在导航主要出口时,二级popup 出口仍然存在,但它会用(popup:compose) 污染URL。
  • 导航时指定skipLocationChange: true
  • @pixelbits 但这仅适用于辅助插座本身的单一导航。见stackblitz.com/edit/angular-kfuscp。单击Contact 链接时,不会更改url,但单击Login 时会出现(popup:compose)。覆盖这里stackoverflow.com/questions/43643780/…
  • 我试了一下,希望组件激活后可以移动到另一个容器中(见this stackblitz)。起初它看起来好像可以工作,但事实并非如此:绑定在此过程中丢失了。一旦issue 20824 中请求的功能到位,这可能是您的解决方案。

标签: angular angular2-routing angular4-router


【解决方案1】:

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

想法很简单,反序列化过程应该知道具有静态命名出口的路由并生成包含命名出口的UrlTree,即/login url 应该生成相同的UrlTree,因为默认序列化程序将为url @987654325 生成@。在序列化过程中,静态命名的出口参数不应包含在结果 url 中。

【讨论】:

  • 这是个好主意,谢谢。我不确定是否有比我更好的方法来重建 url 树,但它对我有用。
【解决方案2】:

如果没有将辅助路由附加到 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 中,不再使用辅助路由。

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

【讨论】:

    【解决方案3】:

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

    实现一个自定义LocationStrategy,我们只需扩展默认的PathLocationStrategy 类并预处理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 现在不能完全重建您的路由器状态(显然)。

    【讨论】:

      【解决方案4】:

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

      正如@kemsky 所建议的那样,可以通过覆盖UrlSerializer 来获得永久的foo 路由器出口:

      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 }, ...]
      ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-12-29
        • 2022-08-17
        • 2017-11-11
        • 1970-01-01
        • 2019-05-17
        • 2023-04-08
        • 2018-02-10
        • 2017-12-07
        相关资源
        最近更新 更多