【问题标题】:Angular 2 How to detect back button press using router and location.go()?Angular 2如何使用路由器和location.go()检测后退按钮按下?
【发布时间】:2016-08-24 20:44:47
【问题描述】:

我已经构建了一个应用程序,它使用router 3.0.0-beta.1 在应用程序部分之间切换。我还使用location.go() 来模拟同一页面的子部分之间的切换。我使用了<base href="/"> 和一些 URL 重写规则,以便在页面刷新的情况下将所有路由重定向到 index.html。这允许路由器接收请求的小节作为 URL 参数。基本上我已经设法避免使用HashLocationStrategy

routes.ts

export const routes: RouterConfig = [
    {
        path: '',
        redirectTo: '/catalog',
        pathMatch: 'full'
    },
    {
        path: 'catalog',
        component: CatalogComponent
    },
    {
        path: 'catalog/:topCategory',
        component: CatalogComponent
    },
    {
        path: 'summary',
        component: SummaryComponent
    }
];

如果我点击导航栏中的一个小节,会发生 2 件事:

  • logation.go() 使用必要的字符串更新 URL 以指示当前小节
  • 自定义的scrollTo() 动画在请求的小节顶部滚动页面。

如果我刷新页面,我将使用先前定义的路线并提取必要的参数以恢复滚动到请求的小节。

this._activatedRoute.params
    .map(params => params['topCategory'])
    .subscribe(topCategory => {
        if (typeof topCategory !== 'undefined' &&
            topCategory !== null
        ) {
            self.UiState.startArrowWasDismised = true;
            self.UiState.selectedTopCategory = topCategory;
        }
    });

除了单击后退按钮外,一切正常。如果上一页是不同的部分,则应用路由器的行为与预期相同。但是,如果上一个页面/url 是一个小节,则 url 将更改为上一个,但 UI 中没有任何反应。如何检测是否按下了后退按钮以调用 scrollTo() 函数再次完成它的工作?

我看到的大多数答案都依赖于事件onhashchange,但这个事件并没有在我的应用程序中被触发,因为毕竟我的 URL 中没有哈希......

【问题讨论】:

标签: javascript angular angular-ui-router


【解决方案1】:

我不知道其他答案是否过时,但它们在 Angular 7 中对我来说都不是很好。我所做的是通过将 Angular 事件侦听器导入到我的组件中来添加它:

import { HostListener } from '@angular/core';

然后在window 对象上监听popstate(正如Adrian 推荐的那样):

  @HostListener('window:popstate', ['$event'])
  onPopState(event) {
    console.log('Back button pressed');
  }

这对我有用。

【讨论】:

  • 如果我使用移动设备并下拉屏幕并刷新页面,这会起作用吗?
  • 如 MDN 所述:“每次活动历史条目在同一文档的两个历史条目之间发生变化时,都会向窗口发送一个 popstate 事件”。这意味着当用户点击“后退”和“前进”时将触发该事件
  • @indrajeet /* 处理标签页关闭/刷新事件 / @HostListener('window:beforeunload', ['$event']) unloadNotification($event: any) { if (pendingTask ()) { / 实现你的逻辑,要么你有任何待处理的任务,要么没有 */ return false; } 否则 { 返回错误; } }
  • “最新”Angular 版本对多年后提出问题的人没有帮助。希望这能说明您使用的实际版本。
  • @SeanHalls 我们当时在 7 岁。我会更新答案。
【解决方案2】:

解决此问题的另一种方法是订阅Angular Router service 发出的事件。由于我们正在处理路由,因此在我看来使用路由器事件更有意义。

constructor(router: Router) {
    router.events
      .subscribe((event: NavigationStart) => {
        if (event.navigationTrigger === 'popstate') {
          // Perform actions
        }
      });
}

我想注意popstate 在浏览器上按向后和向前时会发生。因此,为了有效地执行此操作,您必须找到一种方法来确定正在发生的情况。对我来说,那只是使用 NavigationStart 类型的 event 对象,它提供有关用户来自哪里以及他们要去哪里的信息。

【讨论】:

  • 欣赏转向需要进一步的逻辑来确定前进和后退
  • 这个答案看起来提供了一个更好的解决方案
  • 如果您使用此解决方案,您还必须取消订阅 router.events observable。否则,一旦您使用此代码访问页面,“执行操作”将继续为每个 popstate 操作应用程序执行。
【解决方案3】:

检测浏览器后退按钮点击 从 '@angular/common 导入 platformlocation 并将以下代码放入您的构造函数中:

 constructor(location: PlatformLocation) {
     location.onPopState(() => {
        alert(window.location);
     }); }

【讨论】:

  • 根据 Angular 的官方文档,PlatformLocation 不应该被应用程序开发者直接使用。
【解决方案4】:

