【问题标题】:Rxjs Nested observer is not executedRxjs 嵌套观察者未执行
【发布时间】:2021-05-02 18:42:15
【问题描述】:

我已经阅读了很多关于如何在 RxJs 和 Angular 中嵌套观察者的文档、文章和不同的线程,但我仍然遗漏了一些东西并且最终无法得到结果。

这是我的代码:

page.ts

export class LiabilitiesPage implements OnInit {
     constructor(
        private liabilityService: LiabilityService,
        private router: Router
     ) {}

     refreshLiabilities() {
      // Get the liabilities
      console.log('refreshing') // passing there
      this.liabilityService.getAllLiabilities().subscribe(
      (response: Liability[]) => {
        console.log(response); // <=== Never pass there !

        if (response) {
          this.liabilities = response;
        } else {
          // empty response code
        }
      }, error => {
        // response error code (never passing there either)
      }
  }
}

liability.service.ts

// all the needed imports

@Injectable({
  providedIn: 'root'
})
export class LiabilityService {
  constructor(
    private authService: AuthService,
    private http: HttpClient,
    ) {}

  // first try : Do not send the http request
  getAllLiabilities(): Observable<Liability[]> {
    return this.authService.getOptions()
        .pipe(
            tap(options => this.http.get<Liability[]>(this.url + 'me/', options))
        );
  }

    // try 2 : Doesn't work either
    getAllLiabilities(): Observable<Liability[]> {
      return this.authService.getOptions()
        .pipe(
            switchMap(options => this.http.get<Liability[]>(this.url + 'me/', options)), // at this point I tried pretty much every operators (map, mergeMap etc.)
            withLatestFrom()
        ); 
  }
    /* this code was working before that I transformed the authService.getOptions in observable (it was just returning the options synchronyously before)
getAllLiabilities(): Observable<Liability[]> {
  return this.http.get<Liability[]>(this.url + 'me/', this.authService.getOptions());
  }*/
}

auth.service.ts


public getOptions(): Observable<any> {
      return new Observable((observer) => {
          this.storage.get('authToken').then((token) => {
              console.log('passing') // Pass here
              if (token && typeof token.auth_token !== 'undefined') {
                  console.log('passing') // pass here as well
                  this.isLoggedIn = true;
                  this.token = token.auth_token;
              }
              // it is returning the value
              return {
                  headers: this.headers.set('Authorization', 'Bearer ' + this.token),
                  params: new HttpParams()
              };
          })
      });
    }

我尝试了几乎所有可能的运算符组合以使其在责任服务中工作但没有任何成功。

问题:

问题是我的 page.ts 订阅了 this.http.get&lt;Liability[]&gt;(this.url + 'me/', options) 观察者,但没有触发任何 xhr 请求。 http get 观察者永远不会被执行,我不明白我在那里缺少什么。

我刚刚开始尝试 Angular,但如果我理解正确,操作员应该进行映射和展平,但这看起来永远不会发生。

奖金问题:

我也不明白为什么初始代码:

return this.http.get<Liability[]>(this.url + 'me/', this.authService.getOptions());

正在返回Observable&lt;Liability[]&gt;

并使用 switchMap :

switchMap(options => this.http.get<Liability[]>(this.url + 'me/', options))

它返回一个Observable&lt;HttpEvent&lt;Liability[]&gt;&gt;

如果有人有线索并有时间回答我,那就太棒了

【问题讨论】:

    标签: javascript angular typescript rxjs observers


    【解决方案1】:

    你的promise回调then()有问题:

    this.storage.get('authToken').then((token) => {
        return something; // this won't work.
    })
    

    相反,您可以使用from,它将convert 您对可观察对象的承诺。

    import { from, Observable } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    public getOptions(): Observable<any> {
        return from(this.storage.get('authToken')).pipe(map(token => {
            return headers with token.
        }));
    }
    

    所以你可以像这样重写你的代码:

    认证服务:

    private token: string | null = null;
    
    
    public getOptions(): Observable<any> {
      return this.getToken().pipe(
        map(token => {
          return {
            headers: this.headers.set('Authorization', 'Bearer ' + token),
            params: new HttpParams()
          };
        })
      );
    }
    
    
    private getToken(): Observable<string | null> {
      if (this.token) {
        return of(this.token);
      }
    
      return from(this.storage.get('authToken')).pipe(
        map(token => token?.authToken || null),
        tap(token => this.token = token)
      );
    }
    

    然后你可以使用 switchmap:

    getAllLiabilities(): Observable<Liability[]> {
      return this.authService.getOptions().pipe(
        switchMap(options => this.http.get<Liability[]>(this.url + 'me/', options))
      );
    }
    

    更新

    获取HttpEvent&lt;T&gt; 的原因是,当.get() 的重载接收到any 对象时,它会将http 事件处理完全交给您。 如果您希望它返回提供的元素类型,则必须满足适当的重载。 你可以这样做:

    我们只返回标题,而不是返回整个选项,这应该足够了,因为对于其余的选项我们真的没有足够的发言权。

    身份验证服务

    private token: string | null = null;
    
    public createTokenHeaders(): Observable<HttpHeaders> {
      const headers = new HttpHeaders();
      return addToken(headers);
    }
    
    public addToken(headers: HttpHeaders): Observable<HttpHeaders> {
      return this.getToken().pipe(
        map(token => headers.set('Authorization', 'Bearer ' + (token || '')))
      );
    }
    
    private getToken(): Observable<string | null> {
      if (this.token) {
        return of(this.token);
      }
    
      return from(this.storage.get('authToken')).pipe(
        map(token => token?.authToken || null),
        tap(token => this.token = token)
      );
    }
    

    然后像这样使用它:

    getAllLiabilities(): Observable<Liability[]> {
      const url = this.url + 'me/';
      const headers = new HttpHeaders();
      return this.authService.addToken(headers).pipe(
        switchMap(updatedHeaders => this.http.get<Liability[]>(url, { headers: updatedHeaders }))
      );
    }
    

    或:

    getAllLiabilities(): Observable<Liability[]> {
      const url = this.url + 'me/';
      return this.authService.createTokenHeaders().pipe(
        switchMap(headers => this.http.get<Liability[]>(url, { headers }))
      );
    }
    

    注意:确保使用从调用 addToken 返回的标头。重用您自己实例化的headers 将不起作用,因为设置标头总是返回一个新的HttpHeaders 对象。它是不可变的。

    StackBlitz Example

    【讨论】:

    • 这是一个很棒的答案!谢谢@Silverrmind。您的解决方案有效,但奖金问题仍然存在。 TSLint 突出显示服务函数返回,因为它认为返回类型是HTTPEvent 上的可观察值。我们就是我的奖金问题。我们可以避免这种行为吗? (另一种改变 getAllLibalities 返回类型的方法)
    • @JulienBourdic 我已经更新了我的答案。也许这很清楚。
    • 很抱歉这么晚才回复您。我想再次感谢您抽出宝贵的时间以及您对工作原理的清晰解释!希望你的回答能帮助很多人。 (我已经编辑了您的答案,只为需要扩展它的页面强制创建标题并修复一些方法可见性问题)
    • @JulienBourdic 不客气。我接受了修饰符 -> public 的编辑,但我拒绝了可选参数。对于这种情况,我建议创建一个名为createTokenHeaders(): Observable&lt;HttpHeaders&gt; 的额外方法。这可以使用新实例化的 headers 对象调用 addTokenHeader。另外我建议做空检查而不是未定义,因为空检查会同时捕获未定义和空。
    • 是的,这就是我的观点(对于类型检查),我将始终尝试使用强类型来控制所有内容的运行方式并避免不可调试的副作用。这只是一种习惯,采用这种方法有利有弊:)。感谢这个版本(你的想法真的很优雅),这是一次非常有启发性和有趣的对话。我很高兴创建了这个主题并感谢你加入它:D
    猜你喜欢
    • 1970-01-01
    • 2022-11-25
    • 1970-01-01
    • 2020-08-28
    • 2019-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多