我想在下面的 Angular 17.2 组件的 ts 文件中基于信号执行一些逻辑。我想使用 @ngrx/signals 包和 rxMethod 来处理我的获取请求。然而这似乎是一个问题,因为当代码执行时,信号的值是初始值。
我可以做什么来解决这个问题?在我看来,我可以使用 rxjs-interop 的 toObservable 将组件中的信号转换回可观察的,然后使用 toSignal 将响应转换回信号,以便获得模板中信号的好处。这感觉就像这里发生了太多转换......
或者也许我可以在组件中使用 RxMethod,但在商店中拥有与信号相关的代码似乎更好......
不确定如何处理这个问题,我可以使用一些建议
店铺:
import { inject } from "@angular/core";
import { pipe, switchMap } from "rxjs";
import { patchState, signalStore, withMethods, withState } from "@ngrx/signals";
import { rxMethod } from "@ngrx/signals/rxjs-interop";
import { tapResponse } from "@ngrx/operators";
import { Article } from "../models/article.model";
import { ArticlesService } from "../services/articles.service";
import { ArticleListConfig } from "../models/article-list-config.model";
type ArticlesState = {
articles: Article[];
article: Article;
slug: string;
config: ArticleListConfig;
};
const initialState: ArticlesState = {
articles: [],
article: {
slug: "",
title: "",
description: "",
body: "",
tagList: [],
createdAt: "",
updatedAt: "",
favorited: false,
favoritesCount: 0,
author: {
username: "",
bio: "",
image: "",
following: false,
},
},
slug: "",
config: {
type: "",
filters: {
tags: [],
},
},
};
export const ArticlesStore = signalStore(
withState(initialState),
withMethods((store, articlesService = inject(ArticlesService)) => ({
getBySlug: rxMethod<string>(
pipe(
switchMap((slug) => {
return articlesService.get(slug).pipe(
tapResponse({
next: (article) => patchState(store, { article }),
error: console.error,
})
);
})
)
),
}))
);
服务:
import { Injectable, inject } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
import { Article } from "../models/article.model";
@Injectable({ providedIn: "root" })
export class ArticlesService {
http = inject(HttpClient);
constructor() {}
get(slug: string): Observable<Article> {
return this.http
.get<{ article: Article }>(`/articles/${slug}`)
.pipe(map((data) => data.article));
}
}
成分:
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component, OnInit, effect, inject } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { ArticlesStore } from "src/app/core/stores/article.store";
@Component({
selector: "app-editor-page",
templateUrl: "./editor.component.html",
imports: [ CommonModule],
standalone: true,
providers: [ArticlesStore],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class EditorComponent implements OnInit {
readonly articlesStore = inject(ArticlesStore);
slug: string = "";
constructor(
private readonly route: ActivatedRoute,
) {
effect(() => {
console.log(this.articlesStore.article()); // This outputs twice, first the initial value then the value based on the response
});
}
ngOnInit() {
this.slug = this.route.snapshot.params["slug"];
this.articlesStore.slug = this.route.snapshot.params["slug"];
this.articlesStore.getBySlug(this.articlesStore.slug);
console.log(this.articlesStore.article()); // This outputs the initial value once
}
}
如果您想在从请求中获得第一个结果时执行一些逻辑,请考虑使用
effect
,这样您就可以看着 article
填充数据,然后因为不再需要而销毁效果
export class EditorComponent implements OnInit {
...
#initialArticleHandle = effect(() => {
const article = this.articlesStore.article();
if(article === null) return;
if(article.author.username === this.testUserName) {
// do your stuff
}
else {
this.router.navigateByUrl('/');
}
#initialArticleHandle.destroy(); // destroy effect not to listen article changes further, we've done everything we wanted
});