Angular 文档直接在 PlatformLocation 类中声明...

  • 应用程序开发人员不应直接使用此类。

我在构造函数中使用了 LocationStrategy

constructor(location: LocationStrategy) {
  location.onPopState(() => {
    alert(window.location);
  });
}

【讨论】:

  • 如何停止收听事件或取消订阅此事件。我尝试使用 fromEvent(window, 'popstate') 并在那里取消订阅,但它没有用。
  • 你可以做 location.onPopState = () => {}
【解决方案5】:

一个非常干净的方法是从 rxjs 导入 'fromEvent' 并以这种方式使用它。

fromEvent(window, 'popstate')
  .subscribe((e) => {
    console.log(e, 'back button');
  });

【讨论】:

  • 我采用了这个解决方案,确实更干净。我定义了一个订阅,然后在ngOnInit 中执行subscribe,在ngOnDestroy 中执行unsubscribe
【解决方案6】:

这是 Angular 13 的最新更新

你必须先从角度路由器导入 NavigationStart

import { NavigationStart, Router } from '@angular/router';

然后在构造函数中加入如下代码

constructor(private router: Router) {
  router.events.forEach((event) => {
    if(event instanceof NavigationStart) {
      if (event.navigationTrigger === 'popstate') {
        /* Do something here */
      }
    }
  });
}

【讨论】:

    【解决方案7】:

    使用onpopstate 事件成功了:

    window.addEventListener('popstate',
        // Add your callback here
        () => self.events.scrollToTopCategory.emit({ categId: self.state.selectedTopCategory })
    );
    

    【讨论】:

      【解决方案8】:

      我同意 Adrian Moisa 的回答,

      但是您可以通过注入到您的组件或服务中使用 PlatformLocation 类来使用“更多 Angular 2 方式”,然后您可以通过这种方式定义 onPopState 回调:

      this.location.onPopState(()=>{
        // your code here...
        this.logger.debug('onpopstate event');
      });
      

      【讨论】:

      • onPopState 的问题是即使用户往前走也会引发事件。
      • 这会为window:popstate 事件创建一个全局侦听器,而使用@HostListener 只会在当前组件上侦听。
      • PlatformLocation: "这个类不应该被应用程序开发者直接使用。"
      【解决方案9】:

      更简单的方法 - Link

      import { PlatformLocation } from '@angular/common';
      import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
      ...
      
      constructor(
      private platformLocation: PlatformLocation ,
      private modalService: NgbModal
      )
      {
      platformLocation.onPopState(() => this.modalService.dismissAll());
      }
      

      【讨论】:

        【解决方案10】:

        老实说,我不知道您的用例。而且线程很旧。但这是谷歌的第一次成功。如果其他人正在使用 Angular 2 和 ui-router 寻找这个(就像你正在使用的那样)。

        从技术上讲,它没有检测到后退按钮。它更多的是检测您作为开发人员是否触发了状态更改或用户是否自己更新了 URL。

        您可以为状态更改添加自定义选项,这可以通过 uiSref 和 stateService.go 完成。在您的过渡中,您可以检查是否设置了此选项。 (不会在点击后退按钮等时设置)。

        使用 ui-sref

            <a uiSref="destination-name" [uiOptions]="{custom: {viaSref: true}}">Bar</a>
        

        使用 state.go

        import {StateService} from '@uirouter/core';
        
        ...
        
        @Component(...)
        export class MyComponent {
        
            constructor(
                private stateService: StateService
            ) {}
        
            public myAction(): void {
                this.stateService.go('destination-name', {}, {custom: {viaGo: true}});
            }
        }
        

        您可以在任何转换钩子中检测到它,例如 onSucces!

        import {Transition, TransitionOptions, TransitionService} from '@uirouter/core';
        
        ...
        
        @Component(...)
        export class MyComponent implements OnInit {
        
            constructor(
                private transitionService: TransitionService
            ) {}
        
            public ngOnInit(): void {
                this.transitionService.onSuccess({}, (transition: Transition) => this.handleTransition(Transition));
            }
        
            private handleTransition(transition: Transition): void {
                let transitionOptions: TransitionOptions = transition.options();
                if (transitionOptions.custom?.viaSref) {
                    console.log('viaSref!');
                    return;
                }
                if (transitionOptions.custom?.viaGo) {
                    console.log('viaGo!');
                    return;
                }
                console.log('User transition?');
            }
        }
        

        【讨论】:

          【解决方案11】:

          可以查看“window.history”的大小,如果大小为1则不能返回。

          isCanGoBack(){
              window.history.length > 1;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-05-01
            • 1970-01-01
            • 1970-01-01
            • 2021-07-26
            • 1970-01-01
            • 2013-05-10
            • 1970-01-01
            相关资源
            最近更新 更多