Persist Form

The PersistNgFormPlugin helps to keep your form synced with your store. One common scenario is preventing your user from losing data he filled in a form, after navigating away without saving it.

Let's take for example a Stories store.

stories.store.ts
export interface StoriesState extends EntityState<Story> {}
@StoreConfig({ name: 'stories' })
export class StoriesStore extends EntityStore<StoriesState> {
constructor() {
super(initialState);
}
}
story.model.ts
export type Story = {
title: string;
story: string;
draft: boolean;
category: string;
};
export function createStory() {
return {
title: '',
story: '',
draft: false,
category: 'JS'
} as Story;
}

Now, lets say we're building a form for adding new stories:

export class StoriesComponent implements OnInit {
form: FormGroup;
ngOnInit() {
this.form = this.builder.group({
title: this.builder.control(''),
story: this.builder.control(''),
draft: this.builder.control(false),
category: this.builder.control('js')
});
}
}

We can call the PersistNgFormPlugin, passing the StoriesQuery, the FormGroup, and a factory function which knows how to create a story:

export class StoriesComponent {
form: FormGroup;
persistForm: PersistNgFormPlugin;
constructor(private storiesQuery: StoriesQuery,
private storiesService: StoriesService,
private builder: FormBuilder) {}
ngOnInit() {
this.form = this.builder.group({
title: this.builder.control(''),
story: this.builder.control(''),
draft: this.builder.control(false),
category: this.builder.control('js')
});
this.persistForm = new PersistNgFormPlugin(this.storiesQuery, createStory)
.setForm(this.form);
}
}

With this setup, Akita does the following:

  1. Creates a new key in your store's root (called akitaForm) and initializes it with the result of the provided factory function.

  2. Listens to any change in the form and syncs it with the store.

  3. Updates the form value according to the store value when the user navigates back to the form.

After saving the story in the server you can reset the form:

submit() {
if(this.form.valid) {
this.storiesService.add(this.form.value)
.subscribe(() => this.persistForm.reset());
}
}
ngOnDestroy() {
this.persistForm && this.persistForm.destroy();
}

The reset() method will reset the form back to the initial value given by the factory function, or alternatively you can pass a different initial value.

Options

  • debounceTime: default to 100ms.

  • formKey: The key with which we save the form value (default: akitaForm). Useful for handling multiple forms.

this.persistForm = new PersistNgFormPlugin(this.storiesQuery, createStory, { debounceTime: 300 })

Sync specific key:

You also have the option to sync a specific store's key. For example:

store.ts
const initialState: State = {
config: {
time: '',
isAdmin: false
}
};
@StoreConfig({ name: 'my-store' })
export class MyStore extends Store<State> {
constructor() {
super(initialState);
}
}
component.ts
export class MyComponent implements OnInit {
formKeyBased: FormGroup;
persistFormKey: PersistNgFormPlugin;
constructor(private myStoreQuery: MyStoreQuery,
private builder: FormBuilder) {}
ngOnInit() {
this.formKeyBased = this.builder.group({
time: this.builder.control(''),
isAdmin: this.builder.control(null)
});
this.persistFormKey = new PersistNgFormPlugin(
this.myStoreQuery,
'config')
.setForm(this.formKeyBased);
}
}

Akita will take care to auto sync between the form and the store's key.

Sync root key

To sync a root key skip the second parameter, for example:

component.ts
this.formRootKey = this.builder.group({
someBoolean: this.builder.control(false)
});
this.persistFormRootKey = new PersistNgFormPlugin(this.storiesQuery)
.setForm(this.formRootKey
store.ts
const initialState = {
someBoolean: true
};
@StoreConfig({ name: 'stories' })
export class StoriesStore extends EntityStore<State> {
constructor() {
super(initialState);
}
}

In the above case, Akita will auth sync the someBoolean property.

Sync Array key

To support arrays you need to pass Angular's FormBuilder service to the plugin. Note that it only works for root level arrays. For example:

component.ts
this.formRootKey = this.builder.group({
skills: this.builder.array([]),
someBoolean: this.builder.control(false)
});
this.persistFormRootKey = new PersistNgFormPlugin(this.storiesQuery)
.setForm(this.formRootKey, this.builder);
store.ts
const initialState: State = {
someBoolean: true,
skills: ['JS'],
};
@StoreConfig({ name: 'stories' })
export class StoriesStore extends EntityStore<State, Story> {
constructor() {
super(initialState);
}
}

OR

component.ts
this.formKeyBased = this.builder.group({
time: this.builder.control(''),
tankOwners: this.builder.array([]),
isAdmin: this.builder.control(null)
});
this.persistFormKey = new PersistNgFormPlugin(this.storiesQuery, 'config')
.setForm(this.formKeyBased, this.builder);
store.ts
const initialState: State = {
config: {
time: '',
tankOwners: ['one', 'two '],
isAdmin: false
}
};
@StoreConfig({ name: 'stories' })
export class StoriesStore extends EntityStore<State> {
constructor() {
super(initialState);
}
}