【问题标题】:Why card list is not shown in a component?为什么卡片列表不显示在组件中?
【发布时间】:2021-05-17 21:11:39
【问题描述】:

我是 NgRx 和 Firebase 的新手,当我使用 @ngrx/effects 从 Firestore 获取卡片数据时,卡片列表没有显示在卡片列表页面上。但是当我通过新卡片组件表单添加新卡片时,它会显示在页面上,虽然它不会在页面刷新后显示。

我注意到有趣的事情是,当我查看 Redux 开发工具时,它会显示带有数据的卡片操作 (Image),而状态中没有任何数据。如果我添加一张新卡,它的状态会一直保持数据,直到我刷新页面。 (Image2)

下面是我的代码:

card-list.component.html

<mat-card id="addCard">
    <button mat-flat-button class="card__like" (click)="add()">
        <mat-icon>add_box</mat-icon>
    </button>
</mat-card>
<mat-card *ngFor="let card of cards$ | async">
    <mat-card-content>
        <span class="card__title">{{ card.title }}</span>
        <span class="card__text">{{ card.description }}</span>
    </mat-card-content>
    <mat-card-footer>
        <button mat-flat-button class="card__edit" [routerLink]="['/info', card.id]">
            <mat-icon>edit</mat-icon>
        </button>
        <button mat-flat-button class="card__like" (click)="addToFavorite(card.cardId)">
            <mat-icon [class.liked]="isLiked(card.cardId)">favorite</mat-icon>
        </button>
    </mat-card-footer>
</mat-card>

card-list.component.ts

import { Component, OnInit } from '@angular/core';
import { AngularFireDatabase } from '@angular/fire/database';
import { AngularFirestore } from '@angular/fire/firestore';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { map, take, tap } from 'rxjs/operators';

import { select, Store } from '@ngrx/store';
import { Card } from '../card';
import * as fromCard from '../store/reducer/card.reducer';
import * as actions from '../store/action/card.actions';
import * as selectors from '../store/selector/card.selectors';

import { CardService } from '../services/card.service';
import { LikeService } from '../services/like.service';
import { LoadingService } from '../services/loading.service';

@Component({
  selector: 'card-list',
  templateUrl: './card-list.component.html',
  styleUrls: ['./card-list.component.sass'],
})
export class CardListComponent implements OnInit {
  cards: Card[] = [];

  cards$: Observable<Card[]>;

  constructor(private router: Router,
              private cardService: CardService,
              private likeService: LikeService,
              private loadingService: LoadingService,
              private afs: AngularFirestore,
              private db: AngularFireDatabase,
              private store: Store<fromCard.State>) {
    this.cards$ = this.store.select<Card[]>(selectors.selectAll).pipe(
      tap((_) => console.log(_)),
    );
  }

  ngOnInit() {
    this.getCardsFromRealtimeDB('Descending');
  }

  ... some code

  public getCardsFromRealtimeDB(sortOrder: string) {
    this.store.dispatch(new actions.Query());
  }

  ... some code
}

card.effects.ts

import { Injectable } from '@angular/core';
import { Observable, from as fromPromise } from 'rxjs';
import { switchMap, mergeMap, map } from 'rxjs/operators';
import { Action } from '@ngrx/store';
import { Actions, Effect, ofType } from '@ngrx/effects';

import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
import { Card } from '../../models/card';
import * as cardActions from '../action/card.actions';

@Injectable()
export class CardEffects {
  @Effect()
  query$: Observable<Action> = this.actions$.pipe(
    ofType(cardActions.QUERY),
    switchMap((action) => {
      console.log(action);
      return this.afs.collection<Card>('cards').stateChanges();
    }),
    mergeMap((actions) => actions),
    map((action) => {
      return {
        type: `[Card] ${action.type}`,
        payload: {
          id: action.payload.doc.id,
          ...action.payload.doc.data(),
        },
      };
    }),
  );

  @Effect()
  edit$: Observable<Action> = this.actions$.pipe(
    ofType(cardActions.EDIT_CARD),
    map((action: cardActions.EditCard) => action),
    switchMap((data) => {
      const ref = this.afs.doc<Card>(`card${data.id}`);
      return fromPromise(ref.update(data.changes));
    }),
    map(() => new cardActions.Success()),
  );

