Skip to content

Instantly share code, notes, and snippets.

@kaitwalla
Created January 18, 2023 15:58
Show Gist options
  • Save kaitwalla/076387e2dc205361d8b29088bfb2eeda to your computer and use it in GitHub Desktop.
Save kaitwalla/076387e2dc205361d8b29088bfb2eeda to your computer and use it in GitHub Desktop.
NGXS vs. NGRX vs. Elf
// reports.service.ts
import { trackRequestResult } from '@ngneat/elf-requests';
import { setReports } from './reports.repository';
export function fetchReports() {
return http.get(reportsUrl).pipe(
tap(setReports),
trackRequestResult(['reports'])
);
}
// reports.repository.ts
import { createStore, withProps } from '@ngneat/elf';
import { select } from '@ngneat/elf';
import { withEntities, withUIEntities, withActiveId } from '@ngneat/elf-entities';
interface Report {
id: number,
name: string
}
interface ReportUI {
id: number,
editMode: boolean
}
export const reportStore = createStore(
{ name: 'reports' },
withEntities<Report>(),
withUIEntities<ReportUI>(),
withActiveId(),
joinRequestResult(['reports'])
);
export class ReportsRepository {
reports$ = reportStore.pipe(select((state) => state.reports));
updateReports(reports: ReportProps['reports']) {
reportStore.update((state) => ({
...state,
reports
}));
}
}
// reports.component.ts
import { selectEntity } from '@ngneat/elf-entities';
import { selectActiveEntity } from '@ngneat/elf-entities';
const active$ = reportsStore.pipe(selectActiveEntity());
reportStore.subscribe((state) => {
console.log(state);
})
// returns observable that calls distinctUntilChanged()
const report$ = reportStore.pipe(select((state) => state.reports));
// returns value once without subscribing
const state = reportStore.getValue();
const report$ = reportStore.pipe(selectEntity(id));
entities$.subscribe(
({ isLoading, isError, isSuccess, data, error, status }) => {
console.log(
isLoading,
isError,
isSuccess,
status,
successfulRequestsCount,
data, // typed as Todo[]
error
);
}
);
// reports.store.ts
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
interface ReportsState {
reports: Report[];
}
// reports.action.ts
import { createAction, props } from '@ngrx/store';
export const getReports = createAction('[Reports] Get');
// reports.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { getReports, getReportsSuccess, getReportsError } from './reports.actions';
const reportsReducer = createReducer(
initialState,
on(getReports, state => ({ ...state, loading: true })),
on(getReportsSuccess, (state, { reports }) => ({ ...state, loading: false, reports })),
on(getReportsError, (state, { error }) => ({ ...state, loading: false, error }))
);
//reports.effect.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { map, catchError, switchMap } from 'rxjs/operators';
import { getReports, getReportsSuccess, getReportsError } from './reports.actions';
@Injectable()
export class ReportsEffects {
constructor(private actions$: Actions, private http: HttpClient) {}
getReports$ = createEffect(() =>
this.actions$.pipe(
ofType(getReports),
switchMap(() =>
this.http.get<Report[]>('https://my-api.com/reports').pipe(
map(reports => getReportsSuccess({ reports })),
catchError(error => of(getReportsError({ error })))
)
)
)
);
}
// app.module.ts
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from '../environments/environment';
import { reportsReducer } from './store/reports.reducer';
import { ReportsEffects } from './store/reports.effects';
@NgModule({
imports: [
StoreModule.forRoot({ reports: reportsReducer }),
EffectsModule.forRoot([ReportsEffects]),
StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production })
],
providers: [ReportsService]
})
export class AppModule { }
// reports.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { getReports } from './store/reports.actions';
@Component({
selector: 'app-root',
template: `
<div *ngFor="let report of reports$ | async">
{{ report.title }}
</div>
`
})
export class AppComponent {
reports$ = this.store.select(state => state.reports);
constructor(private store: Store) {
this.store.dispatch(getReports());
}
}
// reports.state.ts
import { State, Action, StateContext } from '@ngxs/store';
@State<Report[]>({
name: 'reports',
defaults: []
})
export class ReportsState {
}
// reports.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class ReportsService {
constructor(private http: HttpClient) {}
getReports() {
return this.http.get<Report[]>('https://my-api.com/reports');
}
}
//reports.action.ts
import { Get, StateContext } from '@ngxs/store';
import { ReportsService } from './reports.service';
export class GetReports {
static readonly type = '[Reports] Get';
}
@Injectable()
export class ReportsActions {
constructor(private reportsService: ReportsService) {}
@Get(GetReports)
getReports({ patchState }: StateContext<Report[]>) {
return this.reportsService.getReports().pipe(
tap((reports: Report[]) => {
patchState(reports);
})
);
}
}
// reports.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngxs/store';
import { GetReports } from './reports.actions';
@Component({
selector: 'app-root',
template: `
<div *ngFor="let report of reports$ | async">
{{ report.title }}
</div>
`
})
export class AppComponent {
reports$ = this.store.select(state => state.reports);
constructor(private store: Store) {
this.store.dispatch(new GetReports());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment