Search code examples
ngxs

Is it possible to use data from state A inside a selector in state B?


I am trying out Ngxs as a state management system and came across a specific use case that I can't seem to figure out. In this use case I am using two normalized objects (I removed some unnecessary fields for readability).

export interface Section {
  id: number;
  sequence: number;
  name: string;
  subName: string;
  totalQuestions: number;
  completedQuestions: number;
  selected: boolean;
  questionFlows: QuestionFlow[];
}

export interface QuestionFlow {
  id: number;
  contractId: number;
  parentId: number;
  subSectionId: number;
  path: string;
  question: string;
  type: string;
  answer: string;
  completed: boolean;
  sequenceNumber: number;
  selected: boolean;
  questionFlows: QuestionFlow[];
}

These two objects reside in separate stores. A SectionStore and a QuestionFlowStore. The state models are like follows:

export class SectionsStateModel {
  sections: { [id: number]: Section };
  currentSection: Section;
}

export class QuestionFlowsStateModel {
  questionFlows: { [id: number]: QuestionFlow };
  currentQuestionFlow: QuestionFlow;
}

Now I would like to create a selector in the QuestionFlowsState that returns every questionFlow that belongs to the currentSection. Is it possible to get the currentSection inside a selector that resides inside the QuestionFlowState while the currentSection resides inside the SectionState? I have tried the code below (with a filled store) without success.

import { SectionsStateModel } from './sections.state';

@State<QuestionFlowsStateModel>({
  name: 'questionFlows',
  defaults: {
    questionFlows: {},
    currentQuestionFlow: null
  }
})
export class QuestionFlowsState {
  @Selector()
  static getQuestionFlowsArrayFromCurrentSection(
    state: QuestionFlowsStateModel,
    sectionState: SectionsStateModel
  ) {
    const questionFlowsFromCurrentSection: QuestionFlow[] = [];

    sectionState.currentSection.questionFlows.forEach(questionFlow => {
      questionFlowsFromCurrentSection.push(state.questionFlows[+questionFlow]);
    });

    return questionFlowsFromCurrentSection;
  }
}

If there is anything missing/unclear in the question, please let me know.

EDIT: After some back and forth with @Danny Blue we've come to the solution of adding a parent state that takes the states that contain the data needed for the selector as children (which can be set in the @State decorator). To access the data from these children stores you'll need to call the state.. and you are good to go. Below is the final code that solves my problem.

import { State, Selector } from '@ngxs/store';

import { SectionsState } from './sections.state';
import { QuestionFlowsState } from './question-flows.state';
import { QuestionFlow } from '../../contract-details.model';
import { SectionsStateModel } from './sections.state';
import { QuestionFlowsStateModel } from './question-flows.state';

@State({
  name: 'parent',
  children: [SectionsState, QuestionFlowsState]
})
export class ParentState {
  @Selector()
  static getParentFlowsArrayFromCurrentSection(
    state
  ) {
    const questionFlowsFromCurrentSection: QuestionFlow[] = [];

    state.sections.currentSection.questionFlows.forEach(questionFlow => {
      questionFlowsFromCurrentSection.push(
        state.questionFlows.questionFlows[+questionFlow]
      );
    });

    return questionFlowsFromCurrentSection;
  }
}

Solution

  • You could add the selector at a parent state class which would give it access to both child state.

    https://ngxs.gitbook.io/ngxs/advanced/sub-states