Angular 订阅调用不止一次
Angular subscription is call more than once
我订阅了服务:
login(): void {
if (!this.loginForm.valid) {
return;
}
const { email, password } = this.loginForm.value;
this.authService.login(email, password).subscribe((res) => {
if (res.ok) {
this.router.navigateByUrl('/dashboard');
} else {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: res.errorMessage,
});
}
});
}
表单提交时调用此方法
<h1 class="mat-display-2 mb-0">Login</h1>
<form [formGroup]="loginForm" (submit)="login()">
<div class="form-group">
<mat-form-field class="form-field" appearance="standard">
<mat-label>Email</mat-label>
<label>
<input matInput formControlName="email" type="email" />
</label>
<mat-error *ngIf="controls.email.errors">
<span *ngIf="controls.email.errors.required">{{ 'auth.EMAIL_REQUIRED' | transloco }}</span>
<span *ngIf="controls.email.errors.email">{{ 'auth.INVALID_EMAIL' | transloco }}</span>
</mat-error>
</mat-form-field>
</div>
<div class="form-group">
<mat-form-field class="form-field" appearance="standard">
<mat-label>{{ 'auth.PWD' | transloco }}</mat-label>
<label>
<input matInput formControlName="password" type="password" />
</label>
<mat-error *ngIf="controls.password.invalid">{{ 'auth.PWD_REQUIRED' | transloco }}</mat-error>
</mat-form-field>
</div>
<p *transloco="let t">
{{ t('auth.LOGIN_HELP') }}
<a class="d-inline" routerLink="../signup" *transloco="let t">{{ t('auth.SIGNUP') }}</a>
</p>
<div class="text-right m-3">
<button mat-raised-button color="primary" class="mat-elevation-z8" type="submit" *transloco="let t">
{{ t('auth.LOGIN') }}
</button>
</div>
</form>
当我更改界面中的语言时出现问题,因为订阅 auth 服务是使用以前的反应形式值调用的,例如,如果发生以前的错误,它会再次使用新语言触发
¿我该如何解决?
我正在使用 transloco 语言库
完成登录组件
import { Component } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import Swal from 'sweetalert2';
import { AuthService } from '../../services/auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
})
export class LoginComponent {
loginForm: FormGroup = this.formBuilder.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required]],
});
constructor(private formBuilder: FormBuilder, private router: Router, private authService: AuthService) {}
get controls(): { [key: string]: AbstractControl } {
return this.loginForm.controls;
}
login(): void {
if (!this.loginForm.valid) {
return;
}
const { email, password } = this.loginForm.value;
this.authService.login(email, password).subscribe((res) => {
if (res.ok) {
this.router.navigateByUrl('/dashboard');
} else {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: res.errorMessage,
});
}
});
}
}
编辑
我找到了解决方案,但有点奇怪...
login(): void {
if (!this.loginForm.valid) {
return;
}
const { email, password } = this.loginForm.value;
let subs: Subscription;
subs = this.authService.login(email, password).subscribe((res) => {
if (res.ok) {
this.router.navigateByUrl('/dashboard');
} else {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: res.errorMessage,
});
}
subs.unsubscribe();
});
}
我确定有更好的方法(RxJS 有很多运算符和用例),但作为一个有效的 solution/workaround 如果你选择首先通过使用 RxJS 运算符“take”发射可观察对象,这样你就可以摆脱接下来的那些重复订阅。
示例:
在您的 AuthService 调用中添加一个 take(1):
this.authService
.login(email, password)
.pipe(take(1))
.subscribe(...)
干杯。
我认为使用 take(1) 运算符应该是更好的方法,因为如果订阅进入 if 块,它会在不取消订阅的情况下转到另一个页面。否则,您还可以确保取消订阅 onDestroy()。 take(1) 将自动覆盖这种情况,因为它仅在第一个数据到来后取消订阅。 https://www.learnrxjs.io/learn-rxjs/operators/filtering/take
我订阅了服务:
login(): void {
if (!this.loginForm.valid) {
return;
}
const { email, password } = this.loginForm.value;
this.authService.login(email, password).subscribe((res) => {
if (res.ok) {
this.router.navigateByUrl('/dashboard');
} else {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: res.errorMessage,
});
}
});
}
表单提交时调用此方法
<h1 class="mat-display-2 mb-0">Login</h1>
<form [formGroup]="loginForm" (submit)="login()">
<div class="form-group">
<mat-form-field class="form-field" appearance="standard">
<mat-label>Email</mat-label>
<label>
<input matInput formControlName="email" type="email" />
</label>
<mat-error *ngIf="controls.email.errors">
<span *ngIf="controls.email.errors.required">{{ 'auth.EMAIL_REQUIRED' | transloco }}</span>
<span *ngIf="controls.email.errors.email">{{ 'auth.INVALID_EMAIL' | transloco }}</span>
</mat-error>
</mat-form-field>
</div>
<div class="form-group">
<mat-form-field class="form-field" appearance="standard">
<mat-label>{{ 'auth.PWD' | transloco }}</mat-label>
<label>
<input matInput formControlName="password" type="password" />
</label>
<mat-error *ngIf="controls.password.invalid">{{ 'auth.PWD_REQUIRED' | transloco }}</mat-error>
</mat-form-field>
</div>
<p *transloco="let t">
{{ t('auth.LOGIN_HELP') }}
<a class="d-inline" routerLink="../signup" *transloco="let t">{{ t('auth.SIGNUP') }}</a>
</p>
<div class="text-right m-3">
<button mat-raised-button color="primary" class="mat-elevation-z8" type="submit" *transloco="let t">
{{ t('auth.LOGIN') }}
</button>
</div>
</form>
当我更改界面中的语言时出现问题,因为订阅 auth 服务是使用以前的反应形式值调用的,例如,如果发生以前的错误,它会再次使用新语言触发
¿我该如何解决?
我正在使用 transloco 语言库
完成登录组件
import { Component } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import Swal from 'sweetalert2';
import { AuthService } from '../../services/auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
})
export class LoginComponent {
loginForm: FormGroup = this.formBuilder.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required]],
});
constructor(private formBuilder: FormBuilder, private router: Router, private authService: AuthService) {}
get controls(): { [key: string]: AbstractControl } {
return this.loginForm.controls;
}
login(): void {
if (!this.loginForm.valid) {
return;
}
const { email, password } = this.loginForm.value;
this.authService.login(email, password).subscribe((res) => {
if (res.ok) {
this.router.navigateByUrl('/dashboard');
} else {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: res.errorMessage,
});
}
});
}
}
编辑
我找到了解决方案,但有点奇怪...
login(): void {
if (!this.loginForm.valid) {
return;
}
const { email, password } = this.loginForm.value;
let subs: Subscription;
subs = this.authService.login(email, password).subscribe((res) => {
if (res.ok) {
this.router.navigateByUrl('/dashboard');
} else {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: res.errorMessage,
});
}
subs.unsubscribe();
});
}
我确定有更好的方法(RxJS 有很多运算符和用例),但作为一个有效的 solution/workaround 如果你选择首先通过使用 RxJS 运算符“take”发射可观察对象,这样你就可以摆脱接下来的那些重复订阅。
示例: 在您的 AuthService 调用中添加一个 take(1):
this.authService
.login(email, password)
.pipe(take(1))
.subscribe(...)
干杯。
我认为使用 take(1) 运算符应该是更好的方法,因为如果订阅进入 if 块,它会在不取消订阅的情况下转到另一个页面。否则,您还可以确保取消订阅 onDestroy()。 take(1) 将自动覆盖这种情况,因为它仅在第一个数据到来后取消订阅。 https://www.learnrxjs.io/learn-rxjs/operators/filtering/take