Angular 16:如何用Signals/Effects实现无订阅API调用与登录跳转
基于Angular 16 Signals实现无订阅登录流程
核心思路
用Angular 16的signal存储登录状态、表单数据等状态值,通过effect监听状态变化自动执行跳转逻辑,结合firstValueFrom将Observable风格的API转为Promise,用async/await处理异步操作,完全避免手动订阅。
1. 全局登录状态管理(AuthService)
先实现一个全局AuthService,统一处理登录逻辑和状态管理:
import { Injectable, signal, effect, inject } from '@angular/core'; import { Router } from '@angular/router'; import { HttpClient } from '@angular/common/http'; import { firstValueFrom } from 'rxjs'; // 定义用户类型 interface User { id: string; email: string; token: string; } @Injectable({ providedIn: 'root' }) export class AuthService { private http = inject(HttpClient); private router = inject(Router); // 登录状态Signal isLoggedIn = signal(false); // 当前用户信息Signal currentUser = signal<User | null>(null); constructor() { // 监听登录状态变化,自动跳转到首页 effect(() => { if (this.isLoggedIn()) { this.router.navigate(['/home']); } }); } // 谷歌登录逻辑 async googleLogin(): Promise<User> { try { // 调用后端谷歌登录API,转为Promise处理 const user = await firstValueFrom( this.http.post<User>('/api/auth/google', {}) ); // 更新状态Signal this.currentUser.set(user); this.isLoggedIn.set(true); return user; } catch (error) { throw new Error('谷歌登录失败'); } } // 邮箱登录逻辑 async emailLogin(email: string, password: string): Promise<User> { try { const user = await firstValueFrom( this.http.post<User>('/api/auth/email', { email, password }) ); this.currentUser.set(user); this.isLoggedIn.set(true); return user; } catch (error) { throw new Error('邮箱登录失败,请检查账号密码'); } } }
2. 登录组件实现(LoginComponent)
组件负责收集表单数据,触发登录操作,无需处理订阅和跳转逻辑:
import { Component, signal, inject } from '@angular/core'; import { AuthService } from './auth.service'; @Component({ selector: 'app-login', template: ` <div class="login-container"> <!-- 谷歌登录按钮 --> <button (click)="handleGoogleLogin()" class="google-btn">谷歌登录</button> <!-- 邮箱登录表单 --> <div class="email-form"> <input type="email" placeholder="邮箱" [value]="emailForm().email" (input)="updateForm('email', $event.target.value)" > <input type="password" placeholder="密码" [value]="emailForm().password" (input)="updateForm('password', $event.target.value)" > <button (click)="handleEmailLogin()" class="email-btn">登录</button> </div> </div> ` }) export class LoginComponent { private authService = inject(AuthService); // 邮箱登录表单数据Signal emailForm = signal({ email: '', password: '' }); // 更新表单字段 updateForm(field: 'email' | 'password', value: string) { this.emailForm.update(prev => ({ ...prev, [field]: value })); } // 触发谷歌登录 async handleGoogleLogin() { try { await this.authService.googleLogin(); } catch (error) { alert(error.message); } } // 触发邮箱登录 async handleEmailLogin() { const { email, password } = this.emailForm(); if (!email || !password) { alert('请填写完整账号密码'); return; } try { await this.authService.emailLogin(email, password); } catch (error) { alert(error.message); } } }
关键要点说明
- Signal替代订阅:用
signal存储状态,状态变化会自动通知依赖它的effect,无需手动订阅Observable - Effect自动处理副作用:登录状态变化时,
effect自动触发跳转,把副作用逻辑从组件分离到服务中 - 无订阅异步处理:用
firstValueFrom将Observable风格的HTTP请求转为Promise,结合async/await处理异步,彻底告别subscribe - 表单状态管理:用
signal存储表单数据,通过update方法修改状态,替代双向绑定或表单控件订阅
内容的提问来源于stack exchange,提问作者Spring
相关产品推荐
相关产品推荐

