我想创建一个订阅Angular参数Map更改的服务。不幸的是,这似乎不起作用。
export class LocationResourcesService {
constructor(private activatedRoute: ActivatedRoute) {
this.activatedRoute.paramMap.pipe(
map((params: ParamMap) => params.get('account-id')),
).subscribe((newValue) => console.log(newValue))
}
// ----------
}
上面的订阅只发出一个值-当Angular第一次加载到页面时。该值是null
。当它被放置在页面中活动的组件的构造函数中时,相同的订阅会这样做。大概是因为路由已经加载并且ActivatedRoute
已经设置。
我会假设ActivatedRoute是一个单例服务,因此我可以订阅它的更改。显然情况并非如此,那么该服务如何订阅tivatedRoute. ParamMap
的值?
不幸的是,这个问题没有简单的答案。我不打算重现整个讨论,但解决方案可以在这里看到:https://github.com/angular/angular/issues/11023#issuecomment-399667101.
我把它复制过来,以便更容易感受到它。正如我所说,这并不简单:
import { Injectable } from '@angular/core';
import { ActivatedRoute, Router, NavigationEnd, Params } from '@angular/router';
import { filter, switchMap } from 'rxjs/operators'
import { of, Observable } from 'rxjs'
@Injectable()
export class MiscService {
params$: Observable<Params>
constructor( private router: Router,
private route: ActivatedRoute) {
// Create an observable representing the params
this.params$ = this.router.events.pipe(
filter(event => event instanceof NavigationEnd),
switchMap(() => {
const params = this.route.firstChild?.params
// return params or an empty observable
return params || of()
})
)
// subscribe to the params$ observable - can be done for
// multiple observers
this.params$.subscribe((params) => {
console.log('params', params)
})
}
}
这是我所做的(使用ReplaySub
的纯RxJS实现):
将存储参数(例如parthing
)的公共服务(作为可观察对象):
@Injectable({
providedIn: "root",
})
export class MyService {
// use ReplaySubject to store/observe the paramThing
paramThing$ = new ReplaySubject<string | null>(1);
constructor() {}
}
然后在ActivatedRoute
给出正确值的组件中:
@Component({
selector: "app-xxx",
templateUrl: "./xxx.component.html",
styleUrls: ["./xxx.component.scss"],
})
export class XxxComponent implements OnInit, OnDestroy {
private subs: Subscription[] = [];
constructor(
private readonly actRoute: ActivatedRoute,
private readonly myService: DashService
) {}
ngOnInit(): void {
const subscription = this.actRoute.paramMap
.pipe(map((parameters) => parameters.get("paramThing")))
.subscribe((paramThingValue) => {
this.myService.paramThing$.next(paramThingValue); // <=== this does the trick
});
this.subs.push(subscription);
}
ngOnDestroy(): void {
for (const item of this.subs) item.unsubscribe();
this.subs = [];
// if you need, you can unset the value when component goes out of scope
this.myService.paramThing$.next(null);
}
}
然后在任何消费组件/服务中,您都可以使用MyService
来获取parthing的值:
我需要parthing
的示例服务。
import { lastValueFrom, take, switchMap } from "rxjs";
@Component({
selector: "app-ccc",
templateUrl: "./ccc.component.html",
styleUrls: ["./ccc.component.scss"],
})
export class CccComponent implements OnInit {
constructor(
private readonly myService: MyService
) {}
async ngOnInit() {
const paramThing = await lastValueFrom(this.myService.paramThing$.pipe(take(1)));
// paramThing:null | string
// or if you want an observable chain for paramThing
this.myService.paramThing$.pipe(
switchMap(paramThing => {
return anotherPromiseOrObservableChain;
}),
// ...
).subscribe(...)
}
}
Peter Nixey的解决方案对我不起作用,除非我将ActivatedRoute服务注入到我想要订阅更改的组件中。虽然Wajahath的解决方案有效,但它很hacky,因为您需要在使用该值之前从组件注入到服务。
我提出以下解决方案。它包括获取当前路由参数、所有路由参数更改的可观察值和特定路由参数更改的可观察值(按参数名称)。
@Injectable({
providedIn: "root"
})
export class ParamsService {
/**
* The route params.
*/
readonly routeParams$: Observable<Params> = this.router.events.pipe(
filter(event => event instanceof NavigationEnd),
).pipe(
map(_ => this.getCurrentRouteParams()),
distinctUntilChanged(),
);
/**
* Gets the observable of a specific route param.
*/
routeParamByName(paramName: string, defaultValue: string): Observable<T> {
return this.routeParams$.pipe(
map(params => params[paramName] ?? defaultValue),
distinctUntilChanged(),
);
}
/**
* Gets the current params in the activated route.
* @see https://stackoverflow.com/questions/39977962/angular-2-0-2-activatedroute-is-empty-in-a-service/74915199#74915199
*/
getCurrentRouteParams(): Params {
let params: any = {};
let stack: ActivatedRouteSnapshot[] = [this.router.routerState.snapshot.root];
while (stack.length > 0) {
const route = stack.pop()!;
params = {...params, ...route.params};
stack.push(...route.children);
}
return params;
}
}