The problem
Ember-Power-Select is a very powerful EmberJS 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, 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
// index.hbs
<PowerSelect
@selected={{@selected}}
@onChange={{@onChange}}
@options={{this.initialSet}}
@searchEnabled={{true}}
@search={{this.search}} as |item|
>
{{item.name}}
</PowerSelect >
// 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.
Photo by Wade Austin Ellis on Unsplash
Top comments (2)
Nice! Have you tried using
vertical-collection
as theoptionsComponent
in Power Select?Yes. First I've seen cibernox's own integration attempt which seems unmaintained. So it felt like there is some problem with it. And later on when I tried to integrate it myself, there really was some hard caveat that I can't remember right now 🤔