Faux occlusion with Ember-Power-Select
Tags: ember
Published: 2021-01-08 00:05:51

## The problem

[Ember-Power-Select](https://ember-power-select.com/) is a very powerful [EmberJS](https://emberjs.com/) addon for creating drop-down selects.

Recently I had an interesting case that I needed to have all options loaded client-side and the number of options was not small (several thousands). At this point I have learned that the component does not have any form of [occlusion rendering](https://github.com/html-next/vertical-collection), so that all the thousand options would just be spit into the DOM. Which is not great.

After few iterations I found a decent solution:

## The solution

```hbs
// index.hbs

<PowerSelect
  @selected={{@selected}}
  @onChange={{@onChange}}
  @options={{this.initialSet}}
  @searchEnabled={{true}}
  @search={{this.search}} as |item|
>
  {{item.name}}
</PowerSelect >
```

```js
// index.js

import Component from "@glimmer/component";
import { action } from "@ember/object";

export default class MySelectComponent extends Component {
  // Maximum number of items to be displayed in the drop-down.
  MAX_ITEMS = 100;

  // All the items before any filter is applied.
  get initialSet() {
    // We use `null` filter, to limit the number
    // of items in initial list to `MAX_ITEMS` 
    return [...this.filter(null)];
  }

  // Our component receives all the possible options 
  // as `items` argument.
  get allItems() {
    return this.args.items;
  }

  // Search action invoked by the PowerSelect.
  @action
  search(needle) {
    return [...this.filter(needle)];
  }

  // We're going to filter options using a generator
  *filter(needle) {
    const { allItems, maxSize } = this;
    let count = 0;
    let i = 0;

    // Iterate over all items from initial set.
    // Stop when we found maximum number of items
    // or when we went through the whole initial set.
    while (count < this.MAX_ITEMS && i < allItems.length) {
      // If there is no search term, every item matches.
      // Otherwise test current item against given needle.
      if (!needle || this.check(allItems[i], needle)) {
        // If we found a match, yield it.
        yield allItems[i];
        count++;
      }
      i++;
    }
  }

  // Function that will test whether item matches needle
  check(item, needle) {
    // For simplicity let's say each item has property
    // `name` and we just check for a substring match.
    if (item.name.includes(needle)) {
      return true;
    }
  }
}
```

### Conclusion

With this approach I'm able to have all the options loaded client-side and display only relatively small portion in the Ember-Power-Select dropdown.

It is worth noting that this approach is suitable only for quite a narrow amount of items. At one point the payload from the server would be too big to transfer / filter on efficiently. Use your own judgement. I myself would look for different approach for bigger numbers than lower thousands of items.

-----

<span>Photo by <a href="https://unsplash.com/@wadeaustinellis?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Wade Austin Ellis</a> on <a href="https://unsplash.com/s/photos/filter?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>