Angular同一组件内切换登录/找回密码路由及视图实现
实现Angular登录/密码重置视图切换(带URL同步与过渡动画)
最优方案:利用Angular子路由实现
不用手动维护状态或监听路由,直接借助Angular路由系统的子路由能力,完美匹配URL变化+局部视图切换的需求,代码更简洁且符合框架设计。
1. 配置子路由
在路由模块中定义/login作为父路由,嵌套两个子路由分别对应登录和密码重置视图:
import { Routes, RouterModule } from '@angular/router'; import { LoginContainerComponent } from './login-container.component'; import { LoginComponent } from './login.component'; import { PasswordForgetComponent } from './password-forget.component'; const routes: Routes = [ { path: 'login', component: LoginContainerComponent, // 父容器组件,负责承载视图切换 children: [ { path: '', component: LoginComponent }, // 默认子路由,对应URL: /login { path: 'forgot-password', component: PasswordForgetComponent } // 对应URL: /login/forgot-password ] } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
2. 父容器组件模板
把原来的*ngIf判断替换为<router-outlet>,Angular会自动根据当前子路由渲染对应组件:
<div class="container"> <!-- 子路由内容会自动渲染在这里 --> <router-outlet></router-outlet> </div>
3. 按钮导航逻辑
在LoginComponent的“忘记密码”按钮中,使用相对路径跳转到子路由:
import { Component } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; @Component({ selector: 'login-component', templateUrl: './login.component.html' }) export class LoginComponent { constructor(private router: Router, private route: ActivatedRoute) {} goToForgotPassword() { // 相对于当前/login路由跳转到子路由forgot-password this.router.navigate(['./forgot-password'], { relativeTo: this.route }); } }
在PasswordForgetComponent的“返回登录”按钮中,返回父路由:
import { Component } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; @Component({ selector: 'password-forget-component', templateUrl: './password-forget.component.html' }) export class PasswordForgetComponent { constructor(private router: Router, private route: ActivatedRoute) {} goBackToLogin() { // 返回上一级路由(即/login) this.router.navigate(['../'], { relativeTo: this.route }); } }
过渡动画实现
可以给视图切换添加平滑的动画效果,这里提供两种常用方式:
方式1:组件级进入/离开动画
在父容器组件中定义动画触发器,然后给子组件绑定动画:
import { Component } from '@angular/core'; import { trigger, transition, style, animate } from '@angular/animations'; @Component({ selector: 'login-container', templateUrl: './login-container.component.html', animations: [ trigger('slideInOut', [ // 进入动画:从右侧滑入 transition(':enter', [ style({ transform: 'translateX(100%)', opacity: 0 }), animate('300ms ease-out', style({ transform: 'translateX(0)', opacity: 1 })) ]), // 离开动画:向左侧滑出 transition(':leave', [ animate('300ms ease-in', style({ transform: 'translateX(-100%)', opacity: 0 })) ]) ]) ] }) export class LoginContainerComponent {}
如果用备选的*ngIf方案(见下文),模板绑定动画:
<div class="container"> <login-component *ngIf="!isForgotPassword$ | async" [@slideInOut]></login-component> <password-forget-component *ngIf="isForgotPassword$ | async" [@slideInOut]></password-forget-component> </div>
方式2:路由动画(子路由场景)
如果用子路由方案,可以给路由添加动画标识,在父容器中触发路由切换动画:
在路由配置中添加data字段:
children: [ { path: '', component: LoginComponent, data: { animation: 'LoginPage' } }, { path: 'forgot-password', component: PasswordForgetComponent, data: { animation: 'ForgotPasswordPage' } } ]
然后在父容器组件中监听路由变化,绑定动画:
import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { trigger, transition, style, animate, query, stagger } from '@angular/animations'; @Component({ selector: 'login-container', templateUrl: './login-container.component.html', animations: [ trigger('routeAnimations', [ transition('* <=> *', [ query(':enter', [ style({ opacity: 0, transform: 'translateX(100%)' }) ], { optional: true }), query(':leave', [ animate('300ms ease-out', style({ opacity: 0, transform: 'translateX(-100%)' })) ], { optional: true }), query(':enter', [ animate('300ms ease-out', style({ opacity: 1, transform: 'translateX(0)' })) ], { optional: true }) ]) ]) ] }) export class LoginContainerComponent { prepareRoute(outlet: RouterOutlet) { return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation']; } }
模板中绑定动画到router-outlet:
<div class="container"> <router-outlet [@routeAnimations]="prepareRoute(outlet)" #outlet="outlet"></router-outlet> </div>
备选方案:手动监听路由(无需子路由)
如果你不想用子路由,可以优化你原本的思路,用async pipe自动管理订阅,避免内存泄漏:
import { Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { map } from 'rxjs/operators'; @Component({ selector: 'login-container', templateUrl: './login-container.component.html' }) export class LoginContainerComponent { // 用Observable判断当前是否是密码重置路由 isForgotPassword$ = this.route.url.pipe( map(urlSegments => urlSegments.some(seg => seg.path === 'forgot-password')) ); constructor(private route: ActivatedRoute) {} }
模板中用async pipe订阅:
<div class="container"> <login-component *ngIf="!isForgotPassword$ | async"></login-component> <password-forget-component *ngIf="isForgotPassword$ | async"></password-forget-component> </div>
按钮导航逻辑和之前一致,直接跳转到/login/forgot-password即可。
内容的提问来源于stack exchange,提问作者A G
相关产品推荐
相关产品推荐

