当改变URL时,主题不会被触发。
当改变URL时,主题不会被触发。
我的应用程序有以下app-routing.module.ts
:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { PageNotFoundComponent } from './core/page-not-found/page-not-found.component'; const routes: Routes = [ { path: '', redirectTo: 'forms', pathMatch: 'full' }, { path: 'forms', loadChildren : () => import('./pages/forms/forms.module').then(m => m.FormsModule) }, { path: 'admin', loadChildren : () => import('./pages/admin/admin.module').then(m => m.AdminModule) }, { path: '**', component: PageNotFoundComponent } @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
如您所见,我有两个我称之为“产品”的东西,它们分别是“forms”和“admin”,它们通过url /forms和/admin进行访问。
app.component.html
如下所示:
Loading...
如您所见,我有一个组件“navbar”,被标记为
。问题是,根据用户所在的产品,此导航栏的内容将有所不同。为了更清楚,让我们看一下navbar.component.html
:
lalalallala
还有navbar.component.ts
:
import { OnInit, OnDestroy, Component } from '@angular/core'; import { Subscription } from '../../../../../node_modules/rxjs'; import { ProductEnum } from '../../../shared/models/enums/product-enum'; import { ProductContainer } from '../../../shared/containers/product-container'; @Component({ selector: 'navbar', templateUrl: './navbar.component.html', styleUrls: ['./navbar.component.scss'] }) export class NavbarComponent implements OnInit, OnDestroy { public currentProduct:ProductEnum; private subscription = Subscription.EMPTY; constructor(private _productContainer:ProductContainer) { } ngOnInit(): void { this.currentProduct = this._productContainer.getCurrentProduct(); this.initializeSubscribers(); } ngOnDestroy(): void { this.subscription.unsubscribe(); } private initializeSubscribers():void { this.subscription.add(this._productContainer.getChangeCurrentProductSubject().subscribe(_newCurrentProduct => { this.currentProduct = _newCurrentProduct; })); } }
所以导航栏的内容将根据currentProduct
属性而变化。而这个变量将通过一个名为ProductContainer
的可注入服务上存在的subject属性进行更新(顺便说一句,这个名称很糟糕,我知道)。
以下是ProductContainer
(product-container.ts
)的代码:
import { Injectable } from '@angular/core'; import { Subject } from '../../../../node_modules/rxjs'; import { ProductEnum } from '../models/enums/product-enum'; @Injectable({ providedIn: 'root', }) export class ProductContainer { private changeCurrentProductSubject: Subject= new Subject (); private currentProduct: ProductEnum = ProductEnum.Forms; public setCurrentProduct(_newCurrentProduct:ProductEnum):void { this.currentProduct = _newCurrentProduct; this.changeCurrentProductSubject.next(this.currentProduct); } public getCurrentProduct():ProductEnum { return this.currentProduct; } public getChangeCurrentProductSubject():Subject { return this.changeCurrentProductSubject; } }
所以,如果我们回到app-routing.module.ts
,我们可以看到当访问\admin url时,将加载AdminModule
。以下是AdminModule
的代码:
import { CommonModule } from '@angular/common'; import { SharedModule } from '../../shared/shared.module'; import { NgModule } from '@angular/core'; import { AdminRoutingModule } from './admin-routing.module'; import { AdminHomeModule } from './admin-home/admin-home.module'; import { CoreModule } from '../../core/core.module'; import { ProductContainer } from '../../shared/containers/product-container'; import { ProductEnum } from '../../shared/models/enums/product-enum'; import { Router, RouterModule } from '@angular/router'; @NgModule({ declarations: [], imports: [ CommonModule, SharedModule, AdminRoutingModule, AdminHomeModule, CoreModule, RouterModule ] }) export class AdminModule { constructor(private _productContainer:ProductContainer, private router: Router) { this._productContainer.setCurrentProduct(ProductEnum.Admin); } }
所以,当访问\admin url时,它将将当前产品设置为Admin,因此导航栏将知道产品已更新。
但问题很简单,navbar中的订阅不起作用。
(编辑:错误的一部分也是由于使用了private subscription = Subscription.EMPTY;
,当我替换为private subscription = new Subscription();
时它就开始工作了)
问题的出现原因:在navbar.component.ts文件中,Subscription的定义方式不正确,导致无法执行订阅操作。
解决方法:将Subscription的定义方式修改为正确的形式,并将_newCurrentProduct参数用括号包裹起来。
下面是修改后的代码示例:
在product.container.ts文件中添加以下代码:
public getChangeCurrentProductSubject(): Observable{ return this.changeCurrentProductSubject.asObservable(); }
在navbar.component.ts文件的ngOnInit()函数中添加以下代码:
import { Subscription } from "rxjs"; private subscription: Subscription; ngOnInit(): void { this.initializeSubscribers(); this.currentProduct = this._productContainer.getCurrentProduct(); this.subscription = this._productContainer.getChangeCurrentProductSubject().subscribe((_newCurrentProduct) => { this.currentProduct = _newCurrentProduct; }); }
修改后的代码应该可以解决问题。
问题:在更改URL时,Subject未被触发的原因和解决方法
原因:
- 初始化时将逻辑放在NgModule中,这是不好的实践,模块不应该存在这样的逻辑。
- 在AdminModule的构造函数中更改currentProduct是行不通的,因为第一次加载后模块将不再加载,所以构造函数不会被调用。
- router.events没有被触发,可能是因为更新URL时整个应用程序被重新加载,包括navbar组件,因此组件也会被重建。
- 在navbar中使用this._router.url时,内容只是“/”,而不是“/forms”或“/admin”,即使URL是/forms或/admin。
解决方法:
- 将逻辑从NgModule中移出。
- 使用BehaviorSubject替代Subject。
- 在navbar组件中订阅url更改事件。
- 在navbar中定义“页面类型”参数,并将其传递给模块的根页面。
- 使用_location.path().startsWith()方法来获取URL路径。
以下是解决方法的代码示例:
// navbar.component.ts import { Component } from '@angular/core'; import { Router, NavigationEnd } from '@angular/router'; import { Location } from '@angular/common'; import { ProductEnum } from './product.enum'; @Component({ selector: 'app-navbar', templateUrl: './navbar.component.html', styleUrls: ['./navbar.component.css'] }) export class NavbarComponent { currentProduct: ProductEnum; constructor(private router: Router, private _location: Location) { if (this._location.path().startsWith(`/forms`)) { this.currentProduct = ProductEnum.Forms; } else if (this._location.path().startsWith(`/admin`)) { this.currentProduct = ProductEnum.Admin; } this.router.events.subscribe(event => { if (event instanceof NavigationEnd) { if (event.url.startsWith(`/forms`)) { this.currentProduct = ProductEnum.Forms; } else if (event.url.startsWith(`/admin`)) { this.currentProduct = ProductEnum.Admin; } } }); } }
感谢您的理解和支持,希望以上解决方法对您有所帮助。