React query builder question: Is there a way to disable a field option when adding rules to a query

Viewed 683

I'm trying to create a query that takes the initial fields (kind, property, and property value). This part is straight forward. However for every additional query option/rule I want to disable the "kind" option and only leave the "property" and "property value" because the database only accepts one "kind" but multiple "properties" and "property values". Here is the library: https://github.com/react-querybuilder/react-querybuilder#usage

Here is a visual

React-Query-Builder

1 Answers

I think I've got something working like you want in this codesandbox. I had to use a lot of custom components, but they all fall back to the default components with relatively little logic on their own -- hiding themselves based on the level prop or just always hiding.

Relevant code is below:

import { useState } from "react";
import QueryBuilder, {
  ActionElement,
  ActionProps,
  ActionWithRulesProps,
  FieldSelectorProps,
  formatQuery,
  OperatorSelectorProps,
  RuleGroupType,
  RuleType,
  ValueEditor,
  ValueEditorProps,
  ValueSelector
} from "react-querybuilder";
import "react-querybuilder/dist/query-builder.css";

const fields = [
  { name: "first_name", label: "First Name" },
  { name: "email", label: "Email" },
  { name: "last_name", label: "Last Name" }
];

const defaultQuery: RuleGroupType = {
  id: "root",
  combinator: "and",
  rules: [
    {
      field: "last_name",
      operator: "=",
      value: ""
    },
    {
      id: "ruleGroup",
      combinator: "and",
      rules: []
    }
  ]
};

const addRuleAction = (props: ActionWithRulesProps) => {
  if (props.level === 0) {
    return null;
  }
  return <ActionElement {...props} />;
};

const addGroupAction = () => null;

const removeGroupAction = () => null;

const removeRuleAction = (props: ActionProps) => {
  if (props.level === 0) {
    return null;
  }
  return <ActionElement {...props} />;
};

const combinatorSelector = () => null;

const fieldSelector = (props: FieldSelectorProps) => {
  if (props.level > 0) {
    return null;
  }
  return <ValueSelector {...props} />;
};

const operatorSelector = (props: OperatorSelectorProps) => {
  if (props.level === 0) {
    return null;
  }
  return <ValueSelector {...props} />;
};

const valueEditor = (props: ValueEditorProps) => {
  if (props.level === 0) {
    return null;
  }
  return <ValueEditor {...props} />;
};

export default function App() {
  const [query, setQuery] = useState<RuleGroupType>(defaultQuery);

  const onQueryChange = (q: RuleGroupType) => {
    const f = (q.rules[0] as RuleType).field;
    const newQuery = {
      ...q,
      rules: [
        q.rules[0],
        {
          ...q.rules[1],
          rules: (q.rules[1] as RuleGroupType).rules.map((r) => ({
            ...r,
            field: f
          }))
        }
      ]
    };

    setQuery(newQuery);
  };

  return (
    <>
      <QueryBuilder
        fields={fields}
        onQueryChange={onQueryChange}
        query={query}
        controlElements={{
          addRuleAction,
          addGroupAction,
          removeGroupAction,
          combinatorSelector,
          operatorSelector,
          removeRuleAction,
          fieldSelector,
          valueEditor
        }}
        getDefaultField={() => "last_name"}
      />
      <h3>Output:</h3>
      <pre>{formatQuery(query)}</pre>
    </>
  );
}
Related