HttpClient問題:http.get需要第二次才會取得到結果?


#1

正在找時間邊學習Angular,之前主要都是後端為主,太笨所以在學習真的有障礙,尤其是關係到RxJS,目前是試著做登入動作,可是第一次點登入時都是Undefined…第二次點了之後才會傳回true。不知道是為什麼,找了一些討論也沒有方向,故來請教各位先進,希不吝教導。其它感覺比較不重要的就不貼上來,相關程式碼如下:(因之前找原因有亂試一些,雖註解起來但刪掉,所以有點亂抱歉!!~還有發文格式~~有待學習sorry)

login.component.ts

    import { Component, OnInit, EventEmitter, Output } from '@angular/core';
    import { FormBuilder, FormGroup, Validators, FormControl } from "@angular/forms";
    import { ActivatedRoute, Router } from '@angular/router';
    import { AuthenticationService } from 'src/app/_services/authentication.service';

    import { faCoffee } from '@fortawesome/free-solid-svg-icons';


    @Component({
      selector: 'app-login',
      templateUrl: './login.component.html',
      styleUrls: ['./login.component.scss']
    })
    export class LoginComponent implements OnInit {
      loginForm: FormGroup;
      loading = false;
      submitted = false;
      returnUrl: string;
      myIcon = faCoffee;
      error: any;

      constructor(
        private formBuilder: FormBuilder,
        private route: ActivatedRoute,
        private router: Router
        ,private authenticationService: AuthenticationService  ) {}


      ngOnInit() {
        this.loginForm = new FormGroup({
          username: new FormControl('', Validators.required),
          password: new FormControl('', Validators.required)
        });
      }

      get f(): any { return this.loginForm.controls; }

      onSubmit() {
        this.submitted = true;

        if (this.loginForm.invalid) {
          return;
        }

        this.loading = true;

        let result = this.authenticationService.login(this.f.username.value, this.f.password.value);

        if (result === "true") {
          this.router.navigate(['navigation']);
        }
        else {
          console.log('登入失敗!');
          this.loading = false;
        }
      }
    }

authentication.service.ts:

    import { Injectable } from '@angular/core';
    import { HttpClient, HttpParams } from "@angular/common/http";
    import { APIBase } from "./apibase";
    import { Router } from '@angular/router';
    import { retry } from 'rxjs/operators';

    @Injectable({
      providedIn: 'root'
    })
    export class AuthenticationService {
      apiBase = new APIBase();
      result: string = '';
      loginResult: string = '';

      constructor(private http: HttpClient, private router: Router, private alertservice: AlertService) { }

      login(empno: number, emppassword: string) {
        let url=this.apiBase.serverUrl + '/api/Hospital/getLoginResult';

        const params = new HttpParams()
        .set('empNo', empno.toString()).set('emppassword',emppassword);

    // **trace時,第一次click submit這邊就空值,第二次再按時~就可以正常取到true....**
        let result = this.http.get(url, {params})
        .pipe(first())
        .subscribe(
          (res:Response) => {
            this.loginResult = res.toString();
          }
        );

        return loginResult;
      }

      logout() {
        // remove user from local storage to log user out
        localStorage.removeItem('currentUser');
      }

    }

目前環境版本參考如下:
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.8.6
@angular-devkit/build-angular 0.8.6
@angular-devkit/build-optimizer 0.8.6
@angular-devkit/build-webpack 0.8.6
@angular-devkit/core 0.8.6
@angular-devkit/schematics 0.8.6
@angular/cdk 7.0.3
@angular/cli 6.2.6
@angular/material 7.0.3
@ngtools/webpack 6.2.6
@schematics/angular 0.8.6
@schematics/update 0.8.6
rxjs 6.2.2
typescript 2.9.2
webpack 4.23.1


#2

請提供 login 頁面相關的程式碼,這樣子看不出問題在哪裡


#3
 login(empno: number, emppassword: string) {
        let url=this.apiBase.serverUrl + '/api/Hospital/getLoginResult';

        const params = new HttpParams()
        .set('empNo', empno.toString()).set('emppassword',emppassword);

    // **trace時,第一次click submit這邊就空值,第二次再按時~就可以正常取到true....**
        let result = this.http.get(url, {params})
        .pipe(first())
        .subscribe(
          (res:Response) => {
            this.loginResult = res.toString();
          }
        );

點出幾個奇怪的地方,

  1. authenticationService.login 沒有回傳任何東西,這裡的 result 不會有資料
 onSubmit() {
        this.submitted = true;

        if (this.loginForm.invalid) {
          return;
        }

        this.loading = true;

        let result = this.authenticationService.login(this.f.username.value, this.f.password.value);

        if (result === "true") {
          this.router.navigate(['navigation']);
        }
        else {
          console.log('登入失敗!');
          this.loading = false;
        }
      }
    }
  1. 即使 login 有 return 因為是非同步,也無法正常地將結果回傳
  2. 如果是直接回傳 http.get 的 subscribe,那也會是 subscription
  3. httpClient 預設回傳的結果是 response 的 body 部份 (預設回傳格式為 json)

所以,以下改法方向你參考看看

 login(empno: number, emppassword: string) {
        let url=this.apiBase.serverUrl + '/api/Hospital/getLoginResult';

        const params = new HttpParams()
        .set('empNo', empno.toString()).set('emppassword',emppassword);

       // 這裡還是很奇怪,要看你 api 回傳的結果,因為 httpClient 預設接受的結果是 res body 的內容
        return this.http.get(url, {params})
        .pipe(first(), tap((res:Response) => {
            this.loginResult = res.toString();
          });
onSubmit() {
        this.submitted = true;

        if (this.loginForm.invalid) {
          return;
        }

        this.loading = true;

     this.authenticationService.login(this.f.username.value, this.f.password.value)
         .subscribe(result=>{
           if (result === "true") {
              this.router.navigate(['navigation']);
           } 
           else {
              console.log('登入失敗!');
              this.loading = false;
           }
       });
    }

#4

抱歉刪註解把return LoginResult; 刪掉了。
在看文章時,以前有用@angular/http,而現在改成@angular/common/http,感覺取資料好像有不同方式,
還是說本來就有很多方式,RxJS學習曲線對我來說真的有點困難~~太笨了。

抱歉我的混亂程式讓你的回答也亂了~下面那個若使用tap會有問題應該是你說的格式問題。
// 這裡還是很奇怪,要看你 api 回傳的結果,因為 httpClient 預設接受的結果是 res body 的內容
return this.http.get(url, {params})
.pipe(first(), tap((res:Response) => {
this.loginResult = res.toString();
});

後來試一下,以下調整,即可正常:
return this.http.get(url, {params})
.pipe(tap((res) => {
console.log(res);
}));

login那邊
this.authenticationService.login(this.f.username.value, this.f.password.value, this.f.location.value, 0)
.subscribe( result => {
if (result === “true”) {
this.router.navigate([‘main’]);
}
else {
console.log(‘登入失敗!’);
this.loading = false;
}
});

結果發現我可以直接return this.http.get(url, {params});即可!好像是看範例亂湊,讓自己愈混亂,唉!

請教是否真的要先搞懂RxJS再來繼續下去呢?


#5

比較像是 API 的不熟悉造成的。讀一下官方文件,會有幫助 https://angular.io/guide/http
在這裡 RxJS 所佔的比例並不高。先讀官方文件,釐清一下