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