Part Two: NGXS State Management: Beginner's Guide.
Published: 2023-07-04 07:42:53

[For part one..](url)

## How are the different concepts organised?

Now that you have gained familiarity with the various concepts of NGXS, let's explore how they interact with each other through a schema, which provides a clear visual representation of their relationships and interactions.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozq0ap7gwp5a8adnxytl.png)
<u>Schema explaining NGXS functioning</u>

--- 

## After we have set up NGXS, Now lets dive deep into our example:

add the following code in the imports of the `@NgModule:`


`NgxsModule.forRoot([TodoState], {
  developmentMode: !environment.production
}),
`

## like this:

```
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
    NgxsModule.forRoot([TodoState], {
      developmentMode: !environment.production
    }),
  ]
})
export class AppModule { }
```

---

As we discussed earlier in the theoretical part, everything begins with a component. Therefore, let's proceed by writing the code for the `app.component.ts` file:

```
import { Component } from '@angular/core';
import {Select, Store} from '@ngxs/store';
import {AddTodo, EmptyTodo} from '../store/todo.actions';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  
  newTodo = '';

  constructor(private readonly store: Store) {}

  onAddTodo(): void {
    if (this.newTodo.length > 0) {
      this.store.dispatch(new AddTodo(this.newTodo));
    }
    this.newTodo = '';
  }

  onEmptyList(): void {
    this.store.dispatch(new EmptyTodo());
  }
```

---

## Let’s create the Actions in the `todo.actions.ts`:

```
export class AddTodo {
  static readonly type = '[Todo] Add';

  constructor(public newTodo: string) {
  }
}

export class EmptyTodo {
  static readonly type = '[Todo] Empty';
}
```

---

Since the `addTodo` action requires the new todo as an input, we need to declare the constructor with `newTodo` as a parameter.

Following the triggering of these actions, certain code will be executed to modify the state. This is where the `todo.state.ts` file comes into play.

```
import {Action, State, StateContext} from '@ngxs/store';
import {AddTodo, EmptyTodo} from './todo.actions';

export interface TodoStateModel {
  todoList: string[];
}

@State<TodoStateModel>({
  name: 'todo',
  defaults: {
    todoList: [],
  }
})
export class TodoState {

  @Action(AddTodo)
  addTodo({patchState, getState}: StateContext<TodoStateModel>, {newTodo}: AddTodo): void {
    patchState({todoList: [...getState().todoList, newTodo]});
  }

  @Action(EmptyTodo)
  emptyTodo({patchState}: StateContext<TodoStateModel>): void {
    patchState({todoList: []});
  }
}
```

The `@State` decorator allows us to define the initial state of the state container.

The functions preceded by the `@Action` annotation are associated with an action and will be executed when triggered.

---

## Let's take a closer look at the more complex `addTodo()` function to understand its components:


`First, it takes two parameters:`

`{patchState, getState}: StateContext<TodoStateModel}`, which provides `patchState()` and `getState()` functions to interact with the state defined in the same file.
`{newTodo}: AddTodo`, which allows us to access data from the triggered action.

---

As mentioned earlier, the state is immutable. To modify the value of one or more state elements, NGXS provides the `patchState()` function. Here's an example of how it's used:

`patchState({todoList: [...getState().todoList, newTodo]});
`

This function enables developers to specify parameters only for the fields they intend to modify, without needing to create a copy of the entire state.

## To allow our component to access the `todoList` value from the state, we can add a getter function. Therefore, the `todo.state.ts` file is updated as follows:

```
import {Action, Selector, State, StateContext} from '@ngxs/store';
import {AddTodo, EmptyTodo} from './todo.actions';

export interface TodoStateModel {
  todoList: string[];
}

@State<TodoStateModel>({
  name: 'todo',
  defaults: {
    todoList: [],
  }
})
export class TodoState {

  @Selector()
  static getTodoList(state: TodoStateModel): string[] {
    return state.todoList;
  }

  @Action(AddTodo)
  addTodo({patchState, getState}: StateContext<TodoStateModel>, {newTodo}: AddTodo): void {
    patchState({todoList: [...getState().todoList, newTodo]});
  }

  @Action(EmptyTodo)
  emptyTodo({patchState}: StateContext<TodoStateModel>): void {
    patchState({todoList: []});
  }
}
```

As you can observe, we have added the `getTodoList()` function, which takes the state as a parameter and is annotated with `@Selector()`.

Whenever the value of `todoList` changes, the code within the selector function is executed, and any component that listens to todoList through `getTodoList()` will automatically receive the updated value.

---

As mentioned earlier, this function acts as a decorator for the returned value. Therefore, if you decide to sort todoList in alphabetical order within the selector, each component listening to todoList through this function will receive the sorted list.

To complete the loop, we need to return to our App component to allow it to access the value of todoList from the state using the newly created selector. To achieve this, we add a new variable in our component preceded by the @Select() annotation, with the selector function as the parameter.

## Here is the final version of `app.component.ts`:

```
import { Component } from '@angular/core';
import {Select, Store} from '@ngxs/store';
import {AddTodo, EmptyTodo} from '../store/todo.actions';
import {TodoState} from '../store/todo.state';
import {Observable} from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  @Select(TodoState.getTodoList) todoList$?: Observable<string[]>;

  newTodo = '';

  constructor(private readonly store: Store) {}

  onAddTodo(): void {
    if (this.newTodo.length > 0) {
      this.store.dispatch(new AddTodo(this.newTodo));
    }
    this.newTodo = '';
  }

  onEmptyList(): void {
    this.store.dispatch(new EmptyTodo());
  }
}
```

## Conclusion:

🥳🙌👏Congratulations! You have successfully learned how to set up and utilize **NGXS**. Your next task, should you decide to accept it, is to adapt the example we have worked on to meet the requirements of your own project.

For further information on **NGXS**, you can refer to the official documentation, available [HERE](https://www.ngxs.io/concepts/store).

Well done, and best of luck with your NGXS implementation! 💫👋👋