// app/simple-form/user.ts
export interface User {
id: number;
name: string;
birthdate: Date;
favoriteColor?: string;
}
<!-- app/simple-form/user-form-ngxs/user-form-ngxs.component.html -->
<h1>Simple Form (NGXS)</h1>
<mat-card>
<form (ngSubmit)="submit()" #userForm="ngForm">
<mat-form-field>
<mat-label>Name</mat-label>
<input
matInput
required
name="name"
[ngModel]="(user$ | async)?.name"
(ngModelChange)="setName($event)"
#name="ngModel"
/>
<mat-error *ngIf="name.invalid">You must enter a value</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Birth date</mat-label>
<input
matInput
required
name="birthdate"
[matDatepicker]="picker"
[ngModel]="(user$ | async)?.birthdate"
(ngModelChange)="setBirthdate($event)"
#birthdate="ngModel"
/>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error *ngIf="birthdate.invalid">You must enter a value</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Favorite color</mat-label>
<mat-select
name="favoriteColor"
[ngModel]="(user$ | async)?.favoriteColor"
(ngModelChange)="setFavoriteColor($event)"
>
<mat-option *ngFor="let color of colors" [value]="color">{{
color
}}</mat-option>
</mat-select>
</mat-form-field>
<div class="form-buttons">
<button
mat-raised-button
type="submit"
color="primary"
[disabled]="(loading$ | async) || !userForm.form.valid"
>
Submit
</button>
</div>
</form>
</mat-card>
<h2>User (JSON)</h2>
<pre
>{{ user$ | async | json }}
</pre>
// app/simple-form/user-form-ngxs/user-form-ngxs.component.ts
import { Component } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { withLatestFrom } from 'rxjs/operators';
import { User } from 'src/app/simple-form/user';
import { UserFormNgxs } from 'src/app/simple-form/user-form-ngxs/user-form-ngxs.actions';
import { UserFormNgxsSelectors } from 'src/app/simple-form/user-form-ngxs/user-form-ngxs.selectors';
@Component({
selector: 'app-user-form-ngxs',
templateUrl: './user-form-ngxs.component.html',
})
export class UserFormNgxsComponent {
readonly colors = ['Red', 'Green', 'Blue'];
@Select(UserFormNgxsSelectors.user)
user$!: Observable<User>;
@Select(UserFormNgxsSelectors.loading)
loading$!: Observable<boolean>;
constructor(
private readonly store: Store,
private readonly snackBar: MatSnackBar
) {}
setName(name: string) {
this.store.dispatch(new UserFormNgxs.SetName(name));
}
setBirthdate(birthdate: Date) {
this.store.dispatch(new UserFormNgxs.SetBirthdate(birthdate));
}
setFavoriteColor(favoriteColor: string) {
this.store.dispatch(new UserFormNgxs.SetFavoriteColor(favoriteColor));
}
submit(): void {
this.store
.dispatch(new UserFormNgxs.Submit())
.pipe(withLatestFrom(this.user$))
.subscribe(([, { id }]) => {
this.snackBar.open(`User saved with ID #${id}`, 'Close');
});
}
}
// app/simple-form/user.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { User } from 'src/app/simple-form/user';
export interface UserSaveResponse {
id: number;
}
@Injectable({
providedIn: 'root',
})
export class UserService {
save(user: User): Observable<UserSaveResponse> {
console.log('Save:', user);
return of({
id: 1,
}).pipe(delay(1000));
}
}
// app/simple-form/user-form-ngxs/user-form-ngxs.state.ts
import { Injectable } from '@angular/core';
import { Action, State, StateContext } from '@ngxs/store';
import { finalize, tap } from 'rxjs/operators';
import { User } from 'src/app/simple-form/user';
import { UserFormNgxs } from 'src/app/simple-form/user-form-ngxs/user-form-ngxs.actions';
import { UserService } from 'src/app/simple-form/user.service';
export interface UserFormNgxsStateModel {
user: User;
loading: boolean;
}
@State<UserFormNgxsStateModel>({
name: 'userFormNgxs',
defaults: {
user: {
id: 0,
name: '',
birthdate: new Date(),
favoriteColor: '',
},
loading: false,
},
})
@Injectable()
export class UserFormNgxsState {
constructor(private readonly userService: UserService) {}
@Action(UserFormNgxs.SetName)
setName(
context: StateContext<UserFormNgxsStateModel>,
{ name }: UserFormNgxs.SetName
) {
const state = context.getState();
context.patchState({
user: { ...state.user, name },
});
}
@Action(UserFormNgxs.SetBirthdate)
setBirthdate(
context: StateContext<UserFormNgxsStateModel>,
{ birthdate }: UserFormNgxs.SetBirthdate
) {
const state = context.getState();
context.patchState({
user: { ...state.user, birthdate },
});
}
@Action(UserFormNgxs.SetFavoriteColor)
setFavoriteColor(
context: StateContext<UserFormNgxsStateModel>,
{ favoriteColor }: UserFormNgxs.SetFavoriteColor
) {
const state = context.getState();
context.patchState({
user: { ...state.user, favoriteColor },
});
}
@Action(UserFormNgxs.Submit)
submit(context: StateContext<UserFormNgxsStateModel>) {
const state = context.getState();
context.patchState({
loading: true,
});
return this.userService.save(state.user).pipe(
tap(({ id }) =>
context.patchState({
user: { ...state.user, id },
})
),
finalize(() =>
context.patchState({
loading: false,
})
)
);
}
}
// app/simple-form/user-form-ngxs/user-form-ngxs.actions.ts
export namespace UserFormNgxs {
export class SetName {
static readonly type = '[UserFormNgxs] SetName';
constructor(readonly name: string) {}
}
export class SetBirthdate {
static readonly type = '[UserFormNgxs] SetBirthdate';
constructor(readonly birthdate: Date) {}
}
export class SetFavoriteColor {
static readonly type = '[UserFormNgxs] SetFavoriteColor';
constructor(readonly favoriteColor: string) {}
}
export class Submit {
static readonly type = '[UserFormNgxs] Submit';
}
}
// app/simple-form/user-form-ngxs/user-form-ngxs.selectors.ts
import { Selector } from '@ngxs/store';
import { User } from 'src/app/simple-form/user';
import {
UserFormNgxsState,
UserFormNgxsStateModel,
} from 'src/app/simple-form/user-form-ngxs/user-form-ngxs.state';
export class UserFormNgxsSelectors {
@Selector([UserFormNgxsState])
static user({ user }: UserFormNgxsStateModel): User {
return user;
}
@Selector([UserFormNgxsState])
static loading({ loading }: UserFormNgxsStateModel): boolean {
return loading;
}
}