Advice on reducing API requests made by model fed drop downs

I have a number of drop down menus in my web app that are populated by ember models populated by my API. It’s a requirement that that they are populated by a model because their content can change.

Currently I feel like I am using a bad process as in the controller for each route I am calling the models needed to populate the drop downs each time. So a typical route might look something like:

export default Route.extend({
  queryParams: {
   param1:  {refreshModel: true}
  },

  model(params) {
    return RSVP.hash({
      menu1: this.get('store').query('menu1', params),
      menu2: this.get('store').query('menu2', params),
      menu3: this.get('store').query('menu3', params),
      menu4: this.get('store').query('menu4', params)
    })
  },

  setupController(controller, models) {
    this._super(controller, models);
    controller.set('menu1', models.menu1);
    controller.set('menu2', models.menu2);
    controller.set('menu3', models.menu3);
     controller.set('menu4', models.menu4);
  }
});

Some of these menus are unique to this route and some of them will be repeated on other routes. I have the following issues:

  1. I feel like I’m making too many requests to my API for something quite mundane
  2. It adds unnecessary complexity to each route
  3. I’m repeating code when the drop downs appear on multiple routes

I thought that perhaps this where an Ember Service might step in but I don’t think you can call the model hook in a service and the only parent route these all share is my ‘authenticate’ route which I (again) am not sure if I should be loading with multiple model requests.

On paper that seems like the way to go though - an Ember Service holding all of the drop down menus that I can distribute around the controllers that need them. This way each user would only be requesting these drop down menus once - when they log in.

Has anyone experienced anything similar?

For dropdowns with dynamic content I’ve adopted an ember-concurrency + ember-power-select approach. By doing this you can get lazily loaded dropdowns (on open) which are also (optionally) searchable.

In code this means something like:

Code

debounceDuration: 250,
defaultOptions: null,
mySelectedModel: null,

loadDropdownContent: task(function* (term, initialLoad = false) {
    if(!initialLoad){
      // don't debounce the initial load!
      yield timeout(this.get('debounceDuration'));
    }

    const query = {
        name: term
    };

    return this.get('store').query(this.get('some-model'), query);
}).restartable(),

actions: {
    loadDefaultOptions(){
        this.set('defaultOptions', this.get('searchModels').perform(null, true));
    }
}

Template

{{#power-select
  selected=mySelectedModel
  options=defaultOptions

  searchEnabled=true
  search=(perform loadDropdownContent)

  onopen=(action 'loadDefaultOptions')
  onclose=(cancel-all loadDropdownContent)
  onchange=(action (mut mySelectedModel))
as |model|
}}
  {{mode.name}}
{{/power-select}}

Please note I haven’t tested the above code. It’s a reduced snippet from an addon I’ll open source soon which makes this even easier to do.

So if I understand this correctly ( I haven’t done much with ember concurrency) our aim here is to call the model and populate the data when the user clicks on the drop down menu?

In that way we are only calling the API when it is required rather than pre loading data that might not be needed.

Exactly. If you don’t need or want the search functionality you could also go back to using findAll or peekAll in order to not always make an API request (and remove the searchy bits from the code of course).