  @Effect()
  delete$: Observable<Action> = this.actions$.pipe(
    ofType(cardActions.DELETE_CARD),
    map((action: cardActions.DeleteCard) => action),
    switchMap((data) => {
      const ref = this.afs.doc<Card>(`card${data.id}`);
      return fromPromise(ref.delete());
    }),
    map(() => new cardActions.Success()),
  );

  constructor(private actions$: Actions,
              private afs: AngularFirestore) {}
}

card.reducer.ts

import { EntityState, createEntityAdapter } from '@ngrx/entity';
import * as CardActions from '../action/card.actions';
import { Card } from '../../models/card';

export const cardAdapter = createEntityAdapter<Card>();

export type Action = CardActions.All;
export interface State extends EntityState<Card> {}

export const initialState: State = cardAdapter.getInitialState();

export function cardReducer(state: State = initialState, action: Action) {
  console.log(action.type, state);

  switch (action.type) {
    case CardActions.ADD_CARD:
      return cardAdapter.addOne(action.payload, state);

    case CardActions.EDIT_CARD:
      return cardAdapter.updateOne({
        id: action.id,
        changes: action.changes,
      }, state);

    case CardActions.DELETE_CARD:
      return cardAdapter.removeOne(action.id, state);

    default:
      return state;
  }
}

card.actions.ts

import { Action } from '@ngrx/store';
import { Card } from '../../models/card';

export const ADD_CARD = '[Card] Add Card';
export const GET_ALL_CARDS = '[Card] Get All Cards';
export const EDIT_CARD = '[Card] Edit Card';
export const DELETE_CARD = '[Card] Delete Card';

export const QUERY = '[Card] Query cards';

export const ADDED = '[Card] Added';
export const EDITED = '[Card] Edited';
export const DELETED = '[Card] Deleted';

export const SUCCESS = '[Card] Success';

export class AddCard implements Action {
  readonly type = ADD_CARD;

  constructor(public payload: Card) {}
}

export class GetAllCards implements Action {
  readonly type = GET_ALL_CARDS;

  constructor(public payload: Card[]) {}
}

export class EditCard implements Action {
  readonly type = EDIT_CARD;

  constructor(public id: number,
              public changes: Partial<Card>) {}
}

export class DeleteCard implements Action {
  readonly type = DELETE_CARD;

  constructor(public id: number) {}
}

export class Query implements Action {
  readonly type = QUERY;

  constructor() {}
}

export class Added implements Action {
  readonly type = ADDED;

  constructor(public payload: Card) {}
}

export class Edited implements Action {
  readonly type = EDITED;

  constructor(public payload: Card) {}
}

export class Deleted implements Action {
  readonly type = DELETED;

  constructor(public payload: Card) {}
}

export class Success implements Action {
  readonly type = SUCCESS;

  constructor() {}
}

export type All
  = AddCard
  | GetAllCards
  | EditCard
  | DeleteCard
  | Query
  | Added
  | Edited
  | Deleted
  | Success

【问题讨论】:

  • 请包括任何进一步的调试步骤。 tap((_) =&gt; console.log(_) 在上面的代码中为您提供了 cards$ 可观察的内容,这是您所期望的吗? QUERY 的结果是什么(为什么不是 GetAllCards?),这是否反映在状态中?
  • @AndrewAllen,tap((_) =&gt; console.log(_) 我想看看是否有任何数据,但我得到的是空数组。 GetAllCards 是我忘记删除的代码中未使用的部分,它可能暗示与 Query 相同的逻辑。查询返回卡片数据,该数据不反映在状态中。
  • 我在你的减速器中看不到GET_ALL_CARDS 的动作,你需要像cardAdaptor.setAll() 这样的东西

标签: angular firebase google-cloud-firestore ngrx


【解决方案1】:

我认为您可能缺少减速器中的 GET_ALL_CARDS 操作。像这样的

card.reducer.ts

export function cardReducer(state: State = initialState, action: Action) {
  console.log(action.type, state);

  switch (action.type) {
    case CardActions.GET_ALL_CARDS:
     return cardAdaptor.setAll(action.payload, {
        ...state
       });

    case CardActions.ADD_CARD:
     return cardAdapter.addOne(action.payload, state);

    ...etc
  }
}

【讨论】:

    猜你喜欢
    • 2018-06-19
    • 2023-02-18
    • 1970-01-01
    • 2020-06-29
    • 2021-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-05
    相关资源
    最近更新 更多