我正在开发一个简单的Angular登录对话框,它使用HttpClient将post()发布到我的后端API。我严格遵循angular httpclient docs中规定的方法。
一切正常,除非发生错误(我故意关闭后端API以生成504)。即使在这种情况下,所有错误处理代码都会按照我的预期执行,但问题是角度UI无法更新。
在正确执行的错误处理代码中,我正在更新组件中的一个简单字符串变量,该变量通过标准插值绑定到对话框UI;但是改变永远不会出现在用户界面中。
这是我的服务代码,用于调用HttpClient(注意:我正在使用pipe()和catchError()并返回文档中建议的新ErrorObservable):
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import 'rxjs/add/operator/timeout';
import { catchError} from 'rxjs/operators';
import { Observable } from 'rxjs/Observable';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
// My models
import { SignupRequest } from '../models/signuprequest';
import { LoginRequest } from '../models/loginrequest';
import { LoginResponse } from '../models/loginresponse';
import { SignupResponse } from '../models/signupresponse';
import { ErrorResponse } from '../models/errorresponse';
@Injectable()
export class LoginService {
private readonly loginUrl = 'http://localhost:4200/api/login';
private readonly signupUrl = 'http://localhost:4200/api/signup';
private readonly timeout = 20000;
constructor(private http: HttpClient) {}
LoginUser(loginRequest: LoginRequest) {
console.log('LoginService: login request ', loginRequest);
return this.http.post<LoginResponse>(this.loginUrl, loginRequest, { observe: 'response' })
.timeout(this.timeout)
.pipe(catchError(this.HandleError));
}
private HandleError(error: HttpErrorResponse, caught: Observable<any>) {
const er: ErrorResponse = {
response: error,
url: error.url ? error.url : '',
status: error.status ? error.status : '',
statusText: error.statusText ? error.statusText : '',
message: error.message ? error.message : '',
error: error.error ? error.error : ''
};
console.log('LoginService: error: ', er);
return new ErrorObservable(er);
}
}
以下是调用服务调用并订阅生成的Observable的组件代码:
import { Component, OnInit, Injectable, ApplicationRef, NgZone } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { HttpResponse } from '@angular/common/http';
import { LoginService } from '../../services/login.service';
import { SignupRequest } from '../../models/signuprequest';
import { LoginRequest } from '../../models/loginrequest';
import { LoginResponse } from '../../models/loginresponse';
import { SignupResponse } from '../../models/signupresponse';
import { ErrorResponse } from '../../models/errorresponse';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['../../app.component.css']
})
export class LoginComponent implements OnInit {
...
constructor( private fb: FormBuilder, private loginService: LoginService ) {
...
}
public Login() {
const loginRequest = this.ExtractLoginRequest();
if (this.ValidLogin(loginRequest)) {
console.log('LoginComponent: login request ', loginRequest);
this.loginService.LoginUser(loginRequest).subscribe(
this.HandleLoginResponse,
// The line below should work fine based on everything I've read
this.HandleError
// But the UI will not update unless I use the zone.run() line below instead!?
// (error) => { this.zone.run( () => { this.HandleNetworkError(error); } ); }
);
console.log('finished the login request flow');
}
private HandleLoginResponse(httpResponse: HttpResponse<LoginResponse>) {
const loginResponse: LoginResponse = httpResponse.body;
if (!this.BackEndLoginError(loginResponse)) {
console.log('LoginComponent: Storing access token in localstorage', loginResponse.token);
window.localStorage.setItem('littlechatToken', loginResponse.token);
}
}
private HandleError(errorResponse: ErrorResponse) {
console.log('LoginComponent: handling error');
// set the error text that is displayed on a div in the UI
this.backendLoginErrorText = 'Network error: ' + errorResponse.message;
}
}
在组件模板中,我进行插值以显示&#39; backendLoginErrorText&#39;可变...
<!-- Login button and error text -->
<div class="centered">
<button type="button" [disabled]="LoginDisabled()" name="login" (click)="Login()">Login</button>
<div class="alert alert-danger" [hidden]="backendLoginErrorText.length === 0"> {{backendLoginErrorText}} </div>
</div>
正如我在组件代码中的代码注释中所指出的,只是将this.HandleError作为错误处理函数传递给subscribe()不起作用 - UI永远不会更新!注意:所有代码都正确执行(即更新肯定发生,更改检测无效)。
如果我改为使用this.zone.run(...),如上所述,更新发生就好了。
在阅读了SO以及其他内容之后,我理解了这里发生了什么的基础知识 - 事实上,在几秒钟之后异步发生504错误会导致错误处理被执行在Angular之外,需要zone.run()?
我正在学习Angular,一位经验丰富的朋友告诉我&#34;如果你曾经在这种情况下使用zone.run(),那么你做错了什么&#34;。我发现很难相信这样一个简单的场景(带有后端API和post()调用的基本登录表单) - 使用angular docs中建议的方法 - 需要这样一个看似hacky的东西...... / p>
我在这里缺少什么?