ngrx store - select 一个实体显示在一个单独的模板中

ngrx store - select one entity to show in a separated template

我有一个博客应用程序,我正在使用 ngrx。

应用程序 - 现在 - 在商店中有两个对象

{
"posts": [...],
"users": [...]
}

现在我想制作一个单独的组件来显示单个 post (selected-post.component).

我尝试 select 使用实体的 ID select 或者像这样:

export const selectPostById = (id: string) => createSelector(
  selectEntities,
  entities => entities[id]
);

然后在组件中获取:

postId: string = localStorage.getItem("_selectedPostId");

selectedPost: Observable<Post> = this.store.select(selectPostById(postId));

然后在我使用的视图中显示它:

<ng-container *ngIf="selectedPost | async">
  <div class="post text-light">
    <div class="p-title fs-3">{{(selectedPost | async).title}}</div>
    <div class="p-image">
      <img [src]="(selectedPost | async).imageSrc">
    </div>
    <div class="p-cont fs-5">
      {{(selectedPost | async).content}}
    </div>
    <div class="p-category float-start fs-6">
      <a class="p-1" href="categories/category" *ngFor="let ct of (selectedPost | async).categories">
        {{ct}}
      </a>
    </div>
    <div class="p-date float-end fs-6">
      {{(selectedPost | async).date}}
    </div>
    <div class="p-author fs-6">
      <a href="users/otheruser">{{(selectedPost | async).author.name}}</a>
    </div>
  </div>
  <div class="info">Comments</div>
  <div class="comments text-light">
    <div class="comment border rounded m-1" *ngFor="let comment of (selectedPost | async).comments">
      <div class="c-author">
        <a href="users/otheruser">{{comment.author.name}}</a>
      </div>
      <div class="c-cont">{{comment.content}}</div>
      <div class="-c-date">{{comment.date}}</div>
      <div class="replies" *ngIf="comment.replies">
        <div class="comment border rounded m-1" *ngFor="let reply of comment.replies">
          <div class="c-author">
            <a href="users/otheruser">{{reply.author.name}}</a>
          </div>
          <div class="c-cont">{{reply.content}}</div>
          <div class="c-date">{{reply.date}}</div>
        </div>
      </div>
    </div>
  </div>
</ng-container>

所以问题是 post 在控制台中没有显示错误:

ERROR TypeError: Cannot read properties of undefined (reading: '1')

我select编辑了ID为“1”的post,它存在于redux devtools显示的状态中。

有什么想法吗?

编辑: 减速器:

import { Action, createFeatureSelector, createReducer, createSelector, on } from '@ngrx/store';
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
import * as postActions from './actions';
import { Post } from 'src/app/models/post';

export interface PostState extends EntityState<Post> {
  error: any;
  selectedPostId: string | null;
}

export function selectUserId(a: Post): string {
  return a.id;
}

export const adapter: EntityAdapter<Post> = createEntityAdapter<Post>({
  selectId: selectUserId,
  sortComparer: false
});

export const initialState = adapter.getInitialState({error: null,selectedPostId: null});

const testsReducer = createReducer(
  initialState,
  on(postActions.loadPostsSuccess, (state, {posts}) => adapter.setAll(posts, state)),
  on(postActions.loadPostsFailed, (state, {error}) => ({...state, error})),
  on(postActions.setSelectedPostId, (state, {id}) => ({...state, selectedPostId: id}))
);

export function reducer(state: PostState, action: Action) {
  return testsReducer(state, action);
}

// Selectors

const {
  selectIds,
  selectEntities,
  selectAll,
  selectTotal,
} = adapter.getSelectors();

const feature = createFeatureSelector<PostState>('posts');

export const getPosts = createSelector(feature,selectAll);

export const getSelectedPostId = (state: PostState) => state.selectedPostId;

export const selectPostId = createSelector(
  feature,
  getSelectedPostId
);

export const selectPostById = (id: string) => createSelector(
  selectEntities,
  entities => entities[id]
);

您必须 link 适配器中的 selectEntities 选择器到 feature 一个(由 createFeatureSelector 函数创建),然后使用它来获取所需的实体编号.

您可以尝试以下方法:

// Selectors
const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors();

const feature = createFeatureSelector<PostState>('posts');

// ...

// Create a selector from the feature selector and selectEntities one.
export const selectPostEntities = createSelector(feature, selectEntities);

// Use the new selector `selectPostEntities` to slice the post entity from it.
export const selectPostById = (id: number) =>
    createSelector(selectPostEntities, (entities) => entities[id]);

此外,您可以通过仅使用 asyn 管道订阅 selectedPost observable 来优化您的组件模板,如下所示:

<ng-container *ngIf="selectedPost | async as _selectedPost">
<!-- You can now use the `_selectedPost` variable without `async` pipe for all the elements inside the ng-container -->
</ng-container>