Skip to content

Instantly share code, notes, and snippets.

@peplocanto
Last active January 20, 2022 17:31
Show Gist options
  • Save peplocanto/c2ef92d7200658a5f31ed266f6302dd0 to your computer and use it in GitHub Desktop.
Save peplocanto/c2ef92d7200658a5f31ed266f6302dd0 to your computer and use it in GitHub Desktop.
Angular Akita Store

Angular Akita Store

Intro

Akita is a fantastic and lightweight library for state management.

Arch

<namespace>
  L <namespace>.store.ts
  L <namespace>.service.ts
  L <namespace>.query.ts  

Implementation

Store

import { Injectable } from '@angular/core';
import { Store, StoreConfig } from '@datorama/akita';
import { User } from '@Models/user';

export interface UserState {
  user: User;
  token: string;
}

export const createInitialUserState = (): UserState => ({
  user: undefined,
  token: undefined
});

@Injectable({ providedIn: 'root' })
@StoreConfig({ name: 'user' })
export class UserStore extends Store<UserState> {
  constructor() {
    super(createInitialUserState());
  }
}

Service

import { Injectable } from '@angular/core';
import { User } from '@Models/user';
import { UserStore } from './user.store';

@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private readonly store: UserStore) {}

  setUser(user: User): void {
    this.store.update((state) => ({ ...state, user }));
  }

  setToken(token: string): void {
    this.store.update((state) => ({ ...state, token }));
  }
}

Query

import { Injectable } from '@angular/core';
import { Query } from '@datorama/akita';
import { UserState, UserStore } from './user.store';

@Injectable({ providedIn: 'root' })
export class UserQuery extends Query<UserState> {
  user$ = this.select((state) => state.user);
  hasAuth$ = this.select((state) => !!state.token);

  get snapshot(): UserState {
    return this.getValue();
  }

  constructor(protected readonly store: UserStore) {
    super(store);
  }
}

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { CoreModule } from '@Core/core.module';
import { AkitaNgRouterStoreModule } from '@datorama/akita-ng-router-store';
import { AkitaNgDevtools } from '@datorama/akita-ngdevtools';
import { environment } from '@Environments/environment';
import { CoreConfiguration } from '@Types/core-configuration.contract';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

export const coreConfigurationFactory = (): CoreConfiguration => environment.coreConfig;

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule,
    AppRoutingModule,
    environment.production ? [] : AkitaNgDevtools.forRoot(), // HERE!
    AkitaNgRouterStoreModule,                                // HERE!
    CoreModule.forRoot(coreConfigurationFactory),
  ],
  providers: [{ provide: RouteReuseStrategy }],
  bootstrap: [AppComponent]
})
export class AppModule {}

Usage - user.component.ts

@Component({
  selector: 'user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.scss']
})
export class UserComponent {
  constructor(private userQuery: UserQuery, private userService: UserService) {}
  
  user$: Observable<User> = this.userQuery.user$.subscribe(); // mutable data
  user: User = this.userQuery.snapshot.user; // static data

  setUser(user: User) {
    this.userService.setUser(user);
  }
}

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment