Beginner's guide to State Management using NGXS
Tags: angular, beginners, redux, webdev
Published: 2021-08-19 12:48:34

>NGXS is a state management pattern + library for Angular. 

>It acts as a single source of truth for your application's state, providing simple rules for predictable state mutations.

>NGXS is modeled after the CQRS pattern popularly implemented in libraries like Redux and NgRx but reduces boilerplate by using modern TypeScript features such as classes and decorators.

Getting started with NGXS as a beginner can be daunting, not because it is some kind of rocket science but essentially due to the fact that not many resources are available to learn it in a right way.

![Block Schematic of NGXS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jg9lyhjajpvpk5pem3tb.png)

In this tutorial, we will use Angular along with NGXS to create a simple CRUD application consuming dummy REST APIs. 

If you're running out of patience already you can hop onto [StackBlitz](https://stackblitz.com/github/siddheshthipse/learning-ngxs) and see for yourself what we're gonna do.

#### Prerequisites
* Basic knowledge of Angular 2+ is must.
* Prior knowledge of RxJS would be helpful but isn't absolutely necessary.

### So let's get started

#### Step 1: Install Angular CLI

`npm install -g @angular/cli`
OR
`yarn add global @angular/cli`

Create a new Angular project, let's call it 'learning-ngxs'
`ng new learning-ngxs`

####Step 2: Install NGXS library
First go to the project folder
`cd learning-ngxs`

Then enter this command
`npm install @ngxs/store --save`
or if you're using yarn
`yarn add @ngxs/store`

####Step 3: Installing Plugins(optional)
* Although this step is optional, I would highly recommend you to go through it since Logger and Devtools are the two extremely handy dev dependencies. 
* These plugins help us in tracking the changes our state goes through.

For installing Logger and Devtools plugins fire the commands `@ngxs/logger-plugin --save` & `@ngxs/devtools-plugin --save-dev` respectively.

####Step 4: Importing Modules
This is how your `app.module.ts` file will look after importing the necessary modules

```
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {HttpClientModule} from '@angular/common/http';
import {FormsModule,ReactiveFormsModule} from '@angular/forms';
//For NGXS
import { NgxsModule } from '@ngxs/store';
import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin';
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';


import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { GeneralComponent } from './general/general.component';
import { AppState } from './states/app.state';
import { DesignutilityService } from './designutility.service';

@NgModule({
  declarations: [
    AppComponent,
    GeneralComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule,
    ReactiveFormsModule,

    NgxsModule.forRoot([]), NgxsLoggerPluginModule.forRoot(), NgxsReduxDevtoolsPluginModule.forRoot()
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

```
#### Step 5: Creating Components and Services
Let's create a component say 'general' for displaying the contents of our state
`ng g c general`

Create a service called 'designutility' for interacting with the server to `GET`, `POST`, `UPDATE` and `DELETE` the data.
`ng g s designutility`

Do not forget to add `DesignutilityService` inside the `providers` array in `app.module.ts`.
```
providers: [DesignutilityService]
```
Make sure that you've imported all the modules mentioned in Step 4.

#### Step 6: Creating Actions
Create a new folder named 'actions' inside src>app
Inside the actions folder, create a new file named `app.action.ts`
```
//Here we define four actions for CRUD operations respectively

//Read
export class GetUsers {
    static readonly type = '[Users] Fetch';
}

//Create
export class AddUsers {
    static readonly type = '[Users] Add';
    constructor(public payload: any) { }
}

//Update
export class UpdateUsers {
    static readonly type = '[Users] Update';
    constructor(public payload: any, public id: number, public i:number) { }
}

//Delete
export class DeleteUsers {
    static readonly type = '[Users] Delete';
    constructor(public id: number) { }
}


```
>Actions can either be thought of as a command which should trigger something to happen, or as the resulting event of something that has already happened.

>Each action contains a type field which is its unique identifier.

Actions are dispatched from the components to make the desirable changes to the State.

You might have noticed that except for `GetUsers`, in all other actions we have a parameterized constructor.
* These parameters are nothing but the data which would be coming from various components whenever the action is dispatched.
* For example in `AddUsers` action we have a constructor with parameter named `payload`, this payload will basically comprise of information about the new user.
* This data about the newly created user will get stored inside the State whenever the action `AddUsers` is dispatched from the component.


#### Step 7: Working with Service
In the `designutility.service.ts`, let’s add HTTP calls to fetch, update, add and delete to-do items. 
In this tutorial, we are using JSONPlaceholder for making fake API calls.
```
import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class DesignutilityService {

  constructor(private http:HttpClient) { }

  fetchUsers(){
    return this.http.get('https://jsonplaceholder.typicode.com/users');
  }

  addUsers(userData){
    return this.http.post('https://jsonplaceholder.typicode.com/users',userData);
  }

  deleteUser(id:number){
    return this.http.delete('https://jsonplaceholder.typicode.com/users/'+id);
  }

  updateUser(payload,id:number){
    return this.http.put('https://jsonplaceholder.typicode.com/users/'+id, payload);
  }
}
```
#### Step 8: Creating State
Now we've arrived at the most important part of this tutorial.

Create a new folder named 'states' inside src>app.
Inside the states folder, create a new file named `app.state.ts`
```
import { Injectable } from "@angular/core";
import { Action, Selector, State, StateContext } from "@ngxs/store";
import { DesignutilityService } from "../designutility.service";
import { tap } from 'rxjs/operators';
import { AddUsers, DeleteUsers, GetUsers, UpdateUsers } from "../actions/app.action";

export class UserStateModel {
    users: any
}

@State<UserStateModel>({
    name: 'appstate',
    defaults: {
        users: []
    }
})

@Injectable()
export class AppState {
    constructor(private _du: DesignutilityService) { }

    @Selector()
    static selectStateData(state:UserStateModel){
        return state.users;
    }
    
    @Action(GetUsers)
    getDataFromState(ctx: StateContext<UserStateModel>) {
        return this._du.fetchUsers().pipe(tap(returnData => {
            const state = ctx.getState();

            ctx.setState({
                ...state,
                users: returnData //here the data coming from the API will get assigned to the users variable inside the appstate
            })
        }))
    }

    @Action(AddUsers)
    addDataToState(ctx: StateContext<UserStateModel>, { payload }: AddUsers) {
        return this._du.addUsers(payload).pipe(tap(returnData => {
            const state=ctx.getState();
            ctx.patchState({
                users:[...state.users,returnData]
            })
        }))
    }

    @Action(UpdateUsers)
    updateDataOfState(ctx: StateContext<UserStateModel>, { payload, id, i }: UpdateUsers) {
        return this._du.updateUser(payload, i).pipe(tap(returnData => {
            const state=ctx.getState();

            const userList = [...state.users];
            userList[i]=payload;
            
            ctx.setState({
                ...state,
                users: userList,
            });
        }))
    }

    @Action(DeleteUsers)
    deleteDataFromState(ctx: StateContext<UserStateModel>, { id }: DeleteUsers) {
        return this._du.deleteUser(id).pipe(tap(returnData => {
            const state=ctx.getState();
            console.log("The is is",id)
            //Here we will create a new Array called filteredArray which won't contain the given id and set it equal to state.todo
            const filteredArray=state.users.filter(contents=>contents.id!==id);

            ctx.setState({
                ...state,
                users:filteredArray
            })
        }))
    }
}
```

##### About Selector()
* The `Selector()` is used to get a specific piece of data from the `AppState`.
* In our case we are grabbing the `users` array which is present inside the `AppState`
* The selector is used to return the data back to the component with the help of `Select()` as shown in Step 10.


#### Step 9: Documenting the State in app.module.ts
Now that we are done with the creation of `AppState`, it is necessary to document this state in our `app.module.ts` file.

So go to imports array inside `app.module.ts` and make the necessary change.
```
NgxsModule.forRoot([AppState]), NgxsLoggerPluginModule.forRoot(), NgxsReduxDevtoolsPluginModule.forRoot()
```
#### Step 10: Working with Component
Component is the place from where we're gonna control the contents of the state, in our case it's `general.component.ts`

We are performing basic CRUD operations on our `AppState`.

For that, we have a table to display existing users, update user information, remove user and a form to insert a new user into the `AppState`.

