Skip to main content

Legacy project setup

This page guides you through installing the foundation-store in your legacy project. This isn't recommended - all new applications created via Genesis Create or genx will install the store for you, and then you can just follow the example tutorial. However, if you're adding Genesis into an existing project (or you're using an old Genesis project from before the store was a default component) you can follow this guide.

Installation

  1. Add @genesislcap/foundation-store as a dependency in your package.json file. Whenever you change the dependencies of your project, ensure you run the $ npm run bootstrap (or npm install for React and Angular) command again.
{
...
"dependencies": {
...
"@genesislcap/foundation-store": "latest"
...
},
...
}

Next you need to add in the base store configuration. Once you've followed this page and the store is setup, you can then configure the store for your use.

Base store file

import {CustomEventMap} from '@genesislcap/foundation-events';
import {AbstractStore, Store, registerStore} from '@genesislcap/foundation-store';
import {observable} from '@genesislcap/web-core';

export interface Store extends StoreRoot {}

export type StoreEventDetailMap = StoreRootEventDetailMap & {};

declare global {
interface HTMLElementEventMap extends CustomEventMap<StoreEventDetailMap> {}
}

class DefaultStore extends AbstractStoreRoot<Store, StoreEventDetailMap> implements Store {
constructor() {
super();

/**
* Register the store root
*/
getApp().registerStoreRoot(this);
}
}

export const Store = registerStore(DefaultStore, 'Store');

// React and angular stores require a layer to work with the dependency injection. See following code sections.

Angular injection layer

To be able to access the store from your Angular components you need a class to wrap up the store dependency. You can add this to the bottom of the store.ts file.

import { DI } from '@genesislcap/web-core';

export function getStore(): Store {
return DI.getOrCreateDOMContainer().get(Store) as Store;
}

You can then access the store via the getStore function.

import { getStore } from './path/to/store';

getStore(); // access the store

React injection layer

To be able to access the store from your React components you need a class to wrap up the store dependency. You can add this to the bottom of the store.ts file.

import { DI } from '@genesislcap/web-core';

class StoreService {
private store: any;

constructor() {
this.store = DI.getOrCreateDOMContainer().get(Store) as Store;
}

getStore() {
return this.store;
}

onConnected(event?: CustomEvent) {
this.store.onConnected(event);
}
}

export const storeService = new StoreService();

You can then access the store via the storeService import and using the getter function.

import { storeService } from './path/to/store';

storeService.getStore(); // access the store

Genesis router wiring

If you are using a Genesis syntax project then you need to wire in the store into your application router. Components that interact with the store will individually require access, but there are steps required at the top level to create the store service. You need to inject the store into the class where you use the router in the template. This is very likely to be your MainApplication class.

main.ts

@customElement({
name,
template,
styles,
})
export class MainApplication extends EventEmitter<StoreEventDetailMap>(GenesisElement) {
@App app: App;
@Connect connect!: Connect;
@Container container!: Container;
@Store store: Store;
}

main.template.ts

export const MainTemplate: ViewTemplate<MainApplication> = html`
<foundation-router
:config=${(x) => x.config}
:store=${(x) => x.store}
></foundation-router>
`;

Store initialisation events

In addition to the events that you create to handle your business logic, you'll also need to emit events to setup and control the state of the store itself.

'store-connected'

In your main application class you need to fire a 'store-connected' event in-order to fully initialise the store.

The 'store-connected' event handler needs to be explicitly bound. When the root store handles 'store-connected', it auto binds all the store event listeners to the rootElement.

At this point you can start emitting strongly typed store events, and they will be handled by their corresponding store. See EventEmitter for more information.

'store-ready'

As you may be required to do some additional work between the initialisation of the store and the application use, there is an additional store-ready event to dispatch. It's not a hard requirement to emit this, but is considered best practice. If you've no work to do, you can just emit this right after 'store-connected'.

this.$emit('store-connected', this);
/**
* Do some other work if needed.
*/
this.$emit('store-ready', true);

'store-disconnected'

Emitting 'store-disconnected' will remove all the previously bound event listeners.

// ./main/main.ts

disconnectedCallback() {
super.disconnectedCallback();
this.$emit('store-disconnected');
}

Configuration example

The following snippets are examples of your main application class dispatching the required events

Example Main class in Genesis. Highlighted lines are directly related to initialising the store - other configuration may be different in your application. Other functionality that the main class may be required to perform is omitted from this example.

/**
* @fires store-connected - Fired when the store is connected.
* @fires store-ready - Fired when the store is ready.
*/
@customElement({
name,
template,
styles,
})
export class MainApplication extends EventEmitter<StoreEventDetailMap>(GenesisElement) {
@App app: App;
@Connect connect!: Connect;
@Container container!: Container;
@Store store: Store;

@inject(MainRouterConfig) config!: MainRouterConfig;

@observable provider!: any;
@observable ready: boolean = false;
@observable data: any = null;

async connectedCallback() {
this.registerDIDependencies();
super.connectedCallback();
this.addEventListeners();
this.readyStore();
DOM.queueUpdate(() => {
configureDesignSystem(this.provider, designTokens);
});
}

disconnectedCallback() {
super.disconnectedCallback();
this.removeEventListeners();
this.disconnectStore();
}

selectTemplate() {
return this.ready ? MainTemplate : LoadingTemplate;
}

protected addEventListeners() {
this.addEventListener('store-connected', this.store.onConnected);
}

protected removeEventListeners() {
this.removeEventListener('store-connected', this.store.onConnected);
}

protected readyStore() {
// @ts-ignore
this.$emit('store-connected', this);
this.$emit('store-ready', true);
}

protected disconnectStore() {
this.$emit('store-disconnected');
}
}