Данные с сервера приходят в полном объёме, можно увидеть в консоли (метод 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
}