```
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { AddUsers, DeleteUsers, GetUsers, UpdateUsers } from '../actions/app.action';
import { AppState } from '../states/app.state';

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

  //Here I have used Reactive Form, you can also use Template Driven Form instead
  userForm: FormGroup;
  userInfo: [];
  @Select(AppState.selectStateData) userInfo$: Observable<any>;

  constructor(private store: Store, private fb: FormBuilder) { }

  ngOnInit(): void {
    this.userForm = this.fb.group({
      id: [''],
      name: [''],
      username: [''],
      email: [''],
      phone: [''],
      website: ['']
    })

    this.store.dispatch(new GetUsers());

    this.userInfo$.subscribe((returnData) => {
      this.userInfo = returnData;
    })
  }

  addUser() {
    this.store.dispatch(new AddUsers(this.userForm.value));
    this.userForm.reset();
  }

  updateUser(id, i) {

    const newData = {
      id: id,
      name: "Siddhesh Thipse",
      username: "iamsid2399",
      email: 'siddheshthipse09@gmail.com',
      phone: '02138-280044',
      website: 'samplewebsite.com'
    }

    this.store.dispatch(new UpdateUsers(newData, id, i));
  }

  deleteUser(i) {
    this.store.dispatch(new DeleteUsers(i));
  }
}
```
##### Few Important Points
* Import `select` and `store` from `ngxs/store`
* The `Select()` is basically used to grab the data present in the `AppState`.
* Notice how we are dispatching various actions to perform the desired operations, for example if we want to delete a user, we are dispatching an action named `DeleteUsers` and passing `i` (userid) as a parameter.
* So that the user having userid equal to `i` will get deleted from the `AppState`.

For designing part I have used Bootstrap 5, but you can totally skip using it if UI isn't your concern as of now.

After creating the basic UI, this is how our `general.component.html` will look like
```
<div class="container-fluid">
  <h2 style="text-decoration: underline;">Getting started with NGXS</h2>
  <div class="row my-4">
    <div class="col-md-3">
      <h5 style="color: grey;">Add new user to State</h5>
      <form [formGroup]="userForm" (ngSubmit)="addUser()">
        <label class="form-label">ID</label>
        <input type="text" class="form-control mb-2" placeholder="User ID" formControlName="id">
        <label class="form-label">Name</label>
        <input type="text" class="form-control mb-2" placeholder="Enter Name" formControlName="name">
        <label class="form-label">Username</label>
        <input type="text" class="form-control mb-2" placeholder="Enter a unique username" formControlName="username">
        <label class="form-label">Email</label>
        <input type="email" class="form-control mb-2" placeholder="example@abcd.com" formControlName="email">
        <label class="form-label">Phone</label>
        <input type="number" class="form-control mb-2" placeholder="Enter Contact No." formControlName="phone">
        <label class="form-label">Website</label>
        <input type="email" class="form-control mb-2" placeholder="Enter website name" formControlName="website">
        <button type="submit" class="btn btn-primary btn-sm mt-2">Add User</button>
      </form>
    </div>
    <div class="col-md-9">
      <h5 style="color: grey;">User Information</h5>
      <table class="table">
        <thead>
          <tr>
            <th scope="col">ID</th>
            <th scope="col">Name</th>
            <th scope="col">Username</th>
            <th scope="col">Email</th>
            <th scope="col">Phone</th>
            <th scope="col">Website</th>
            <th scope="col">Update</th>
            <th scope="col">Delete</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let contents of userInfo; index as i">
            <th scope="row">{{contents.id}}</th>
            <td>{{contents.name}}</td>
            <td>{{contents.username}}</td>
            <td>{{contents.email}}</td>
            <td>{{contents.phone}}</td>
            <td>{{contents.website}}</td>
            <td><button class="btn btn-outline-warning btn-sm" (click)="updateUser(contents.id,i)"><i
                  class="bi bi-pencil-fill"></i></button></td>
            <td><button class="btn btn-danger btn-sm" (click)="deleteUser(contents.id)"><i
                  class="bi bi-trash-fill"></i></button></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>
```
![Landing Page UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gmol3081o9g5cryy6hm1.png)

That's it, we have successfully implemented State Management in our Angular Application.

Now there is definitely more to NGXS than this, but once you've completely understood the basics, learning the advanced stuff is a cakewalk.

Incase of any suggestions/queries please feel free to comment down below.

Source code available on [Github](https://github.com/siddheshthipse/learning-ngxs)