i'm currently trying to understand how NGXS works, so i was able to setup the state and action but when i call my service and i try to put the state into a variable i get an observable.
attached is the code and what i get from the console, i'm trying to get a list of items when my application starts so basically i have 2 states the clients array which will store the response from the service and a loading state which will change to false if i get a response, this is the first time i'm trying to handle this concepts so thanks in advance for the help
import { State, Action, StateContext, Selector } from "@ngxs/store";
import { Client } from "../models/client-model";
import { GetAllClients } from "../actions/client.actions";
import { ClientService } from "../services/client.service";
import { tap } from 'rxjs/operators';
export class ClientStateModel {
clients: Client[];
loading: boolean;
}
@State<ClientStateModel>({
name: "clients",
defaults: {
clients: [],
loading: true
}
})
export class ClientState {
constructor(private _clientService: ClientService) {}
@Action(GetAllClients)
getClients(ctx: StateContext<ClientStateModel>, action: GetAllClients){
return this._clientService.getClients().pipe(tap(clientsList => {
const state = ctx.getState();
ctx.setState({
...state,
clients: clientsList,
loading: false
});
}))
}
}
this is the service
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';
@Injectable()
export class ClientService {
public url: string;
constructor(
private _http: HttpClient
) {
this.url = environment.apiURL;
}
getClients(): Observable<any> {
const headers = new HttpHeaders({'Content-Type': 'application/json'});
return this._http.get(`${this.url}get-clients`,{headers: headers});
}
}
and this will be my attempt to consume the state
import { Component, OnInit } from "@angular/core";
import { Client } from "./models/client-model";
import { Router } from "@angular/router";
import { Store } from "@ngxs/store";
import { GetAllClients } from "./actions/client.actions";
import { Observable } from "rxjs";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
title = "Managed Services Dashboard";
Clients: Observable<Client>;
isLoading;
constructor(private store: Store, private _router: Router) {}
ngOnInit(): void {
this.getClients();
}
getClients() {
this.store.dispatch(new GetAllClients()).subscribe(result => {
this.Clients = this.store.select(state => state.clients.clients);
})
}
goToSelectedClient(client) {
console.log(client);
this._router.navigate(["client-details"]);
}
}
this is what i get in the console.
Ok, so first on the service, if you're getting a list of clients back, you can make the return type more specific:
getClients(): Observable<Client[]> {
const headers = new HttpHeaders({'Content-Type': 'application/json'});
return this._http.get(`${this.url}get-clients`,{headers: headers});
}
Next, the action that gets the clients doesn't need an action parameter since you aren't passing any data in it. Also it can use patchState like this:
@Action(GetAllClients)
getClients(ctx: StateContext<ClientStateModel>) {
return _clientService.getClients().pipe(
tap ( (clients: Client[]) => {
ctx.patchState({ clients });
}),
);
}
In your component, you can use @Select to get an observable on client state changes:
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
@Select(ClientState) clientState$: Observable<ClientStateModel>;
title = "Managed Services Dashboard";
isLoading;
.
.
.
getClients() {
this.store.dispatch(new GetAllClients());
}
elsewhere in the component, or in the template, you can use the clientState$ observable:
<div class="list-group">
<a
class="list-group-item"
style="cursor: pointer"
*ngFor="let client of (clientState$ | async).clients">
{{ client.name }}
</a>
</div>
You can also subscribe to the observable to handle client list changes in the code. You'll notice I removed the subscribe to the dispatch. That is specified to return a void, but can be used to determine if an error happened in your action handler (say an HTTP error) - that you would want to handle in the component.