Sorting model with dependent entities

Hey there,

I want to sort in the afterModel hook model data i loaded based on (computed) asynchronous loaded data (see below).

I know that i can sort the data within the controller as special attribute with computed.sortBy but this seems to be quite a fuzz if i could do it in the route already.

Is there a way to actually sort already within the route if the part of the relevant data is loaded asynchronously?

Thanks in advance, Markus

export default Model.extend({
    name: attr('string'),
    vendor: belongsTo('vendor', { inverse: null }),
    displayName: computed('name', 'vendor', 'vendor.isFulfilled', function () {
        if (this.vendor.get('name')) {
            return ` (${this.vendor.get('name')}) ${this.name}`;
        }
        return this.name;
    }),
});
export default Model.extend({
    name: attr('string'),
});
export default AuthenticatedRoute.extend({
    model() {
        return RSVP.hash({
            products: this.store.findAll('product'),
        });
    },

    afterModel(model) {
        if (model.products) model.products = model.products.sortBy('displayName');
    },
});

There’s nothing to stop you from doing it in the afterModel hook but it’s not really conventional. You could sort the data in a number of different places:

  • controller: before rendering it or passing it into a component
  • component: pass unsorted model into a component and sort it there
  • route template: using a helper such as {{sort-by ...}} sort the data “where it’s rendered”
  • component template: same as route template, just in a component
  • afterModel hook: what you’re doing above

If you’re wanting to think of the route as something that gives the controller/template the data in a “ready to go state” and keep the sorting as close to the data fetching as possible there’s nothing really wrong with that. I personally like to explicitly declare things like this in a controller/component so it’s clear what is happening to the data as it traverses down the path from data => UI. I think about it like this:

model "raw data" (from server, fetched by route)
  => becomes
sortedProducts, defined in controller or component, declaratively and explicitly returning sorted version of raw data
  => becomes
UI (DOM markup) via the template

I like to treat my model data as “raw” and untouched as much as possible so it’s clear what to expect. If I see “model” in a template i’m going to assume that nothing has munged it in between the server and the template. If I see “sortedProducts” in a template i know where to find that and what it is.

Anyway, if you decide to go with afterModel you shouldn’t need the if statement, the model should always be resolved by the time it gets to the afterModel hook, and RSVP.hash should always give you a hash in that case, and findAll should give you an array, either empty or full, so the if check you have would be unnecessary.

1 Like