Angular2路由和中间取消Observables

时间:2017-05-04 21:24:42

标签: angular rxjs

我有一个Angular Http get请求,它会根据返回的数据将路由导航到三个路由之一。示例代码如下:

SomeComponent:

constructor(
   private someService: SomeService,
   private router: Router
) { }

ngOnInit() {
   this.someService.getSomeData().subscribe(
      () => {
         console.log("getSomeData() did not route to a new link! Routing to a 3rd link");
         this.router.navigate(['home']);
      },
      () => {
         console.log("Some error occurred.");
      });

SomeService:

constructor(
   private router: Router,
   private someOtherService: SomeOtherService
) { }

getSomeData() {
   return this.someOtherService.getSomeOtherData().map(data => {
      if (data === 'someValue') {
         console.log("The data was 'someValue'");
         this.router.navigate(['route1']);
      }
      else if (data == 'someOtherValue') {
         console.log("The data was 'someOtherValue'");
         this.router.navigate(['route2']);
      }
   });
}

SomeOtherService:

constructor(
   private http: Http
) { }

getSomeOtherData() {
   return this.http.get(this.getDataUrl) {
      .map((res: Response) => {
         console.log("Sending over the data back to SomeService");
         let body = res.json();
         return body.data;
      })
      .catch((err: Response) => {
         console.log(err);
         return Observable.throw(err);
      });
   }
}

预期的行为是,在从SomeOtherService接收数据后,路由器将导航到route1route2,具体取决于数据(我认为我在哪里读取会停止Observable流?也许我误读了)。如果数据不匹配,则Observable流继续到SomeComponent,然后导航到home

实际行为是路由器最初将路由到route1route2,但由于流继续,路由器最终会路由到home

所以我的问题是,如果您订阅了一个rxjs Observable,有没有办法取消/取消订阅Observable中流?我认为导航到另一条路线会取消观察,但这对我来说似乎不起作用。

我尝试在map方法中取消订阅有趣(坏)的结果。我也试过看其他的Observable方法但是对rxjs不熟悉我不确定哪种方法最合适(我真的只熟悉mapcatch,{{ 1}},此时mapTo)。

2 个答案:

答案 0 :(得分:2)

  

预期的行为是,在从SomeOtherService接收数据后,路由器将导航到route1或route2,具体取决于数据(我认为我在哪里读取会停止Observable流?也许我误读了)。如果数据不匹配,则Observable流继续到SomeComponent,然后导航到home。

不完全: - )

导航到另一个视图会完成旧Observables提供的所有ActivatedRoute。您创建的Observables不是从ActivatedRoute获取的(也不是通过其他方式绑定到ActivatedRoute),因此将独立于哪条路线处于活动状态。

此外,router.navigate(...)是一个异步操作,这意味着它只安排导航,但不等待导航完成。也就是说,即使您的ObservablesActivatedRoute绑定,它们仍然存在,并且会通知观察员。

您可以使用以下方法解决此问题:

constructor(
   private router: Router,
   private someOtherService: SomeOtherService
) { }

getSomeData() {
   return this.someOtherService.getSomeOtherData().map(data => {
      if (data === 'someValue') {
         console.log("The data was 'someValue'");
         return ['route1'];
      }
      else if (data == 'someOtherValue') {
         console.log("The data was 'someOtherValue'");
         return ['route2'];
      } else {
         return null;
      }
   });
}

并使用它:

this.someService.getSomeData().subscribe({
  next: (route) => {
     this.router.navigate(route || ['home']);
  },
  error: (err) => {
     console.log("Some error occurred.", err);
  }
});

顺便说一句,为了更好地了解Observable是什么,您可能需要阅读ReactiveX documentation

答案 1 :(得分:1)

有多种方法可以取消订阅可观察的

最直接的方法是保留订阅句柄并取消订阅,例如在OnDestroy方法中。

import { Subscription } from 'rxjs/Rx'

...

private sub: Subscription;

constructor(
   private someService: SomeService,
   private router: Router
) { }

ngOnInit() {
   this.sub = this.someService.getSomeData().subscribe(..)
}

ngOnDestroy() {
    if (this.sub) this.sub.unsubscribe();
}

另外在map()方法中导航有点奇怪。 map()运算符用于将可观察流转换为另一个流,它不是端点。通常,您会返回someObs.map(s => s.name).subscribe()之类的内容。如果您只想做某事,可以使用do()运算符。

但在你的情况下,如果数据===' someValue'您将首先导航到地图运算符中的route1。然后触发订阅回调(next())并且路由器导航到' home'。

要获得您描述的功能,您可以这样做:

SomeComponent:

constructor(
   private someService: SomeService,
   private router: Router
) { }

ngOnInit() {
   this.someService.getSomeData().subscribe({
      next: () => {
         console.log("getSomeData() did not route to a new link! Routing to a 3rd link");
         this.router.navigate(['home']);
      },
      complete: () => {},
      error: (err) => {
         console.log("Some error occurred.", err);
      }
    });

SomeService:

constructor(
   private router: Router,
   private someOtherService: SomeOtherService
) { }

getSomeData() {
   return Observable.create(observer => {
    this.someOtherService.getSomeOtherData().first().subscribe(data => {
      if (data === 'someValue') {
         console.log("The data was 'someValue'");
         this.router.navigate(['route1']);
      }
      else if (data == 'someOtherValue') {
         console.log("The data was 'someOtherValue'");
         this.router.navigate(['route2']);    
      } else {
         // none of the options above. call next() on the observer so 
         // it will route to 'home'
         observer.next();
      }
      observer.complete();

   });
});


}