Данные с сервера приходят в полном объёме, можно увидеть в консоли (метод tap в effect.ts). Но html их не рисует? И как обработать error в компоненте?

actions.ts

import { Action } from "@ngrx/store";

export enum ArticlesActions {
    LoadArticles = '[Articles Page] Load Articles',
    ArticlesLoadedSuccess = '[Articles Page] Articles Loaded Success',
    ArticlesLoadedError = '[Articles Page] Articles Loaded Error',
  }
  
  export interface Article {
    id: number;
    nameArticle: string;
    author: string;
    pages: number
  }
  
  export class LoadArticles implements Action {
    readonly type = ArticlesActions.LoadArticles;
  }
  
  export class ArticlesLoadedSuccess implements Action {
    readonly type = ArticlesActions.ArticlesLoadedSuccess;
  
    constructor(public payload: { articles: Article[] }) {}
  }
  
  export class ArticlesLoadedError implements Action {
    readonly type = ArticlesActions.ArticlesLoadedError;
  }
  
  export type ArticlesUnion =
    | LoadArticles
    | ArticlesLoadedSuccess
    | ArticlesLoadedError;

effect.ts

import { Injectable } from "@angular/core";
import { Actions, Effect, ofType } from "@ngrx/effects";
import { Observable, of } from "rxjs";
import { map, mergeMap, catchError, tap } from 'rxjs/operators';

import { ArticlesService } from "../services/articles.service";
import { ArticlesActions, ArticlesLoadedError, ArticlesLoadedSuccess } from "./articles.actions";

@Injectable()
export class ArticlesEffects {

    constructor(
        private actions$: Actions,
        private articlesService: ArticlesService
    ) {}

    @Effect()
      loadArticles$ = this.actions$.pipe(
        ofType(ArticlesActions.LoadArticles),
        mergeMap(() =>
          this.articlesService.getArticles().pipe(
            map(
              (articles: any) =>
                new ArticlesLoadedSuccess({
                  articles: articles,
                })
            ),
            tap(action => console.log(action.payload.articles)),
            catchError(() => of(new ArticlesLoadedError()))
          )
        )
    );
}

reduser.ts

import { createFeatureSelector, createSelector, State } from "@ngrx/store";
import { Article, ArticlesActions, ArticlesUnion } from "./articles.actions";

export interface ArticlesState {
    list: Article[];
  }
  
  const initialState: ArticlesState = {
    list: [],
  };
  
export const articlesReducer = (state = initialState, action: ArticlesUnion) => {
    switch (action.type) {
        case ArticlesActions.ArticlesLoadedSuccess:
            return {
              ...state,
              list: action.payload.articles,
            };
        case ArticlesActions.ArticlesLoadedError:
            return {
              ...state,
              list: [],
            };
        default:
            return state;
    }
}
  
const selectArticles = createFeatureSelector<ArticlesState>('articles')
  
export const selectArticlesList = createSelector(
    selectArticles,
    (state: ArticlesState) => state.list
);

articles.component.ts

import { Component, OnInit } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { Article, LoadArticles } from './store/articles.actions';
import { ArticlesState, selectArticlesList } from './store/articles.reducer';

@Component({
  selector: 'app-articles',
  templateUrl: './articles.component.html',
  styleUrls: ['./articles.component.scss']
})
export class ArticlesComponent implements OnInit {

  articles$: Observable<Article[]>

  constructor(private store: Store<ArticlesState>) {
  }
  ngOnInit(): void {
    this.store.dispatch(new LoadArticles());
    this.articles$ = this.store.pipe(select(selectArticlesList));
    this.error$ = ........
  }
}

articles.component.ts

<table class="table table-hover">
  <thead>
    <tr class="table-primary">
      <th scope="col">№</th>
      <th scope="col">name article</th>
      <th scope="col">author</th>
      <th scope="col">pages</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <!-- <td colspan="5" class="alert alert-danger" *ngIf="error$ | async as error">{{error}}</td> -->
    </tr>
    <tr>
    <tr *ngFor="let article of articles$ | async">
      <th scope="row">{{article.id}}</th>
      <td>{{article.nameArticle}}</td>
      <td>{{article.author}}</td>
      <td>{{article.pages}}</td>
    </tr>
  </tbody>
</table>

articles.service.ts

import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";

@Injectable({ providedIn: 'root' })
export class ArticlesService {
  constructor(private http: HttpClient) {}

  getArticles() {
    return this.http.get('http://localhost:8000/api/articles');
  }
}

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { EffectsModule } from '@ngrx/effects';
import { environment } from '../environments/environment';
import { ArticlesComponent } from './articles/articles.component';
import { ArticlesEffects } from './articles/store/articles.effects';
import { articlesReducer } from './articles/store/articles.reducer';

@NgModule({
  declarations: [
    AppComponent,
    ArticlesComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    StoreModule.forRoot(articlesReducer),
    EffectsModule.forRoot([ArticlesEffects]),
    StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

store

{
  payload: {
    articles: [
      {
        id: 1,
        nameArticle: 'nameArticle_1',
        author: 'author_1',
        pages: 1
      },
      {
        id: 2,
        nameArticle: 'nameArticle_2',
        author: 'author_2',
        pages: 1
      },
      {
        id: 3,
        nameArticle: 'nameArticle_3',
        author: 'author_3',
        pages: 3
      }
    ]
  },
  type: '[Articles Page] Articles Loaded Success'
}

Ответы (1 шт):

Автор решения: Alexy

StoreModule ожидает что вы передадите первым параметром ActionReducerMap

В вашем случае это будет что-то вида:

export interface IAppState {
  articles: ArticlesState
}

export const appReducer: ActionReducerMap<IAppState, any> = {
  articles: articlesReducer
}

Пример https://codesandbox.io/s/frosty-allen-wot04

→ Ссылка