简体   繁体   English

Rxjs 嵌套观察者未执行

[英]Rxjs Nested observer is not executed

I've read a lot of documentation, articles and different thread about how to nest observers in RxJs and Angular, I still missing something and not able to get a result at the end.我已经阅读了很多关于如何在 RxJs 和 Angular 中嵌套观察者的文档、文章和不同的线程,但我仍然遗漏了一些东西并且最终无法得到结果。

Here is my code:这是我的代码:

page.ts页面.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 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授权服务.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()
              };
          })
      });
    }

I tried almost all the possible operator combinations to make it works in the liabilityService without any success.我尝试了几乎所有可能的运算符组合以使其在 liabilityService 中工作但没有成功。

Problem:问题:

The problem is that my page.ts subscribes to the this.http.get<Liability[]>(this.url + 'me/', options) observer but none xhr request is fired.问题是我的page.ts订阅了this.http.get<Liability[]>(this.url + 'me/', options)观察者,但没有触发 xhr 请求。 The http get observer is never executed and I don't understand what I'm missing there. http get observer 永远不会执行,我不明白我在那里遗漏了什么。

I'm just starting experimenting Angular, but if I understood correctly the operators should do the mapping and flattening but this looks to never happen.我刚刚开始试验 Angular,但如果我理解正确,操作员应该进行映射和展平,但这看起来永远不会发生。

Bonus question:奖金问题:

I'm not catching either why the initial code:我不明白为什么初始代码:

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

is returning an Observable<Liability[]>正在返回一个Observable<Liability[]>

and with the switchMap:并使用 switchMap:

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

It is returning a Observable<HttpEvent<Liability[]>>它返回一个Observable<HttpEvent<Liability[]>>

If somebody has a clue and the time to answer me on that, it would be amazing如果有人有线索并有时间回答我,那就太棒了

You have a problem in the promise callback then() :您在 promise 回调then()中遇到问题:

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

instead you can use from , which will convert your promise to an observable.相反,您可以使用from ,它将您的 promise 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.
    }));
}

So you could rewrite your code like this:所以你可以像这样重写你的代码:

auth service:授权服务:

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)
  );
}

then you can use a switchmap:然后你可以使用开关图:

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

Update更新

The reason for getting HttpEvent<T> is because when the overload of .get() receives an any object it leaves the http event handling entirely up to you.获取HttpEvent<T>的原因是因为当.get()的重载接收到any object 时,它会将 http 事件处理完全交给您。 If you want it to return the provided element type, you have to satisfy the proper overload.如果你想让它返回提供的元素类型,你必须满足适当的重载。 You can achieve that doing it like so:你可以这样做:

Instead of returning the entire options, we only return the headers, which should be enough, because we do not really have enough to say about the rest of the options.我们没有返回整个选项,而是只返回标头,这应该足够了,因为我们真的没有足够的东西来说明选项的 rest。

auth service授权服务

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)
  );
}

Then use it like so:然后像这样使用它:

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 }))
  );
}

or:要么:

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

Note: Make sure you use the headers returned from the call to addToken.注意:确保使用从调用 addToken 返回的标头。 Reusing your own instantiated headers will not work because setting a header always returns a new HttpHeaders object. It is immutable.重用您自己的实例化headers将不起作用,因为设置 header 总是会返回一个新的HttpHeaders object。它是不可变的。

StackBlitz Example StackBlitz 示例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM