// app/simple-form/user.ts
export interface User {
id: number;
name: string;
birthdate: Date;
favoriteColor?: string;
}
<!-- app/simple-form/user-form-ngxs-plugin/user-form-ngxs-plugin.component.html -->
<h1>User Form (NGXS with form-plugin)</h1>
<mat-card>
<form
ngxsForm="userFormNgxsPlugin.userForm"
[formGroup]="userForm"
(ngSubmit)="submit()"
>
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput required name="name" formControlName="name" />
<mat-error *ngIf="name?.errors?.required"
>You must enter a value</mat-error
>
</mat-form-field>
<mat-form-field>
<mat-label>Birth date</mat-label>
<input
matInput
required
name="birthdate"
formControlName="birthdate"
[matDatepicker]="picker"
/>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error *ngIf="birthdate?.errors?.required"
>You must enter a value</mat-error
>
</mat-form-field>
<mat-form-field>
<mat-label>Favorite color</mat-label>
<mat-select name="favoriteColor" formControlName="favoriteColor">
<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.valid"
>
Submit
</button>
</div>
</form>
</mat-card>
<h2>User (JSON)</h2>
<pre
>{{ user$ | async | json }}
</pre>
<h2>Form state (JSON)</h2>
<pre
>{{ userFormState$ | async | json }}
</pre>
// app/simple-form/user-form-ngxs-plugin/user-form-ngxs-plugin.component.ts
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';
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 { UserFormNgxsPlugin } from 'src/app/simple-form/user-form-ngxs-plugin/user-form-ngxs-plugin.actions';
import { UserFormNgxsPluginSelectors } from 'src/app/simple-form/user-form-ngxs-plugin/user-form-ngxs-plugin.selectors';
import { UserForm } from 'src/app/simple-form/user-form-ngxs-plugin/user-form-ngxs-plugin.state';
@Component({
selector: 'app-user-form-ngxs-plugin',
templateUrl: './user-form-ngxs-plugin.component.html',
})
export class UserFormNgxsPluginComponent {
readonly colors = ['Red', 'Green', 'Blue'];
readonly userForm = this.formBuilder.group({
name: [''],
birthdate: [''],
favoriteColor: [''],
});
@Select(UserFormNgxsPluginSelectors.form)
userFormState$!: Observable<UserForm>;
@Select(UserFormNgxsPluginSelectors.model)
user$!: Observable<User>;
@Select(UserFormNgxsPluginSelectors.loading)
loading$!: Observable<boolean>;
get name() {
return this.userForm.get('name');
}
get birthdate() {
return this.userForm.get('birthdate');
}
constructor(
private readonly formBuilder: FormBuilder,
private readonly store: Store,
private readonly snackBar: MatSnackBar
) {}
submit(): void {
this.store
.dispatch(new UserFormNgxsPlugin.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-plugin/user-form-ngxs-plugin.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 { UserFormNgxsPlugin } from 'src/app/simple-form/user-form-ngxs-plugin/user-form-ngxs-plugin.actions';
import { UserService } from 'src/app/simple-form/user.service';
export interface UserForm {
model: User;
status: string;
dirty: boolean;
}
export interface UserFormNgxsPluginStateModel {
userForm: UserForm;
loading: boolean;
}
@State<UserFormNgxsPluginStateModel>({
name: 'userFormNgxsPlugin',
defaults: {
userForm: {
model: {
id: 0,
name: '',
birthdate: new Date(),
favoriteColor: '',
},
status: 'INVALID',
dirty: false,
},
loading: false,
},
})
@Injectable()
export class UserFormNgxsPluginState {
constructor(private readonly userService: UserService) {}
@Action(UserFormNgxsPlugin.Submit)
submit(context: StateContext<UserFormNgxsPluginStateModel>) {
const state = context.getState();
context.patchState({
loading: true,
});
return this.userService.save(state.userForm.model).pipe(
tap(({ id }) =>
context.patchState({
userForm: {
...state.userForm,
model: {
...state.userForm.model,
id,
},
},
})
),
finalize(() =>
context.patchState({
loading: false,
})
)
);
}
}
// app/simple-form/user-form-ngxs-plugin/user-form-ngxs-plugin.actions.ts
export namespace UserFormNgxsPlugin {
export class Submit {
static readonly type = '[UserFormNgxsPlugin] Submit';
}
}
// app/simple-form/user-form-ngxs-plugin/user-form-ngxs-plugin.selectors.ts
import { Selector } from '@ngxs/store';
import { User } from 'src/app/simple-form/user';
import {
UserForm,
UserFormNgxsPluginState,
UserFormNgxsPluginStateModel,
} from 'src/app/simple-form/user-form-ngxs-plugin/user-form-ngxs-plugin.state';
export class UserFormNgxsPluginSelectors {
@Selector([UserFormNgxsPluginState])
static form({ userForm }: UserFormNgxsPluginStateModel): UserForm {
return userForm;
}
@Selector([UserFormNgxsPluginSelectors.form])
static model({ model }: UserForm): User {
return model;
}
@Selector([UserFormNgxsPluginState])
static loading({ loading }: UserFormNgxsPluginStateModel): boolean {
return loading;
}
}