简体   繁体   中英

How to change the query imperatively in SearchKit?

We are trying to make a RefinementListFilter with checkboxes that has all items checked by default, and when all the items are unchecked by the user, all the items become checked automatically. We accomplished something similar but it has issues (sorry if the code feels ugly, it is WIP):

In the render code

<RefinementListFilter
  id="type"
  title="By Type"
  field="_type"
  size={10}
  operator="OR"
  itemComponent={RefinementOption}
  listComponent={RefinementList}
/>

Other code

/**
 * Returns the label associated to a data type
 * @param {string} val
 */
const valueToLabel = (val) => {
  const data = [
    { label: 'Documents', value: 'document' },
    { label: 'Links', value: 'link' },
    { label: 'Web Pages', value: 'article' },
    { label: 'Species Info', value: 'species' },
    { label: 'Habitat Types Info', value: 'habitat' },
    { label: 'Sites Info', value: 'site' },
    { label: 'Protected Area', value: 'protected_area' }, // hidden
  ];
  return data.filter((x) => x.value === val)[0].label;
};

/**
 * Displays a checkbox w/ a label that replaces the default data type string.
 * @param {object} props
 */
const RefinementOption = (props) => {
  return (
    <div
      role="checkbox"
      onKeyPress={() => {}}
      className={props.bemBlocks
        .option()
        .state({ selected: props.active })
        .mix(props.bemBlocks.container('item'))}
      tabIndex={0}
      aria-checked={props.active}
    >
      <label style={{ flexGrow: 1 }}>
        <input
          type="checkbox"
          onClick={(...args) => {
            return props.onClick(...args);
          }}
          checked={props.active}
          className="sk-item-list-option__checkbox"
        />
        <span className={props.bemBlocks.option('text')}>
          {valueToLabel(props.label)}
        </span>
      </label>
      {/* <div className={props.bemBlocks.option('count')}>{props.count}</div> */}
    </div>
  );
};

/**
 * Before showing the data types' filter sort its options well and the default
 * behavior is to all be checked (this is the same as when all are unchecked,
 * because the implicit operator is OR).
 * @param {object} props
 */
const RefinementList = (props) => {
  const data = [
    { order: 1, key: 'document' },
    { order: 2, key: 'link' },
    { order: 3, key: 'article' },
    { order: 4, key: 'species' },
    { order: 5, key: 'habitat' },
    { order: 6, key: 'site' },
    { order: 7, key: 'protected_area' }, // hidden
  ];

  let arr = [...props.items];
  arr = arr.filter((x) => x.key !== 'protected_area');
  arr = arr.sort((a, b) => {
    console.log('A', a.key, 'B', b.key);

    const ai = data.findIndex((x) => x.key === a.key);
    const bi = data.findIndex((x) => x.key === b.key);
    if (ai < 0 || isNaN(ai) || bi < 0 || isNaN(bi)) {
      return 0;
    }

    return data[ai].order - data[bi].order;
  });

  const allKeys = arr.map((x) => x.key);
  const activeCount = props.selectedItems.length;
  let selectedItems = props.selectedItems.map((x) => x.key);
  if (activeCount === 0) {
    selectedItems = allKeys;
  } else {
    selectedItems = props.selectedItems;
    if (selectedItems.length === allKeys.length) {
      selectedItems = [];

      // TODO: do this in the selected filters view w/o reloading the page
      const newLoc = window.location.href
        .replace(/type\[\d+\]=[a-zA-Z0-9_]+/g, '')
        .replace(/&&/g, '&')
        .replace(/\?&/g, '?')
        .replace(/[&?]$/, '');
      if (newLoc !== window.location.href) {
        window.location.href = newLoc;
      }
    }
  }

  return (
    <CheckboxItemList {...props} items={arr} selectedItems={selectedItems} />
  );
};

What we are trying to avoid is in the TODO comment in the code above.

The documentation of SearchKit is not complete in my opinion. I could not find how to do this.

What I tried and works is to change the query by changing the URL of the browser (setting location.href ) but the reload of the page is really ugly and does not match the behavior of the rest of the page.

The only thing left for me to try, if I do not get any answer, is to read the three demos' source code and then read the source code of SearchKit.

The Git repo with the relevant file is OSS.

There are no error messages.

Thank you.

Working solution, after taking a look at the SearchKit source code (yes, the first line is a global variable, I might change that into something else in future):

let globalSearchKitManagers = [];

...

for (const sk of globalSearchKitManagers) {
  if (typeof sk !== 'undefined') {
    const filters = sk.accessors.accessors.filter(
      (x) => x.field === '_type',
    );
    for (const x of filters) {
      sk.removeAccessor(x);
    }
    sk.performSearch(true, true);
  }
}

...

React.useEffect(() => {
  globalSearchKitManagers.push(searchkit);
  return () => {
    globalSearchKitManagers.splice(
      globalSearchKitManagers.indexOf(searchkit),
      1,
    );
  };
}, [searchkit]);

Update: We are now using SearchkitComponent instead of the global variable in the above code snippet.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM