我有一个按钮,当我单击它时,它将通过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
等待吗?
答案 0 :(得分:2)
相反,您应该返回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;
}
答案 1 :(得分: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);
}
您可以找到有关从异步调用here返回数据的更多信息。
答案 2 :(得分:1)
在您的import UIKit
import AVFoundation
func printMetadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], previewLayer: AVCaptureVideoPreviewLayer) {
for object in metadataObjects {
let visualCodeObject = previewLayer.transformedMetadataObject(for: object)
guard let object = visualCodeObject, let barcode = object as? AVMetadataMachineReadableCodeObject else {
NSLog("Ignoring object that is not AVMetadataMachineReadableCodeObject")
continue
}
guard let barcodeString = barcode.stringValue else {
NSLog("Captured something that's not a string")
continue
}
NSLog("Captured string %@", barcodeString)
}
}
class CaptureView: UIView, AVCaptureMetadataOutputObjectsDelegate {
private let previewLayer = AVCaptureVideoPreviewLayer()
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
printMetadataOutput(output, didOutput: metadataObjects, previewLayer: self.previewLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
self.previewLayer.frame = self.frame
}
init(frame: CGRect, delegate: AVCaptureMetadataOutputObjectsDelegate) {
guard let captureDevice = AVCaptureDevice.default(for: .video) else {
fatalError("Couldn't find default capture device")
}
guard let captureDeviceInput = try? AVCaptureDeviceInput(device: captureDevice) else {
super.init(frame: frame)
return
}
let captureSession = AVCaptureSession()
captureSession.addInput(captureDeviceInput)
self.previewLayer.session = captureSession
self.previewLayer.videoGravity = .resizeAspectFill
super.init(frame: frame)
self.layer.addSublayer(self.previewLayer)
self.previewLayer.frame = self.frame
captureSession.startRunning()
let metadataOutput = AVCaptureMetadataOutput()
// metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.setMetadataObjectsDelegate(delegate, queue: DispatchQueue.main)
metadataOutput.rectOfInterest = CGRect(x: 0, y: 0, width: 1, height: 1)
if captureSession.canAddOutput(metadataOutput) {
captureSession.addOutput(metadataOutput)
} else {
fatalError("Can't add metadata output to capture session")
}
metadataOutput.metadataObjectTypes = [.qr]
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class MetadataDelegate: NSObject, AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
guard let previewLayer = connection.videoPreviewLayer else {
print("previewLayer was nil")
return
}
printMetadataOutput(output, didOutput: metadataObjects, previewLayer: previewLayer)
}
}
class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
private let metadataDelegate = MetadataDelegate()
override func viewDidLoad() {
let captureView = CaptureView(frame: CGRect(), delegate: self.metadataDelegate)
captureView.frame = self.view.frame
captureView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.view.addSubview(captureView)
}
}
方法上,您在进行订阅的地方调用login()
。订阅是事件侦听器,因此仅当您的getLoginDetails()
呈现值时,变量this.loginResponse
才会被填充。
最后,在您的resp
方法中,您可以归因于详细说明尚未填充的内容。
所以我建议你这样做
login()
答案 3 :(得分:1)
问题是您在分配值之前没有等待响应。这是发生了什么。
您发送请求。开始等待响应。将undefined
分配给details
。响应到来,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);
}
答案 4 :(得分:1)
您可以使用异步并等待。 异步: 函数前的“异步”一词意味着一件简单的事情:函数总是返回承诺。其他值会自动包装在已解决的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
}
});