2

我有一个按钮,当我单击它时,它会通过 API 调用将用户登录到数据库中,但是代码似乎不会等待响应,直到第二次单击该按钮。

HTML:

<button class="login-button" [disabled]='!loginForm.valid' (click)="login()">Login User</button>

Login.Component.ts:

login() {
    this.getLoginDetails();

    // After the first click of the button this is 'undefined' but after the second it returns the details
    var details = this.loginResponse;

    // Store details in session
    localStorage.setItem('token', details.token);   // Throws exception on first click due to 'details' being undefined
    localStorage.setItem('refreshToken', details.refreshToken);
    localStorage.setItem('userId', details.userId.toString());

    this.router.navigate(['/subscribers']);
}

getLoginDetails(){
    var data = JSON.stringify({
        "EmailAddress": this.loginForm.controls['userName'].value,
        "Password": this.loginForm.controls['password'].value 
    });

    const httpOptions = {
        headers: new HttpHeaders({
            'Content-Type':  'application/json'
        })
    };

    // After the first click this is undefined but after the second it returns a value
    let resp = this.http.post("https://localhost:8080/api/auth/login", data, httpOptions);

    resp.subscribe((response: LoginResponse) => {
        if(response) {
            this.loginResponse = response
        } 
    });
}

问题似乎是在第一次点击后,帖子没有返回undefined,但在第二次点击后它返回了一个值。

我认为问题在于程序没有等待 API 返回值,但是我不明白为什么在第二次单击后它会起作用。

使用 Angular 10,有谁知道如何确保post等待?

4

5 回答 5

3

相反,您应该resp在方法中返回并订阅它login()

login() {
  this.getLoginDetails().subscribe((response: LoginResponse) => {
      // assign the response here
      var details = response;

      // Store details in session
      localStorage.setItem('token', details.token);   // Throws exception on first click due to 'details' being undefined
      localStorage.setItem('refreshToken', details.refreshToken);
      localStorage.setItem('userId', details.userId.toString());

      this.router.navigate(['/subscribers']);
   });
}

getLoginDetails(){
  // other implementation

  return resp;
}
于 2020-08-27T12:14:28.497 回答
2

这就是异步数据的本质。它是异步分配的,因此当您尝试使用它时,您不能假定它已经被分配了一个值。相反,您需要等待它被分配。换句话说,订阅需要响应的可观察对象。

尝试以下

login() {
  this.getLoginDetails().subscribe(
    (details: LoginResponse) => {
      if(details) {
        // Store details in session
        localStorage.setItem('token', details.token);
        localStorage.setItem('refreshToken', details.refreshToken);
        localStorage.setItem('userId', details.userId.toString());
        this.router.navigate(['/subscribers']);
      }
    },
    error => {
      // always good practice to handle errors from HTTP observables
    }
  );
}

getLoginDetails(): Observable<any> {     // <-- return the observable here
  var data = JSON.stringify({
    "EmailAddress": this.loginForm.controls['userName'].value,
    "Password": this.loginForm.controls['password'].value 
  });

  const httpOptions = {
    headers: new HttpHeaders({
      'Content-Type':  'application/json'
    })
  };

  return this.http.post("https://localhost:8080/api/auth/login", data, httpOptions);
}

您可以在此处找到有关从异步调用返回数据的更多信息。

于 2020-08-27T12:17:06.490 回答
1

在你的login()方法上,你调用getLoginDetails()你订阅的地方。订阅是一个事件侦听器,因此只有在您呈现值this.loginResponse时才会填充您的变量。resp

因此,最后在您的login()方法中,您将细节归因于尚未填充的内容。

所以我建议你这样做

login() {
    this.getLoginDetails().subscribe((response: LoginResponse) => {
        if(response) {

            // Store details in session
            localStorage.setItem('token', response.token);
            localStorage.setItem('refreshToken', response.refreshToken);
            localStorage.setItem('userId', response.userId.toString());

            this.router.navigate(['/subscribers']);
        } 
    });    
}

getLoginDetails(){
    var data = JSON.stringify({
        "EmailAddress": this.loginForm.controls['userName'].value,
        "Password": this.loginForm.controls['password'].value 
    });

    const httpOptions = {
        headers: new HttpHeaders({
            'Content-Type':  'application/json'
        })
    };

    return this.http.post("https://localhost:8080/api/auth/login", data, httpOptions);
}
于 2020-08-27T12:15:11.877 回答
1

问题是您在分配值之前没有等待响应。这就是发生的事情。

你发送一个请求。开始等待响应。分配undefineddetails。响应到来并loginResponse生效。您发送第二个请求。将第一个请求的响应分配给details

这是解决方案:

login() {
  this.getLoginDetails().subscribe((details: LoginResponse) => {
    if(details) {
      localStorage.setItem('token', details.token); 
      localStorage.setItem('refreshToken', details.refreshToken);
      localStorage.setItem('userId', details.userId.toString());

      this.router.navigate(['/subscribers']);
    } 
  });
}

getLoginDetails(){
    var data = JSON.stringify({
        "EmailAddress": this.loginForm.controls['userName'].value,
        "Password": this.loginForm.controls['password'].value 
    });

    const httpOptions = {
        headers: new HttpHeaders({
            'Content-Type':  'application/json'
        })
    };

    return this.http.post("https://localhost:8080/api/auth/login", data, httpOptions);
}
于 2020-08-27T12:21:01.520 回答
1

您可以使用异步和等待。async:- 函数前面的“async”这个词意味着一件简单的事情:一个函数总是返回一个promise。其他值自动包装在已解决的承诺中。

您的代码:-

    async login() {
   await this.getLoginDetails();
}

async getLoginDetails(){
    


//other implementation

    let resp = await this.http.post("https://localhost:8080/api/auth/login", data, httpOptions);

    resp.subscribe((response: LoginResponse) => {
    if(response) {
        this.loginResponse = response
    } 
  });
于 2020-08-27T12:27:20.747 回答