Skip Navigation

com.sabre.redapp.example3.web.persistence Sample

Persistence sample shows how to handle shared state in Red App using Redux.

export class Main extends Module {

    localStore: LocalStore;

    init(): void {

        super.init();

        const xp = getService(ExtensionPointService);

        const sidepanelConfig = new RedAppSidePanelConfig([ // (1)
            new RedAppSidePanelButton('Open persist view', 'btn btn-secondary side-panel-button redapp-web-persistence', () => this.showView()),
        ]);

        xp.addConfig('redAppSidePanel', sidepanelConfig);

        registerService(PRCustomCommandHandler); // (2)
        registerService(LocalStoreHelperService);

        this.localStore = new LocalStore();

        getService(PRCustomCommandHandler).setLocalStore(this.localStore); // (3)

        const button: StaticButton = new StaticButton(this.localStore.store); // (4)
        xp.addConfig('novice-buttons', new WidgetXPConfig(button, -1000));
    }

    private showView(): void {

        let addRemarkModalOptions = {
            title: 'Modal with session persistence',
            actions: [{
                className: 'app.common.views.Button',
                caption: 'OK',
                actionName: 'cancel',
                type: 'secondary'
            }]
        };

        getService(LayerService).showInModal(
            new BasicView({ model: new BasicModel(this.localStore) }), addRemarkModalOptions, { display: 'areaView' }
        );
    }
}
  1. Starting with Main class we create button in Workflows sidepanel that allows us to show modal window.

  2. Then we register custom command handler service.

  3. Created store was send passed to command handler, so it can be used in the service.

  4. Same way we send it to StaticButton and futher to React component so it can be used there

export interface Data { // (1)
  messageAreaA: string;
  messageAreaB: string;
  messageAreaC: string;
  messageAreaD: string;
  messageAreaE: string;
  messageAreaF: string;
}

const defaultState: Data = { // (2)
  messageAreaA: 'Area A',
  messageAreaB: 'Area B',
  messageAreaC: 'Area C',
  messageAreaD: 'Area D',
  messageAreaE: 'Area E',
  messageAreaF: 'Area F'
}

function reducer(state: Data = defaultState, action) {
  const newState = {};
  const currentMessageName = getService(LocalStoreHelperService).getCurrentMessageName();
  newState[currentMessageName] = action.newVal;

  switch (action.type) {
    case 'CHANGE_MSG':
      return {
        ...state,
        ...newState
      };
    default:
      return state
  }
}

export class LocalStore {

  public store = createStore(reducer); // (3)

  getData(): Data {
    return this.store.getState();
  }

  getCurrentAreaMessage(): string { // (4)
    const currentMessageName = getService(LocalStoreHelperService).getCurrentMessageName();

    return this.store.getState()[currentMessageName];
  }

  setMessage(newVal: string) {
    const action = { type: "CHANGE_MSG", newVal: newVal };
    this.store.dispatch(action);
  }
}
  1. Since we use TypeScript we need interface to strong-type the data keept in the store

  2. Redux reducer - simple function that changes the state of redapp based on send actions

  3. Creating redux store

  4. Method that can be used to create action and dispatch it to store. It’s a good practice to create actions in one place.

export class BasicModel extends AbstractModel {

    text: string;
    localStore: LocalStore;

    constructor(localStore: LocalStore) {
        super();
        this.localStore = localStore;
        console.log("Store state durig initailization:", localStore.store.getState());
        this.text = localStore.getCurrentAreaMessage(); // (1)
    }

    getText(): string {
        return this.text;
    }

    setText(text: string): void {
        this.text = text;
        this.localStore.setMessage(text); // (2)
    }
}
  1. Model’s data will be based on one kept in redux store

  2. Changing the data is done using the setMessage function in LocalStore, the one that creates actions and sends it to the reducer using the dispatch function.

export class BasicView extends AbstractView<BasicModel> {

    constructor(options?: AbstractViewOptions) {
        super(options);
        super.addDomEvents({
            'keyup .change-message-ra': '_saveVal', // (1)
        });
    }

    _saveVal() {
        const x: string = super.$('.brand-input').val();
        super.getModel().setText(x);
    }

}
  1. After adding each new char in view’s text field, the text field in model is updated. So, after each key-stroke in text field there is a new action created and redux store state updates.

export class PRCustomCommandHandler extends CustomCommandHandler {

    localStore: LocalStore;

    static SERVICE_NAME =
        "com-sabre-redapp-example3-web-persistence-web-module-PRCustomCommandHandler";

    onCommandSend(rq: CustomCommandRq): Promise<CustomCommandRs> {

        return new Promise<CustomCommandRs>((resolve, reject) => {

            const areaService: IAreaService = getService(IAreaService);

            areaService.showBanner('Info', 'Message from redux: ' + this.localStore.getCurrentAreaMessage()); // (1)
            resolve();
        });

    }

    setLocalStore(store: LocalStore): void {
        this.localStore = store;
        console.log("Local store set to ", store);
    }
}
  1. Using state from the store is pretty straight-forward, use getState() or dedicated function on store to access needed state.

Working sample can be found in samples directory under com.sabre.redapp.example3.web.persistence name.